Contents
What's Inside
01HTML ElementsBasics
07CSS Selectors & SpecificityAdvanced
02Layout & SpacingBasics
08JavaScript ConceptsAdvanced
03App Page StructureBasics
09The DOMAdvanced
04Visual & Style TermsBasics
10Data & StorageAdvanced
05Behaviour & EventsBasics
11Reading the CodebaseAdvanced
06App-Specific VocabularyBasics
12Writing Good RequestsReference
Every visible piece of the app is an HTML element. Each element has a type (its tag) and optional attributes that control its behaviour. Knowing the correct name lets you target changes precisely.
Form Controls
| Name | HTML tag | What it does | Example in app |
| Text input | <input type="text"> | Single line of freeform text | Guest name, hotel name |
| Number input | <input type="number"> | Numeric value with up/down arrows | Pax count |
| Date input | <input type="date"> | Date picker (browser-native) | Arrival / Departure date |
| Time input | <input type="time"> | HH:MM clock picker | Transfer time, Experience time |
| Checkbox | <input type="checkbox"> | On / off toggle, multiple can be ticked | Quote checkbox, Roundtrip |
| Radio button | <input type="radio"> | Pick exactly one from a group | Arrival / Departure selector |
| Select / dropdown | <select> | Click to reveal a list of options | Airport list |
| Combobox | <input> + dropdown JS | Searchable dropdown with freetext fallback | Airport search field |
| Textarea | <textarea> | Multi-line freeform text | Special notes, Itinerary notes |
| Button | <button> | Triggers an action when clicked | Save, Add block, Generate |
| Label | <label> | Descriptive text above or beside a field | "Arrival Date", "Guest Name" |
Structural Elements
| Name | Tag | Purpose |
| Div | <div> | Generic container — the most common element, used for grouping and layout |
| Span | <span> | Inline container — wraps text or icons within a line |
| Paragraph | <p> | A block of text with automatic top/bottom spacing |
| Heading | <h1>–<h6> | Section titles — h1 is largest, h6 smallest |
| SVG | <svg> | Vector graphic — used for icons and chevrons |
| Image | <img> | Raster image (PNG, JPG) embedded in the page |
| Anchor / Link | <a> | Clickable link to another page or section |
💡
Attribute vs. Property. Attributes are written in the HTML tag (type="text", placeholder="Enter name", id="guest-name"). Properties are the live JavaScript values you read or set in code. Most attributes have a matching property, but they are not the same thing — changing a property does not always update the attribute in the HTML source.
02
Layout describes how elements are positioned relative to each other. CSS provides two main modern layout systems — Flexbox and Grid — plus the classic Box Model that controls spacing around every element.
The Box Model
┌─────────────────────────────────────────┐ MARGIN (space outside)
│ ┌───────────────────────────────────┐ │
│ │ BORDER (the outline) │ │
│ │ ┌─────────────────────────────┐ │ │
│ │ │ PADDING (space inside) │ │ │
│ │ │ ┌───────────────────────┐ │ │ │
│ │ │ │ CONTENT │ │ │ │
│ │ │ └───────────────────────┘ │ │ │
│ │ └─────────────────────────────┘ │ │
│ └───────────────────────────────────┘ │
└─────────────────────────────────────────┘
| Term | Where | Affects |
| Padding | Inside the border | Space between content and border — makes an element feel roomier without changing its outer footprint |
| Margin | Outside the border | Space between this element and neighbouring elements |
| Border | The outline itself | Can be solid, dashed, or a single bottom line (underline style) |
| Width / Height | The content area | The actual size of the element before padding and border are added (unless box-sizing: border-box) |
| box-sizing: border-box | Global rule | Makes width/height include padding and border — the modern standard used throughout the app |
Flexbox — One Dimension
┌──────── flex container (display: flex) ────────┐
│ ┌──────┐ ┌──────┐ ┌──────┐ │
│ │ item │ │ item │ │ item │ ← flex-direction: row │
│ └──────┘ └──────┘ └──────┘ │
└────────────────────────────────────────────────┘
gap: 12px ↑
| Property | What it does | Common values |
display: flex | Turns a container into flex layout | — |
flex-direction | Which direction items flow | row (horizontal), column (vertical) |
align-items | Align items on the cross axis | center, flex-start, stretch |
justify-content | Space items along the main axis | space-between, center, flex-end |
gap | Space between flex children | 8px, 12px, etc. |
flex: 1 | Item grows to fill available space | flex: 1, flex-shrink: 0 |
flex-wrap | Allow items to wrap to a new row | wrap, nowrap |
CSS Grid — Two Dimensions
┌──────── grid container (display: grid) ────────┐
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ col 1 │ │ col 2 │ │ col 3 │ row 1 │
│ └──────────┘ └──────────┘ └──────────┘ │
│ ┌──────────┐ ┌─────────────────────────┐ │
│ │ col 1 │ │ col 2–3 (span 2) │ row 2 │
│ └──────────┘ └─────────────────────────┘ │
└────────────────────────────────────────────────┘
| Property | What it does | Example |
display: grid | Turns container into grid layout | — |
grid-template-columns | Defines column widths | 1fr 1fr (equal halves), repeat(3,1fr) (3 equal cols) |
gap | Space between all cells | 7px, 12px 8px (row gap, col gap) |
grid-column: span 2 | Makes an item stretch across 2 columns | Used on the Experience Title field |
fr unit | "Fraction" — divides available space proportionally | 2fr 1fr = left column is twice as wide |
💡
Flex vs Grid. Use Flex when you have items in a single row or single column and want them to be responsive. Use Grid when you need precise alignment in both rows and columns simultaneously — like the 3-column chip grid in Activity Categories.
03
The Concierge Desk has a consistent anatomy. Every part of the interface has a name. Use these names when describing where a change should happen.
Anatomy Diagram
┌──────────────────────────────────────────────────────────────┐
│ APP HEADER (always dark) │
│ La Réserve Eden au Lac Zurich — Concierge Desk │
├──────────────────────────────────────────────────────────────┤
│ TAB BAR [Transfers] [Restaurants] [Massages] [Itinerary] │
├─────────────────────────────────┬────────────────────────────┤
│ FORM PANEL │ PREVIEW PANEL │
│ │ │
│ ┌─────────────────────────┐ │ ┌──────────────────────┐ │
│ │ SECTION / BLOCK │ │ │ DOCUMENT OUTPUT │ │
│ │ ┌──────┐ ┌──────┐ │ │ │ (live preview) │ │
│ │ │ FIELD│ │ FIELD│ │ │ │ │ │
│ │ └──────┘ └──────┘ │ │ └──────────────────────┘ │
│ └─────────────────────────┘ │ │
│ │ │
├─────────────────────────────────┴────────────────────────────┤
│ ACTION BAR [Clear] [Copy] [Print] │
└──────────────────────────────────────────────────────────────┘
Named Areas
| Name | What it is |
| App header | The always-dark top bar with the hotel name and session info |
| Tab bar | The row of category buttons (Transfers, Restaurants, etc.) — switches the active panel |
| Tab / tab panel | One category and its entire content area |
| Form panel | The left side where you fill in data |
| Preview panel | The right side that shows the generated document in real time |
| Section | A grouped block within a tab, usually with a heading (e.g. "ARRIVAL") |
| Block | A self-contained card — e.g. one Transfer block, one Activity block |
| Block header | The top bar of a block with its title and action buttons (delete, duplicate, WhatsApp, etc.) |
| Field | One label + one input together — the smallest data-entry unit |
| Field row | Multiple fields side by side in a horizontal layout |
| Action bar | The strip at the bottom with Copy, Print, Clear, etc. |
| Settings panel | The side panel opened from the gear icon — contains all configuration |
| Settings tab | Tabs inside Settings (General, Itinerary, Transfers, etc.) |
| Activity manager | The modal (full-screen overlay) for editing the activity library |
| Modal | Any popup that appears over the main content and requires closing |
04
CSS controls every visual property. These are the words used to describe and change them precisely.
Typography
| Term | CSS property | Meaning |
| Font size | font-size | How large the text is (px, em, rem, pt) |
| Font weight | font-weight | Thickness: 300 = light, 400 = regular, 500 = medium, 700 = bold |
| Font family | font-family | The typeface — app uses Jost (UI), Cormorant Garamond (headers), Barlow Condensed (docs) |
| Line height | line-height | Spacing between lines of text — 1.5 is comfortable, 1.0 is very tight |
| Letter spacing | letter-spacing | Space between individual characters — positive values spread text out |
| Text transform | text-transform | uppercase / lowercase / capitalize |
| Italic | font-style: italic | Slanted text |
Colour & Appearance
| Term | CSS property | In this app |
| Gold / accent | --gold | Primary brand colour #8F6D55 — used on borders, icons, highlights |
| Charcoal | --charcoal | Near-black #0F0F0D used in the header and dark panels |
| Muted | --muted | Grey #8A8886 for secondary text and placeholder labels |
| Background (bg) | --bg | Main page background #E9E8E6 — warm off-white in light mode |
| Panel | --panel-bg | Card / section background — slightly lighter than bg |
| Border | --border | Colour of dividing lines #C8C4C0 |
| Opacity / alpha | rgba() | Fourth value controls transparency: rgba(0,0,0,0.1) = 10% black |
| CSS variable | --name | A named colour or value defined once and reused everywhere — changes one place, updates everywhere |
Shape & Decoration
| Term | What it means |
| Border radius | Rounds the corners — 0 = sharp right angle, 8px = gently rounded, 50% = circle |
| Box shadow | Drop shadow — values are: x-offset y-offset blur spread colour |
| Underline style | Input field with only a bottom border — no box; the app uses this for all form fields |
| Chip / pill | Small rounded tag element — the Activity Categories chips in Settings |
| Divider / separator | A horizontal line between sections — in the app it can be a gold hairline |
| Chevron | The › arrow icon on feature card buttons |
| Backdrop / overlay | Semi-transparent layer placed over the page when a modal is open |
| Transition | Smooth animation when a value changes — e.g. colour shift on hover |
| Blur / backdrop-filter | Frosted-glass effect behind a floating panel |
05
JavaScript makes the app interactive. Understanding event terminology helps you describe what should happen when — and why something is not happening when it should.
Event Types
| Event | When it fires | Common use |
oninput | Every keystroke / value change in real time | Triggering the live preview — fires as you type |
onchange | When a field loses focus after its value changed | Checkboxes, dropdowns — fires when you pick and move on |
onclick | Mouse click or tap | Buttons, chips, tab selectors |
onfocus | When an element receives keyboard / cursor focus | Showing helper text, opening combobox dropdown |
onblur | When an element loses focus | Closing dropdowns, saving a field silently |
onkeydown | A key is pressed down | Keyboard shortcuts, Enter to submit |
Timing & Async Concepts
| Term | What it means | In this app |
| Debounce | Delay a function until the user stops typing for N milliseconds — prevents rapid re-renders | Preview updates wait 100–280ms after last keystroke |
| setTimeout | Run a function once after a delay | Used inside the debounce to schedule each preview |
| clearTimeout | Cancel a scheduled setTimeout before it fires | Called each time a new keystroke resets the debounce timer |
| requestAnimationFrame | Run code just before the browser paints the next frame — smoother than setTimeout(fn, 0) | Wraps the preview DOM update for flicker-free rendering |
| Promise / async | Code that runs in the background and completes later — used for network requests | Cloud save to Supabase, loading stored settings |
| Callback | A function passed to another function to be called when something finishes | Event handlers are callbacks — passed to addEventListener |
⚠️
Timer conflicts. If two tabs share one timer variable, switching tabs mid-typing can cancel the pending update on the other tab. The fix is giving each tab its own dedicated timer variable — which was implemented in the per-tab debounce refactor.
06
These names refer to specific components, areas, or patterns used in this tool. Use them when describing targeted changes.
Named Components
| Name to use | What it refers to |
| Arrival block / Departure block | The two collapsible sections inside a Transfer tab entry |
| Transfer block | One full Transfer entry (contains Arrival + Departure + Experience) |
| Itinerary block | One day card in the Itinerary tab — contains activities and schedule |
| Activity block | One activity entry within an Itinerary block |
| Experience block | The optional curated experience section at the bottom of a Transfer/block |
| Guest confirmation | The luxury experience letter/email output for a guest |
| Airport list | Saved airports in Settings → General — feeds the combobox dropdown |
| Category chips | The 3-column chip grid in Settings → Itinerary for activity categories |
| Feature card button | Large tappable row with icon + label + sub-label + chevron — e.g. "Manage Activities" |
| Tab bar (Settings) | The row of selector buttons inside the Settings panel (not the main tab bar) |
| Color scheme | Light / Dark / Auto toggle in Settings → General |
| Cloud save | Automatic sync to Supabase backend — triggered on Save button press |
| schedulePreview | The JavaScript function that debounces and fires the live preview update |
| generateTransfer / generateItinerary | Functions that rebuild the preview HTML for their respective tab |
07
A CSS selector is the pattern that identifies which HTML elements a rule applies to. Specificity determines which rule wins when two rules target the same element.
Selector Types
| Selector | Syntax | Matches |
| Element | input | Every <input> on the page — broad |
| Class | .field | Every element with class="field" — the most common selector |
| ID | #guest-name | The one element with id="guest-name" — must be unique on page |
| Descendant | .field input | Any <input> that is inside a .field element — can be deeply nested |
| Direct child | .field > input | Only <input> that is an immediate child of .field |
| Attribute | input[type="time"] | Inputs where the type attribute equals time |
| Pseudo-class | button:hover | Button in its hovered state |
| Pseudo-element | .cover::before | A virtual element inserted before the content — used for decorative lines |
| Negation | input:not([type="checkbox"]) | All inputs except checkboxes |
| Media query | @media (max-width: 560px) | Rules that only apply at certain screen widths — used for mobile layouts |
Specificity — Why Your Rule Might Lose
Specificity is calculated as three numbers: [IDs] [classes] [elements]
#guest-name → 1, 0, 0 (highest)
.field input → 0, 1, 1
.field input[type] → 0, 2, 1
input → 0, 0, 1
input !important → overrides everything
- A more specific selector always wins over a less specific one, regardless of source order.
!important overrides all specificity — use it sparingly, only when a browser's built-in style is impossible to override otherwise.
- Rules in a
style="" attribute on an element beat any external CSS class.
- If two rules have equal specificity, the one that appears later in the file wins.
CSS Custom Properties (Variables)
How CSS variables work
:root {
--gold: #8F6D55;
--border: #C8C4C0;
}
.section-block {
border: 1px solid var(--border);
}
html.dark-mode {
--border: rgba(255,255,255,0.12);
}
💡
Because every colour in the app uses a CSS variable, switching from light to dark mode is achieved by redefining ~10 variables — all 28,000 lines instantly update. This is why dark mode works without duplicating any styling.
08
JavaScript is the language that makes the app dynamic — reading inputs, building document HTML, saving data, and responding to user actions.
Core Language Terms
| Term | What it is | Example |
| Variable | A named container for a value | let name = 'Mr Smith' |
| Constant | A variable whose reference cannot be reassigned | const gold = '#8F6D55' |
| Function | A reusable block of code called by name | function generateTransfer() {…} |
| Arrow function | Shorter function syntax — common in modern code | () => generateTransfer(true) |
| Object | A collection of key–value pairs | { name: 'Smith', pax: 2 } |
| Array | An ordered list of values | ['ZRH', 'GVA', 'MXP'] |
| Boolean | A true/false value | true / false |
| Null / undefined | Absent or unset value — often the source of errors | null, undefined |
| Template literal | String with embedded expressions — uses backticks | `Hello ${name}` |
| Conditional (if/else) | Runs different code depending on a condition | if (pax > 1) {…} |
| Loop (forEach, for) | Repeats code for each item in a list | airports.forEach(a => …) |
| Return | Sends a value back from a function | return html; |
The APP Object — Global State
The app stores all shared runtime state in a single object called APP. Any function can read from or write to it. If something is misbehaving, this object is the first place to inspect.
Key APP properties
APP.mode
APP.previewTimerTransfers
APP.previewTimerItin
APP.blocks
APP.settings
Common Patterns in the Codebase
Reading a field value
var nameEl = document.getElementById('guest-name');
var name = nameEl ? nameEl.value.trim() : '';
Building HTML as a string (template literal)
var html = `
<div class="transfer-header">
<span>${name}</span>
<span>${date}</span>
</div>
`;
The debounce pattern — schedulePreview
if (APP.previewTimerTransfers) clearTimeout(APP.previewTimerTransfers);
APP.previewTimerTransfers = setTimeout(() => {
requestAnimationFrame(() => generateTransfer(true));
}, 100);
09
The DOM (Document Object Model) is the live tree of all elements on the page. JavaScript reads, modifies, and rebuilds parts of this tree to update what you see without reloading the page.
Core DOM Operations
| Operation | JavaScript | What it does |
| Find by ID | document.getElementById('x') | Returns the one element with that ID — most common way to get a specific field |
| Find by class | document.querySelector('.class') | Returns the first match — like CSS selector syntax |
| Find all | document.querySelectorAll('.class') | Returns all matches as a list |
| Read value | element.value | The current text / selection in a form field |
| Set HTML | element.innerHTML = '…' | Replace the element's entire contents with new HTML |
| Set text only | element.textContent = '…' | Replace contents with plain text — safer, no HTML injection risk |
| Add class | element.classList.add('active') | Adds a CSS class to an element |
| Remove class | element.classList.remove('active') | Removes a CSS class |
| Toggle class | element.classList.toggle('open') | Adds if missing, removes if present |
| Show / hide | element.style.display = 'block' | Controls visibility via inline style — 'none' hides, 'block' shows |
| Create element | document.createElement('div') | Creates a new element not yet in the page |
| Append child | parent.appendChild(child) | Inserts a created element into the DOM |
| Replace children | parent.replaceChildren(frag) | Atomically swaps all children — used in preview to prevent flash |
DocumentFragment — Atomic DOM Updates
A DocumentFragment is an in-memory container. You build the entire new content inside it without touching the visible page, then swap it in one operation — eliminating the visible blank-flash that happened before this was implemented.
Fragment pattern used in showDocMultiPage
const frag = document.createDocumentFragment();
pagesHtmlArray.forEach((html, i) => {
const div = document.createElement('div');
div.innerHTML = html;
frag.appendChild(div);
});
container.replaceChildren(frag);
10
The app stores data in two places — locally in the browser and remotely in the cloud. Understanding which is which helps you describe storage-related issues clearly.
Storage Layers
| Layer | Technology | Persists if… | Used for |
| Local storage | localStorage | Browser cache not cleared | Theme preference, brightness level, tab colors |
| Cloud storage | Supabase | Always — stored on server | Airport list, activity categories, saved settings |
| In-memory (runtime) | JS variables, APP object | Only while the page is open | Current form state, unsaved block data, timers |
Data Formats
| Format | What it looks like | Used for |
| JSON | {"name":"ZRH","label":"Zurich Airport"} | Sending / storing structured data — airports, settings, activity lists |
| Array of objects | [{label:"ZRH"}, {label:"GVA"}] | Airport list, activity categories — a list where each item is a set of named values |
| String | "2026-06-05" | Dates, names, any text value from a field |
| Boolean flag | true / false | Toggle states — round-trip, quote visible, arrival vs departure |
💡
Deduplication. When data is saved from two sources (default list + user additions) duplicates can accumulate. The airport list deduplication works by normalising each label to lowercase and tracking which ones have already been seen — the first occurrence is kept, all subsequent duplicates are filtered out.
11
The entire app lives in one file — public/concierge.html — approximately 28,000 lines long. Knowing how to navigate it makes targeted changes possible without breaking unrelated parts.
File Structure (top to bottom)
①
Inline JavaScript — theme initialisationRuns before the page renders — reads localStorage, sets dark/light class on <html>
②
CSS — inside <style> tagsAll styling: design tokens, layout, component styles, dark-mode overrides, print rules
③
HTML — the <body>All markup: header, tab bar, form panels, preview panels, settings side-panel, modals
④
JavaScript — inside <script> at bottomAll logic: event handlers, preview generators, settings functions, cloud save, utility helpers
How to Locate Something
- Find a field: Search for its
id attribute — e.g. id="itin-exp-time"
- Find a CSS rule: Search for the class name — e.g.
.cat-chip-grid
- Find a function: Search for
function functionName — e.g. function schedulePreview
- Find where a button's action is defined: Read the
onclick="..." value, then search for that function name
- Find where a setting is saved: Search for the
cloudSave call near the save button's function
- Find dark-mode overrides: Search for
.dark-mode near the class you want to change
Common Bugs & Their Signatures
| Symptom | Likely cause | What to say |
| Page locked / nothing works | A JavaScript syntax error prevents all code from running | "The page is locked — something broke the JS" |
| Preview doesn't update | Wrong event, shared timer conflict, or generation counter mismatch | "Typing [field name] does not trigger the preview" |
| Preview flickers / goes blank | DOM rebuild happening in multiple operations instead of one atomic swap | "The preview briefly goes blank when I type" |
| Style not applying | Lower specificity than an existing rule — or a more specific rule exists later | "The border on X is still showing even after the change" |
| Setting not saving | Missing cloudSave call, or wrong key name | "The airport list resets when I reload" |
| Duplicates appearing | Data merged from default list + saved list without deduplication | "There are duplicate entries in the X list" |
| Layout misaligned | Inconsistent height, padding, or vertical-align between sibling elements | "The X underline is not at the same level as Y" |
12
The quality of the result is directly proportional to the precision of the request. These examples show how to translate what you see into language that maps to exact code changes.
The Formula
[Where] + [What element] + [Which property] + [Desired state]
"In the Arrival block, the Time input has a box border
instead of the underline-only style used on the Date field"
"In Settings → Itinerary, the category chip inputs have
a visible inner border that makes them look like a box-in-a-box"
Before & After Examples
Vague
The time thing looks wrong
Precise
The Time input in the Experience block has a box border instead of underline-only styling, and its height is taller than the other fields in the same row
Vague
Make the settings look nicer
Precise
Redesign the Itinerary settings tab to use 2–3 column rows for the category list, and add a prominent feature card button for the Activity Manager at the top
Vague
The preview is glitching
Precise
The preview panel briefly goes blank each time I type in the Name field — it flashes white for a moment before the content re-appears
Vague
There are duplicates in the list
Precise
In Settings → General, the Airport / Destination List shows the same airport label appearing two or three times — please remove duplicates when loading and when saving the list
Useful Describing Words
underline-only
box border
misaligned
too tall / too short
same height as
vertically centred
hairline separator
doesn't trigger
flickers / flashes
resets on reload
duplicated
overlapping
not rendering
missing on mobile
clipped / cut off
font too light
too much padding
squashed together
doesn't wrap
overflows the container
13
Copy any of these prompts, fill in the bracketed parts, and send directly. Each one is structured using the vocabulary from this handbook so the request maps precisely to code.
Visual / Styling Fixes
Alignment problem
In the [Arrival / Departure / Experience] block, the [Time / Date / Airport] field
has its underline at a different vertical level than the [other field name] field
beside it. Please align all three underlines to the same baseline.
Border / styling mismatch
The [element name] in the [section / block name] is showing a full box border instead
of the underline-only style used on all other fields. Please remove the top, left, and
right borders and keep only the bottom border, matching the other inputs.
Spacing adjustment
The [section / block / card] in the [tab name] tab has too much [padding / margin]
[above / below / between items]. Please reduce it to match the spacing used in the
[reference section] section.
Font or colour change
The [label / heading / sub-label] on the [element / section] is [too bold / too large /
the wrong colour]. Please change the font-weight to [300 / 400 / 500] and the colour
to [muted / gold / text] to match the style used in [reference area].
Layout change — columns
The [list / group of items] in [Settings tab / section name] is displayed as a single
column. Please convert it to a [2 / 3]-column grid layout, similar to the Activity
Categories chip grid in Settings → Itinerary.
Dark mode inconsistency
The [element name] looks correct in light mode but in dark mode the [background /
border / text colour] does not update. Please add a dark-mode CSS override so it
uses [the appropriate CSS variable] instead of the hardcoded colour.
Behaviour / Preview Fixes
Preview not triggering
When I type in the [field name] field on the [tab name] tab, the preview panel
does not update. It only updates after I [describe what does trigger it, e.g. change
the date or airport]. Please ensure that oninput on that field calls schedulePreview.
Preview flickering / blank flash
The preview panel in the [tab name] tab briefly goes blank for a fraction of a second
every time it updates. Please ensure the DOM rebuild uses the DocumentFragment /
replaceChildren pattern to make the swap atomic and flicker-free.
Event not firing
The [button / checkbox / dropdown] for [feature name] in the [section / block] does
not respond when I [click / change / select]. Please check whether the onclick /
onchange / oninput attribute is wired to the correct function.
Action too slow / delayed
The preview on the [tab name] tab feels sluggish — there is a noticeable delay
after I stop typing before it updates. Please reduce the debounce timeout in
schedulePreview for that tab from [current ms] to [target ms].
Data & Settings Fixes
Duplicate entries
The [airport list / category list / activity list] in Settings → [tab name] shows
duplicate entries — the same [label / name] appears [2 / 3] times. Please add
deduplication (case-insensitive, by label) both when loading and when saving the list.
Setting not persisting
The [setting name] in Settings → [tab name] resets to its default value every time
I reload the page. Please ensure the value is included in the cloudSave call and
loaded correctly when the settings panel initialises.
Default value wrong
The [field name] field in the [tab / block] defaults to [current value] on a fresh
load. Please change the default to [desired value] — either in the HTML placeholder
attribute or in the initialisation function that populates the form.
Feature & Design Requests
Add a new field
Please add a new [text / date / select] field called "[label text]" to the
[section name] in the [tab name] tab. It should sit [before / after / beside]
the [adjacent field name] field, and its value should be included in the preview
output [where in the preview document].
Add a new settings option
Please add a new option to Settings → [tab name] that lets me [describe the
setting]. It should appear as a [feature card button / checkbox / text input]
and be saved via cloudSave under the key "[key name]".
Redesign a section
Please redesign the [section name] in [Settings tab / tab name]. Instead of the
current [single column / flat list] layout, use [2-column grid / feature card
buttons / chip grid] to make it feel more professional. Keep all existing
functionality — only change the visual presentation.
Move an element
Please move the [element / section name] from its current position [describe
where it is now] to [describe the target position — e.g. directly below the
Color Scheme section, inside the same section block]. Remove any leftover empty
space where it used to be.
Remove something
Please remove the [label text / element / button] from the [section / block /
field] in the [tab name] tab. Keep the [adjacent element] in place — only
remove [the specific thing] and close up the resulting gap.
💡
Screenshot + prompt. For visual issues, the most effective approach is: take a screenshot of exactly what looks wrong, paste it into the chat, and attach one of the above prompt templates describing the problem. The combination of visual evidence and precise language produces the most accurate fix on the first attempt.