    :root {
      --bg: #F5F3F8;
      --surface: #FFFFFF;
      --surface-hover: rgba(35,0,76,0.03);
      --border: rgba(35,0,76,0.08);
      --border-strong: rgba(35,0,76,0.16);
      --text: #23004C;
      --text-secondary: rgba(35,0,76,0.62);
      --text-tertiary: rgba(35,0,76,0.45);
      --accent: #7A00E6;
      --accent-light: rgba(122,0,230,0.7);
      --accent-bg: rgba(122,0,230,0.06);
      --danger: #DA3A16;
      --danger-bg: rgba(218,58,22,0.06);
      /* Semantic colors fixed across both themes (like --danger already
         is) — vivid enough to read on either surface, so they don't need
         a light/dark variant. Centralized here (were previously repeated
         as raw hex at each usage site) so a future tweak only needs to
         happen once. */
      --success: #268500;
      /* "Running late" - amber, between the accent and --danger, for the
         live day's drift readouts. Same fixed-across-themes rule. */
      --warning: #B45309;
      /* Agenda block hues by type (the glyph decides the type). Sights stay
         the accent; the rest are picked to sit beside it, not fight it. */
      --type-sights: var(--accent);
      --type-food: #B45309;
      --type-transit: #5B5B6B;
      --type-outdoors: #268500;
      --type-nightlife: #8B1E6B;
      /* Priority scale — deliberately its own trio, not aliased to
         --danger/--success, so a future change to either of those (used
         elsewhere for errors/positive-balance/etc.) can't silently recolor
         priority flags too, or vice versa. Inverted + re-sourced from
         "Color Schema 1" (the fixed 8-color palette already reused
         elsewhere in this file for tags): high was --danger's red before
         (coupled to it) and low was an ad-hoc green outside that palette —
         swapped, and medium given its own schema color too so the whole
         trio is schema-sourced, not just the two ends. */
      --priority-high: #268500;
      --priority-medium: #007FAD;
      --priority-low: #DA3A16;
      /* Day-card header, light mode only (see the matching dark/@media
         block below, which keeps these aliased to --text/--text-tertiary
         so dark mode's look is untouched). Deliberate Color Schema 1
         picks rather than the inherited body-text color: vivid purple for
         the title makes it read as a heading rather than plain text; the
         date reuses the schema's darkest tone (not --text-tertiary's
         faint gray) plus a heavier weight below, so it's legible at a
         glance instead of an afterthought under the title. */
      --day-title-color: #7A00E6;
      --day-date-color: #23004C;
      --day-date-weight: 700;
      /* One UI corner scale. The previous 8/12/16/24 sat squarely in the
         8–12px band that reads as generic Material/web default — the single
         most common way an attempt at this look lands off-brand. One UI's
         signature is noticeably softer: small controls ~16, cards and list
         rows ~20–24, large surfaces and sheet tops ~28–32. */
      --radius-sm: 14px;
      --radius-md: 18px;
      --radius-lg: 22px;
      --radius-xl: 30px;
      --shadow: 0 1px 2px rgba(0,0,0,0.04), 0 4px 8px rgba(0,0,0,0.02);
      --shadow-lg: 0 4px 16px rgba(0,0,0,0.06);
      --transition: 200ms cubic-bezier(0.4,0,0.2,1);
      /* Settle easing. This token previously held a back-out curve with a
         real overshoot (…,1.56,…) for "modern bold" presence. One UI's motion
         language is the opposite: content decelerates and settles rather than
         springing past its mark, so the overshoot is gone and every one of the
         14 call sites inherits the calmer curve at once — a spring on a sheet
         or a toast is the tell that reads as iOS rather than One UI.
         --ease-settle is the bare curve for `animation` shorthands (which need
         duration and easing separately); --transition-settle bundles a
         duration for `transition` shorthand use. Slightly longer than the flat
         --transition, because One UI motion is unhurried, not sluggish. Not
         theme-dependent, so these live only in this block. */
      --ease-settle: cubic-bezier(0.22,0.61,0.36,1);
      --transition-settle: 340ms var(--ease-settle);
      --dock-bg: rgba(255,255,255,0.85);
      --track-bg: rgba(35,0,76,0.05);
      --badge-bg: rgba(35,0,76,0.04);
      --scrollbar-thumb: rgba(35,0,76,0.15);
    }
    /* Auto (default): follow the OS/browser setting. Overridden below by
       an explicit [data-theme] on <html> when the user picks light/dark
       manually instead of "Auto" — see app.cycleTheme() in app-core.js. */
    @media (prefers-color-scheme: dark) {
      :root {
        --bg: #000000;
        --surface: #161618;
        --surface-hover: #1E1E21;
        --border: rgba(255,255,255,0.08);
        --border-strong: rgba(255,255,255,0.16);
        --text: #F5F3F8;
        --text-secondary: rgba(245,243,248,0.65);
        --text-tertiary: rgba(245,243,248,0.45);
        --accent: #007FAD;
        --accent-light: rgba(0,127,173,0.75);
        --accent-bg: rgba(0,127,173,0.14);
        --type-food: #E8A85C;
        --type-transit: #9A9AAB;
        --type-outdoors: #7CD66B;
        --type-nightlife: #D77BC0;
        --danger: #DA3A16;
        --danger-bg: rgba(218,58,22,0.16);
        --success: #268500;
        --priority-high: #268500;
        --priority-medium: #007FAD;
        --priority-low: #DA3A16;
        /* Dark mode is explicitly out of scope for the day-card header
           restyle — aliasing to --text/--text-tertiary here reproduces
           exactly what .day-title/.day-date already looked like before
           those tokens existed, so dark mode's appearance doesn't change. */
        --day-title-color: var(--text);
        --day-date-color: var(--text-tertiary);
        --day-date-weight: 500;
        --shadow: 0 1px 2px rgba(0,0,0,0.2), 0 4px 8px rgba(0,0,0,0.16);
        --shadow-lg: 0 4px 20px rgba(0,0,0,0.35);
        --dock-bg: rgba(22,22,24,0.85);
        --track-bg: rgba(255,255,255,0.08);
        --badge-bg: rgba(255,255,255,0.06);
        --scrollbar-thumb: rgba(255,255,255,0.18);
      }
    }
    /* Manual overrides — win regardless of OS setting or source order
       (attribute selector outranks the plain :root/@media :root above). */
    :root[data-theme="light"] {
      --bg: #F5F3F8;
      --surface: #FFFFFF;
      --surface-hover: rgba(35,0,76,0.03);
      --border: rgba(35,0,76,0.08);
      --border-strong: rgba(35,0,76,0.16);
      --text: #23004C;
      --text-secondary: rgba(35,0,76,0.62);
      --text-tertiary: rgba(35,0,76,0.45);
      --accent: #7A00E6;
      --accent-light: rgba(122,0,230,0.7);
      --accent-bg: rgba(122,0,230,0.06);
      --danger: #DA3A16;
      --danger-bg: rgba(218,58,22,0.06);
      --success: #268500;
      --priority-high: #268500;
      --priority-medium: #007FAD;
      --priority-low: #DA3A16;
      --day-title-color: #7A00E6;
      --day-date-color: #23004C;
      --day-date-weight: 700;
      --shadow: 0 1px 2px rgba(0,0,0,0.04), 0 4px 8px rgba(0,0,0,0.02);
      --shadow-lg: 0 4px 16px rgba(0,0,0,0.06);
      --dock-bg: rgba(255,255,255,0.85);
      --track-bg: rgba(35,0,76,0.05);
      --badge-bg: rgba(35,0,76,0.04);
      --scrollbar-thumb: rgba(35,0,76,0.15);
    }
    :root[data-theme="dark"] {
      --bg: #000000;
      --surface: #161618;
      --surface-hover: #1E1E21;
      --border: rgba(255,255,255,0.08);
      --border-strong: rgba(255,255,255,0.16);
      --text: #F5F3F8;
      --text-secondary: rgba(245,243,248,0.65);
      --text-tertiary: rgba(245,243,248,0.45);
      --accent: #007FAD;
      --accent-light: rgba(0,127,173,0.75);
      --accent-bg: rgba(0,127,173,0.14);
      --danger: #DA3A16;
      --danger-bg: rgba(218,58,22,0.16);
      --success: #268500;
      --priority-high: #268500;
      --priority-medium: #007FAD;
      --priority-low: #DA3A16;
      --day-title-color: var(--text);
      --day-date-color: var(--text-tertiary);
      --day-date-weight: 500;
      --shadow: 0 1px 2px rgba(0,0,0,0.2), 0 4px 8px rgba(0,0,0,0.16);
      --shadow-lg: 0 4px 20px rgba(0,0,0,0.35);
      --dock-bg: rgba(22,22,24,0.85);
      --track-bg: rgba(255,255,255,0.08);
      --badge-bg: rgba(255,255,255,0.06);
      --scrollbar-thumb: rgba(255,255,255,0.18);
    }
    * { margin: 0; padding: 0; box-sizing: border-box; -webkit-tap-highlight-color: transparent; }
    html { height: 100%; overflow: hidden; }
    body {
      /* system-ui sits second so Android skins that ship their own UI face
         (Samsung One UI Sans, most notably) get it instead of falling all the
         way through to Roboto. -apple-system stays first: older Safari only
         understands that spelling. Both resolve to the same face everywhere
         else, so this is a no-op outside Samsung. */
      font-family: -apple-system, system-ui, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
      background: var(--bg); color: var(--text); height: 100%;
      line-height: 1.5; -webkit-font-smoothing: antialiased; overscroll-behavior-y: none;
    }
    ::-webkit-scrollbar { width: 4px; height: 4px; }
    /* Wide enough to actually grab with a pointer; the phone keeps the hairline. */
    @media (hover: hover) and (pointer: fine) {
      ::-webkit-scrollbar { width: 10px; height: 10px; }
      ::-webkit-scrollbar-thumb { border: 3px solid transparent; background-clip: content-box; border-radius: 8px; }
      ::-webkit-scrollbar-thumb:hover { background: var(--text-tertiary); background-clip: content-box; }
      /* The strips that deliberately hide their bar keep hiding it. */
      .dock::-webkit-scrollbar, .attachments::-webkit-scrollbar,
      .time-scroll::-webkit-scrollbar, .filter-chip-row::-webkit-scrollbar { display: none; }
    }
    ::-webkit-scrollbar-track { background: transparent; }
    ::-webkit-scrollbar-thumb { background: var(--scrollbar-thumb); border-radius: 4px; }

    :focus-visible { outline: 2px solid var(--text); outline-offset: 2px; border-radius: 4px; }
    .cb:focus-visible { outline-offset: 3px; }

    .app { height: 100%; display: flex; flex-direction: column; max-width: 1080px; margin: 0 auto; position: relative; }
    .app-header {
      position: sticky; top: 0; z-index: 50; background: var(--surface);
      border-bottom: 1px solid var(--border); padding: 14px 20px; flex-shrink: 0;
    }
    .header-bar { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
    .brand { display: flex; align-items: center; gap: 10px; }
    .brand-icon {
      width: 32px; height: 32px; border-radius: 10px; background: var(--text); color: var(--surface);
      display: flex; align-items: center; justify-content: center; flex-shrink: 0;
    }
    .brand-text h1 { font-size: 1rem; font-weight: 600; letter-spacing: -0.01em; }

    .icon-btn {
      width: 36px; height: 36px; border-radius: 50%; border: 1px solid var(--border);
      background: transparent; color: var(--text-secondary); display: flex;
      align-items: center; justify-content: center; cursor: pointer; transition: var(--transition); flex-shrink: 0;
    }
    .icon-btn:active { background: var(--bg); color: var(--text); }

    .main-scroll { flex: 1; overflow-y: auto; overflow-x: hidden; -webkit-overflow-scrolling: touch; padding: var(--scroll-pad); padding-bottom: calc(100px + env(safe-area-inset-bottom)); }

    .dock {
      position: fixed; bottom: calc(12px + env(safe-area-inset-bottom)); left: 50%;
      transform: translateX(-50%); z-index: 100; display: none; gap: 2px;
      background: var(--dock-bg); backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px);
      /* See :root[data-low-end] below — the blur is dropped on constrained
         devices, where a fixed, always-composited blur is the classic
         mid-range Android scroll-jank source. */
      border: 1px solid var(--border); border-radius: 24px; padding: 4px;
      box-shadow: var(--shadow-lg);
      /* 5 tabs at the old fixed 72px/button width could overflow off-screen
         on narrow phones (no wrap here, deliberately — a horizontally
         scrolling bottom dock reads worse than a slightly narrower one).
         The cap plus overflow-x is a defensive fallback if a 6th tab ever
         gets added and the width math below isn't revisited. */
      max-width: calc(100vw - 16px); overflow-x: auto; scrollbar-width: none;
    }
    .dock::-webkit-scrollbar { display: none; }
    .dock-btn {
      width: 64px; height: 44px; border-radius: 18px; border: none; background: transparent;
      color: var(--text-secondary); display: flex; flex-direction: column;
      align-items: center; justify-content: center; gap: 2px; cursor: pointer;
      font-family: inherit; font-size: 0.6875rem; font-weight: 600; letter-spacing: 0.02em;
      transition: var(--transition); flex-shrink: 0;
      position: relative; overflow: hidden;
    }
    .dock-btn .dock-icon { display: flex; align-items: center; justify-content: center; }
    .dock-btn.on { background: var(--text); color: var(--surface); }

    .fab {
      position: fixed; bottom: calc(76px + env(safe-area-inset-bottom)); right: 20px; z-index: 90;
      width: 52px; height: 52px; border-radius: 50%; background: var(--text); color: var(--surface);
      border: none; display: flex; align-items: center; justify-content: center;
      cursor: pointer; box-shadow: var(--shadow-lg); overflow: hidden;
      /* Bold spring for appear/disappear and press feedback — box-shadow
       stays on the flat --transition since a shadow overshooting looks
       like a rendering glitch, not intentional motion. */
      transition: transform var(--transition-settle), opacity 200ms ease, box-shadow var(--transition);
    }
    .fab.hidden { transform: scale(0) translateY(20px); opacity: 0; pointer-events: none; }
    @media (prefers-reduced-motion: reduce) { .fab { transition: opacity 200ms ease, box-shadow var(--transition); } }

    /* ══ Desktop layout: bottom dock becomes a side rail ════════════════════
       Measured before writing this: at 1920 the app was a 1080px column with
       840px of empty margin (44% of the window), and navigation was a 338px
       phone tab bar floating at the bottom centre — a thumb-zone affordance in
       the least reachable place for a mouse.

       Deliberately CSS-only, inside one media query. The markup and the JS are
       untouched, so the phone layout is unchanged BY CONSTRUCTION rather than
       by testing every rule twice. The dock is un-fixed so it can join a grid;
       everything else is geometry.

       grid-template-columns is `auto` for the rail on purpose: the dock's
       display is toggled inline by openTrip/closeTrip, so on Home it is
       display:none and an `auto` track collapses to zero — no empty 200px
       gutter on the trip list. A fixed track would have left one. */
    @media (min-width: 1024px) {
      /* Desktop type scale: every font-size is in rem, so raising the root
         raises the whole hierarchy proportionally. 16px stays the phone
         base (also what keeps iOS from zooming focused inputs); desktop
         viewing distance earns roughly one step up, large desktops two. */
      html { font-size: 17px; }

      .app {
        max-width: 1400px;
        display: grid;
        grid-template-columns: auto minmax(0, 1fr);
        grid-template-rows: auto minmax(0, 1fr);
      }
      .app-header { grid-column: 1 / -1; }

      .dock {
        position: static; grid-column: 1; grid-row: 2;
        left: auto; bottom: auto; transform: none; max-width: none;
        width: 200px; height: 100%; overflow-x: visible;
        flex-direction: column; align-items: stretch; gap: 2px;
        border-radius: 0; border: none;
        border-right: 1px solid var(--border);
        background: var(--surface); backdrop-filter: none; -webkit-backdrop-filter: none;
        padding: 12px 10px; box-shadow: none;
      }
      /* Rail items read as list rows, not phone tab stacks: icon beside label,
         left-aligned, full width. */
      .dock-btn {
        width: 100%; height: 40px; border-radius: var(--radius-md);
        flex-direction: row; justify-content: flex-start; gap: 10px;
        padding: 0 12px; font-size: 0.8125rem; letter-spacing: 0;
      }

      /* The dock no longer floats over the content, so the reserved strip goes. */
      .main-scroll { grid-column: 2; grid-row: 2; padding-bottom: 24px; }

      /* Anchor the FAB to the content column rather than the screen corner —
         .app is already position:relative, so absolute puts it at the bottom
         right of the app, not 400px away at the edge of a wide monitor. */
      .fab { position: absolute; bottom: 24px; right: 24px; }
    }

    /* ══ Desktop pointer affordances ════════════════════════════════════════
       This app grew phone-first, so nearly every control had :active (touch
       feedback) and no :hover — a mouse user got no response at all until the
       click landed. Measured before writing this: only 4 of 21 primary
       interactive controls had a hover rule.

       Everything here is behind (hover: hover) and (pointer: fine) so a touch
       device never gets sticky hover left behind after a tap — the classic
       phone bug from adding desktop hover carelessly.

       Focus-visible is defined alongside, deliberately: keyboard users get no
       hover cue at all, so the focus ring has to be at least as loud as the
       hover state (§8). It's one shared token so the two can't drift. */
    :root {
      --focus-ring: 0 0 0 2px var(--surface), 0 0 0 4px var(--accent);
      --scroll-pad: 20px;
      --card-pad: 16px;
      /* How far a sticky top:0 sits below #mainScroll's visual top edge.
         MEASURED, not derived: it is the same 32px for a header nested in a
         card (.day-header) and one that isn't (.tag-group-header), so it is a
         property of the scrollport, not of either element's padding - which
         is why it isn't computed from --scroll-pad/--card-pad. Negating it
         pins headers flush. An e2e test asserts that flushness, so if this
         ever drifts the suite fails instead of the UI quietly sagging. */
      --sticky-top: -32px;
    }

    @media (hover: hover) and (pointer: fine) {
      /* Buttons and pills lift slightly toward the pointer. */
      .btn:hover, .btn-sm:hover, .media-btn:hover, .chip:hover {
        border-color: var(--text-tertiary); background: var(--surface-hover, var(--bg));
      }
      /* Icon-only controls fill their circle rather than moving, so a dense
         icon cluster doesn't jitter as the pointer crosses it. */
      .icon-btn:hover, .del-btn:hover, .modal-close:hover, .pri-btn:hover, .qty-btn:hover {
        background: var(--bg); color: var(--text);
      }
      .cb:hover { border-color: var(--text-secondary); }
      /* Rows and cards get a background wash — the standard desktop list cue. */
      .item:hover, .tool-row:hover, .activity:hover, .expense:hover { background: var(--bg); }
      .trip-card:hover, .note:hover { box-shadow: var(--shadow); border-color: var(--border-strong); }
      .dock-btn:hover:not(.on) { background: var(--bg); color: var(--text); }
      .fab:hover { box-shadow: var(--shadow-lg), 0 0 0 4px var(--accent-bg); }
    }

    /* Focus ring — applies on every device, keyboard users exist everywhere. */
    .btn:focus-visible, .btn-sm:focus-visible, .chip:focus-visible, .icon-btn:focus-visible,
    .del-btn:focus-visible, .modal-close:focus-visible, .media-btn:focus-visible,
    .dock-btn:focus-visible, .add-item-btn:focus-visible, .fab:focus-visible,
    .pri-btn:focus-visible, .qty-btn:focus-visible, .tool-row:focus-visible,
    .trip-card:focus-visible, .day-header:focus-visible, .activity:focus-visible,
    .day-menu-item:focus-visible, .card-menu-item:focus-visible {
      outline: none; box-shadow: var(--focus-ring);
      /* A ring drawn outside the element must not be clipped by an ancestor
         that hides overflow (list rows do). */
      position: relative; z-index: 1;
    }
    /* No transition declaration is added here on purpose. These controls
       already carry `transition: var(--transition)`, which is a shorthand with
       no property list, so it animates ALL animatable properties — box-shadow
       and border-color included. Re-declaring transition here would have
       silently replaced .trip-card's own `transform 150ms var(--ease-settle)`
       spring from the mobile pass, trading a working animation for one this
       block didn't need. */

    /* ── Low-end device fallback (§5) ──────────────────────────────────────
       Set by applyLowEndFallback() in app-core.js from hardwareConcurrency /
       deviceMemory. The dock is fixed and composited on every scroll frame, so
       its blur is the most expensive always-on effect in the app; on a
       constrained phone a solid fill keeps the surface readable and the scroll
       cheap. The colour is the same token at full opacity, so the dock still
       reads as the same component rather than suddenly looking unstyled.

       This is the guarded, evidence-free-safe half of the backdrop-filter
       question: capable phones keep the blur, constrained ones never pay for
       it, and no measurement was needed to justify either. Whether a MID-range
       phone should also drop it stays a device-check (see the findings doc). */
    :root[data-low-end] .dock { backdrop-filter: none; -webkit-backdrop-filter: none; background: var(--surface); }
    :root[data-low-end] .modal-backdrop { backdrop-filter: none; -webkit-backdrop-filter: none; }

    .card {
      background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg);
      padding: var(--card-pad); transition: box-shadow var(--transition);
      overflow: hidden;
    }
    .card:hover { box-shadow: var(--shadow); }

    .progress-card { padding: 12px; margin-bottom: 12px; }
    .progress-header { display: flex; justify-content: space-between; align-items: flex-end; margin-bottom: 10px; }
    .progress-label { font-size: 0.6875rem; font-weight: 700; color: var(--text-secondary); text-transform: uppercase; letter-spacing: 0.08em; }
    .progress-value { font-size: 1.5rem; font-weight: 600; color: var(--text); letter-spacing: -0.02em; line-height: 1; }
    .progress-track { height: 3px; background: var(--track-bg); border-radius: 2px; overflow: hidden; }
    .progress-fill { height: 100%; background: var(--text); border-radius: 2px; transition: width 400ms var(--transition); }

    .chip {
      padding: 6px 12px; border-radius: 20px; border: 1px solid var(--border); background: var(--surface);
      color: var(--text-secondary); font-size: 0.75rem; font-weight: 500; cursor: pointer;
      white-space: nowrap; flex-shrink: 0; transition: var(--transition); font-family: inherit;
      position: relative; overflow: hidden;
    }
    .chip:active { border-color: var(--text-tertiary); color: var(--text); }
    .chip.on { background: var(--text); color: var(--surface); border-color: var(--text); }

    .grid { display: flex; flex-direction: column; gap: 10px; }
    @media (min-width: 640px) { .grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px; } }
    @media (min-width: 1000px) { .grid { grid-template-columns: repeat(3, 1fr); } }

    .cat-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px; }
    .cat-title { font-size: 0.875rem; font-weight: 600; display: flex; align-items: center; gap: 8px; }
    .cat-badge { font-size: 0.6875rem; font-weight: 700; color: var(--text-tertiary); background: var(--badge-bg); padding: 2px 6px; border-radius: 12px; }
    .del-btn { width: 24px; height: 24px; border-radius: 50%; border: none; background: transparent; color: var(--text-tertiary); display: flex; align-items: center; justify-content: center; cursor: pointer; transition: var(--transition); }
    .del-btn:active { background: var(--danger-bg); color: var(--danger); }
    /* Dedicated drag-to-reorder handle for Activity/Commute cards — same
       size/shape as the adjacent move/delete .del-btn icons so the three
       sit as one consistent icon cluster, but with touch-action:none set
       permanently (not toggled at drag time) so the browser never has a
       chance to treat drag movement as a native scroll for a touch that
       started here — that's what a long-press-anywhere-on-card gesture
       couldn't guarantee, since touch-action there had to stay pan-y for
       normal list scrolling. */
    .drag-grip { touch-action: none; cursor: grab; }
    .drag-grip:active { cursor: grabbing; }

    /* gap 5 -> 8: the extra 3px per gap is spent entirely on the hit-area
       expanders below (which widen to match), buying ~3px per control without
       shrinking any control's visual size. Five gaps costs the item text 15px
       of a 338px row — the cheapest horizontal room available, short of
       removing a control, which is the density decision recorded in the
       findings doc. */
    .item { display: flex; align-items: center; gap: 8px; padding: 4px 0; border-bottom: 1px solid var(--border); min-height: 48px; position: relative; overflow: hidden; }

    /* ── Tap-target expanders ───────────────────────────────────────────────
       The in-row controls are 18–24px visually, well under the 44px floor.
       These transparent overlays grow the HIT area without touching the
       visuals or the layout — the same technique .ticket-checkbox already
       uses, so this follows an existing convention rather than inventing one.

       Sized from measured geometry, not from the guideline number: list rows
       are >=48px tall (min-height on .item), so 44px of height fits inside one row without spilling
       into the row above or below. Horizontally the controls sit in an 8px
       gap (widened from 5 for exactly this purpose), so each side grows by
       3px — go wider and neighbouring overlays overlap, which makes the WRONG
       control absorb the tap and is worse than a small target.

       Reaching a true 44x44 on every one of these would mean fewer controls
       per row or taller rows, i.e. undoing the deliberate compact-list pass.
       That's a density decision, not a CSS one — see the findings doc. */
    .cb, .pri-btn, .qty-btn, .del-btn { position: relative; }
    .cb::after, .pri-btn::after, .qty-btn::after, .item .del-btn::after {
      content: ''; position: absolute; left: 50%; top: 50%;
      transform: translate(-50%, -50%);
      width: calc(100% + 6px); height: 44px;
    }
    /* The gift row's delete is pinned to the row corner (position:absolute), so
       a 44px-tall overlay would reach down over the row's text. Keep its
       expansion to the button's own box plus the same 2px-a-side allowance. */
    /* Pinned to the row corner, so a centred 44px overlay would reach above the
       row and be clipped. 40px is what actually fits below the 4px pin inside a
       48px row. */
    .gift-del-btn::after { height: 40px; }
    /* The add-Activity/add-Commute row has no neighbours above or below it, so
       its buttons can take the full 44px with no overlap risk at all. */
    .add-item-btn { position: relative; }
    .add-item-btn::after {
      content: ''; position: absolute; left: 0; top: 50%;
      transform: translateY(-50%); width: 100%; height: 44px;
    }
    @media (pointer: fine) {
      /* Mouse users don't need the expansion and it can swallow hover on
         neighbouring controls, so drop it where a precise pointer exists. */
      .cb::after, .pri-btn::after, .qty-btn::after, .item .del-btn::after { display: none; }
    }
    .item:last-child { border-bottom: none; }
    .item.done { opacity: 0.45; }
    .item.done .it-text { text-decoration: line-through; color: var(--text-tertiary); }

    .cb { width: 18px; height: 18px; min-width: 18px; border-radius: 5px; border: 1.5px solid var(--border-strong); display: flex; align-items: center; justify-content: center; cursor: pointer; transition: var(--transition); background: transparent; }
    .cb.on { background: var(--text); border-color: var(--text); }
    .cb svg { color: var(--surface); width: 10px; height: 10px; display: none; }
    .cb.on svg { display: block; }

    .it-text { flex: 1; font-size: 0.875rem; color: var(--text); padding: 3px; border-radius: 5px; border: 1px solid transparent; outline: none; transition: var(--transition); }
    .it-text:focus { border-color: var(--border-strong); background: var(--surface-hover); }

    .cat-name-text { border-radius: 5px; border: 1px solid transparent; outline: none; padding: 1px 3px; margin: -1px -3px; transition: var(--transition); }
    .cat-name-text:focus { border-color: var(--border-strong); background: var(--surface-hover); }

    .add-form { display: grid; grid-template-columns: 1fr 40px; gap: 6px; margin-top: 8px; padding-top: 8px; border-top: 1px solid var(--border); min-width: 0; }
    .add-form > * { min-width: 0; overflow: hidden; }
    .add-form > .btn-sm { overflow: visible; }
    .add-form .btn-icon { width: 40px; height: 40px; border-radius: 50%; padding: 0; display: flex; align-items: center; justify-content: center; }
    .time-inp { width: 100%; min-width: 0; padding: 8px 2px; text-align: center; -webkit-appearance: none; appearance: none; font-size: 0.8125rem; }
    /* 16px, not 15: iOS Safari zooms the viewport when a text field under 16px
       takes focus and never zooms back out. .field inputs are already 16px;
       this is the inline add-item form, which was the odd one out. */
    .inp { flex: 1; background: var(--bg); border: 1px solid var(--border); border-radius: var(--radius-md); padding: 8px 10px; color: var(--text); font-family: inherit; font-size: 1rem; outline: none; transition: var(--transition); min-width: 0; width: 100%; }
    .inp:focus { border-color: var(--border-strong); }
    .btn-sm { padding: 8px 12px; border-radius: var(--radius-md); border: none; background: var(--text); color: var(--surface); font-weight: 600; font-size: 0.75rem; cursor: pointer; font-family: inherit; transition: var(--transition); display: inline-flex; align-items: center; justify-content: center; text-align: center; }

    .slot { border: 1.5px dashed var(--border); background: transparent; display: flex; align-items: center; justify-content: center; min-height: 56px; cursor: pointer; color: var(--text-tertiary); gap: 6px; font-weight: 500; font-size: 0.8125rem; border-radius: var(--radius-lg); transition: var(--transition); }
    .slot:active { border-color: var(--text-tertiary); color: var(--text-secondary); background: var(--surface-hover); }
    .slot svg { width: 16px; height: 16px; }

    .empty { text-align: center; padding: 60px 20px; color: var(--text-tertiary); }
    .empty svg { width: 40px; height: 40px; stroke-width: 1; opacity: 0.3; margin-bottom: 12px; }
    .empty p { font-size: 0.9375rem; font-weight: 500; }

    .notes-toolbar { display: flex; justify-content: space-between; align-items: center; gap: 8px; margin-bottom: 16px; }
    .filter-bar { display: flex; align-items: center; gap: 6px; flex: 1; }
    .filter-bar select { flex: 1; background: var(--surface); border: 1px solid var(--border); color: var(--text); padding: 6px 28px 6px 10px; border-radius: 20px; font-size: 0.75rem; outline: none; cursor: pointer; appearance: none; background-image: url("data:image/svg+xml,%3Csvg width='10' height='6' viewBox='0 0 10 6' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M1 1L5 5L9 1' stroke='%23A0A0A0' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E"); background-repeat: no-repeat; background-position: right 10px center; min-height: 32px; }

    .tag-group { margin-bottom: 14px; }
    .tag-group-header { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; padding: 0 4px; }
    .tag-group-line { flex: 1; height: 1px; background: var(--border); }
    .tag-group-label { font-size: 0.6875rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.12em; color: var(--text-secondary); padding: 2px 8px; border-radius: 20px; border: 1px solid var(--border); background: var(--surface); flex-shrink: 0; }
    .tag-group-count { font-size: 0.6875rem; color: var(--text-tertiary); font-weight: 700; }

    .note { position: relative; padding: 0; overflow: hidden; }
    .note.selectable { cursor: pointer; }
    .note.selected { box-shadow: inset 0 0 0 2px var(--accent); }
    .note-head .cb { flex-shrink: 0; }
    .note-accent { position: absolute; left: 0; top: 0; bottom: 0; width: 2px; }
    .note-body { padding: 12px; }
    .note-head { display: flex; justify-content: space-between; align-items: flex-start; gap: 8px; min-width: 0; }
    .note-title { font-size: 0.875rem; font-weight: 600; word-break: break-word; overflow-wrap: break-word; line-height: 1.3; flex: 1; min-width: 0; overflow: hidden; }
    .note-tag { font-size: 0.6875rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.08em; padding: 2px 8px; border-radius: 20px; border: 1px solid var(--border); color: var(--text-secondary); background: var(--bg); flex-shrink: 0; white-space: nowrap; }
    .note-content { color: var(--text-secondary); font-size: 0.8125rem; line-height: 1.45; white-space: pre-wrap; word-break: break-word; overflow-wrap: break-word; margin-bottom: 8px; }
    /* Backlink from a note to the activity/commute it was created from —
       shown both on the note card in the Notes tab and inside the note
       edit modal (see openNoteModal/renderNotes). */
    .note-backlink { display: flex; align-items: center; gap: 5px; font-size: 0.6875rem; font-weight: 600; color: var(--accent); cursor: pointer; margin-bottom: 8px; }
    .note-backlink svg { width: 12px; height: 12px; flex-shrink: 0; }
    .note-backlink:hover { text-decoration: underline; }
    /* Linked-notes list inside the Activity/Commute edit modal. */
    .linked-note-row { display: flex; align-items: center; gap: 8px; padding: 8px 10px; border: 1px solid var(--border); border-radius: var(--radius-md); margin-bottom: 6px; cursor: pointer; transition: var(--transition); }
    .linked-note-row:hover { background: var(--bg); }
    .linked-note-row svg { flex-shrink: 0; color: var(--text-tertiary); }
    .linked-note-title { font-size: 0.8125rem; font-weight: 600; }
    .linked-note-snippet { font-size: 0.6875rem; color: var(--text-tertiary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .note-foot { display: flex; justify-content: space-between; align-items: center; font-size: 0.6875rem; color: var(--text-tertiary); font-weight: 500; padding-top: 8px; border-top: 1px solid var(--border); }
    .note-actions { display: flex; gap: 2px; }
    .note-actions .icon-btn { width: 24px; height: 24px; border: none; }

    .attachments { display: flex; gap: 6px; overflow-x: auto; padding: 2px 0 6px; -webkit-overflow-scrolling: touch; scrollbar-width: none; }
    .attachments::-webkit-scrollbar { display: none; }
    .att { position: relative; flex-shrink: 0; width: 60px; height: 60px; border-radius: var(--radius-sm); overflow: hidden; background: var(--bg); display: flex; align-items: center; justify-content: center; cursor: pointer; }
    .att img, .att video { width: 100%; height: 100%; object-fit: cover; }
    .att .play { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; background: rgba(0,0,0,0.1); }
    .att .play svg { color: white; width: 16px; height: 16px; }

    .data-actions { display: flex; justify-content: center; gap: 8px; padding: 24px 0 16px; border-top: 1px solid var(--border); margin-top: 24px; flex-wrap: wrap; }
    .data-actions button { background: transparent; border: none; color: var(--text-tertiary); font-size: 0.75rem; font-weight: 600; cursor: pointer; font-family: inherit; padding: 6px 10px; border-radius: var(--radius-md); transition: var(--transition); display: inline-flex; align-items: center; gap: 4px; white-space: nowrap; }
    .data-actions button:active { color: var(--text); background: var(--surface-hover); }
    @media (max-width: 480px) {
      .data-actions { gap: 4px; padding: 16px 0 12px; }
      .data-actions button { font-size: 0.6875rem; padding: 5px 8px; }
      .data-actions button svg { width: 14px; height: 14px; }
    }

    .modal { position: fixed; inset: 0; z-index: 1000; display: flex; flex-direction: column; opacity: 0; pointer-events: none; transition: opacity 200ms; }
    .modal.open { opacity: 1; pointer-events: auto; }
    .modal-backdrop { position: absolute; inset: 0; background: rgba(0,0,0,0.15); backdrop-filter: blur(4px); -webkit-backdrop-filter: blur(4px); }
    /* Bottom sheet — the primary modal pattern in One UI, so this is the
       motion that matters most. Slides up from the bottom and settles with
       --ease-settle: it decelerates into place rather than springing past and
       bouncing back, which is the single clearest motion tell between One UI
       and iOS. The 0.94→1 scale is kept but now reads as the sheet easing to
       full size, not overshooting it. Large radius on the TOP corners only
       (--radius-xl, 30px) stays visible throughout the travel, which is the
       other half of the One UI sheet signature. One transition serves both
       directions rather than giving each its own curve, which the CSS
       transition model makes fragile to get right. */
    .modal-panel { position: relative; margin-top: auto; background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-xl) var(--radius-xl) 0 0; max-height: 92vh; overflow: hidden; display: flex; flex-direction: column; transform: translateY(100%) scale(0.94); transition: transform 420ms var(--ease-settle); }
    .modal.open .modal-panel { transform: translateY(0) scale(1); }
    /* Bottom-sheet drag handle. One UI's sheets are grabbable, so the bar is a
       real affordance, not decoration — see setupSheetDrag in app-core.js. It
       is injected once per panel by applyDialogSemantics rather than written
       into all 28 modals, so any modal added later gets it free.
       Hidden at >=768px, where .modal-panel is a centred dialog rather than a
       sheet and there is nothing to drag down. */
    .sheet-handle {
      flex-shrink: 0; width: 40px; height: 4px; margin: 10px auto 0;
      border-radius: 999px; background: var(--border-strong);
      position: relative; cursor: grab;
      /* We own the vertical gesture here, so the browser must not also try to
         scroll the panel while the sheet is being dragged. */
      touch-action: none;
    }
    .sheet-handle:active { cursor: grabbing; }
    /* The grab target is far larger than the 40x4 bar it shows. */
    .sheet-handle::before { content: ''; position: absolute; left: 50%; top: 50%; transform: translate(-50%,-50%); width: 160px; height: 32px; }
    /* While a finger is on the sheet it must track that finger exactly, so the
       settle transition is suspended for the duration of the drag. */
    .modal-panel.dragging { transition: none; }
    @media (min-width: 768px) { .sheet-handle { display: none; } }
    @media (prefers-reduced-motion: reduce) { .modal-panel { transition: transform 200ms ease; } }
    @media (min-width: 768px) {
      /* A dialog, not a sheet: centered in both axes, and it appears with a
         quick settle instead of the phone's 420ms slide-from-the-thumb. */
      .modal-panel {
        margin: auto; border-radius: var(--radius-lg); max-height: 85vh;
        width: 100%; max-width: 520px;
        transform: translateY(10px) scale(0.98);
        transition: transform 160ms var(--ease-settle);
      }
      .modal.open .modal-panel { transform: translateY(0) scale(1); }
    }
    @media (min-width: 1024px) {
      /* The dense editors earn a wider canvas than list-y pickers do. */
      #activityModal .modal-panel, #commuteModal .modal-panel { max-width: 680px; }
      #noteModal .modal-panel { max-width: 620px; }
    }
    .modal-header { padding: 20px 24px 0; display: flex; justify-content: space-between; align-items: center; flex-shrink: 0; }
    .modal-header h3 { font-size: 1.0625rem; font-weight: 600; }
    .modal-close { width: 32px; height: 32px; border-radius: 50%; border: none; background: var(--bg); color: var(--text-secondary); display: flex; align-items: center; justify-content: center; cursor: pointer; transition: var(--transition); position: relative; overflow: hidden; }
    .modal-close:active { background: var(--border); color: var(--text); }
    .modal-body { padding: 16px 24px; overflow-y: auto; -webkit-overflow-scrolling: touch; flex: 1; }
    .modal-footer { padding: 12px 24px calc(12px + env(safe-area-inset-bottom)); display: flex; justify-content: flex-end; gap: 8px; border-top: 1px solid var(--border); flex-shrink: 0; }

    .field { margin-bottom: 15px; }
    .field label { display: block; font-size: 0.6875rem; font-weight: 700; color: var(--text-secondary); text-transform: uppercase; letter-spacing: 0.08em; margin-bottom: 6px; }
    .field input, .field textarea, .field select { width: 100%; background: var(--bg); border: 1px solid var(--border); border-radius: var(--radius-md); padding: 10px 12px; color: var(--text); font-family: inherit; font-size: 1rem; outline: none; transition: var(--transition); }
    .field input:focus, .field textarea:focus, .field select:focus { border-color: var(--text-secondary); }
    .field textarea { min-height: 120px; resize: vertical; line-height: 1.5; }
    .field-hint { font-size: 0.71875rem; color: var(--text-tertiary); margin-top: 6px; line-height: 1.4; }
    .field-row { display: flex; gap: 12px; }
    .field-row .field { flex: 1; margin-bottom: 0; }
    @media (max-width: 480px) { .field-row { flex-direction: column; gap: 20px; } .field-row .field { margin-bottom: 0; } }

    /* Groups Time + Date + Timezone for one endpoint (start or end), or Mode
       + Route, into a single visual unit instead of scattering related
       fields across separate rows of the form. Tinted background (not just
       a border) so the bundling actually reads at a glance — see
       app.onActivity/CommuteStart*Change(). */
    .time-group { background: var(--accent-bg); border: 1px solid var(--border-strong); border-radius: var(--radius-md); padding: 14px; margin-bottom: 15px; }
    .time-group-label { font-size: 0.6875rem; font-weight: 700; color: var(--accent); text-transform: uppercase; letter-spacing: 0.08em; margin-bottom: 12px; display: flex; align-items: center; gap: 6px; }
    .time-group-label .hint { font-weight: 400; color: var(--text-tertiary); text-transform: none; letter-spacing: normal; }
    .time-group .field-row { margin-bottom: 15px; }
    .time-group > .field:last-child { margin-bottom: 0; }
    .time-group .field input, .time-group .field select, .time-group .field textarea { background: var(--surface); }

    /* Edit Activity popup: Title → Start → End → Location → Description →
       Notes, top to bottom, each gap a single deliberate value shared by
       the element above and below it (no stacked/double margins). Kept
       deliberately tight — Start/End/Location need to be visible together
       without scrolling — so the popup gets its own (scoped) container
       padding too, instead of the roomier defaults every other modal uses. */
    #activityModal .modal-header { padding: 12px 16px 0; }
    #activityModal .modal-body { padding: 10px 16px; }
    .activity-title-field { margin-top: 0; margin-bottom: 9px; }
    .activity-time-module { padding: 10px 12px; margin-bottom: 9px; }
    .activity-time-module.end-module { margin-bottom: 9px; }
    .activity-desc-field { margin-bottom: 10px; }
    .activity-desc-field textarea { min-height: 70px; }
    .activity-notes-field { margin-bottom: 14px; }
    .notes-section-header { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 5px; }
    .notes-section-header label { margin-bottom: 0; }
    .btn-add-note-inline { display: inline-flex; align-items: center; gap: 4px; background: var(--surface); border: 1px solid var(--border-strong); border-radius: var(--radius-sm); padding: 8px 10px; line-height: 1; color: var(--accent); font-size: 0.6875rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.05em; cursor: pointer; flex-shrink: 0; }
    .btn-add-note-inline:hover { background: var(--accent-bg); }
    #activityModal .modal-footer { padding: 14px 16px calc(14px + env(safe-area-inset-bottom)); }
    @media (max-width: 480px) {
      #activityModal .modal-header { padding: 10px 12px 0; }
      #activityModal .modal-body { padding: 8px 12px; }
      .activity-desc-field textarea { min-height: 54px; }
      #activityModal .modal-footer { padding: 12px 12px calc(12px + env(safe-area-inset-bottom)); flex-direction: column-reverse; gap: 8px; }
      #activityModal .modal-footer .btn { width: 100%; }
    }

    /* Multi-step commute editor — one .time-group per step, collapsed to a
       single summary line except the step being worked on, to cut down
       scroll height on commutes with several steps. Padding/spacing here
       is intentionally tighter than the base .time-group/.field defaults
       above (still used as-is by the Activity modal) — everything is
       scoped under .commute-step-editor so it doesn't leak there. */
    .commute-step-editor { padding: 10px 12px; margin-bottom: 8px; }
    .commute-step-editor .field { margin-bottom: 8px; }
    .commute-step-editor .time-group-label { margin-bottom: 0; }
    .commute-step-header { cursor: pointer; border-radius: var(--radius-sm); margin: -4px -6px; padding: 4px 6px; }
    .commute-step-header:hover { background: var(--surface); }
    .commute-step-header .step-title { flex-shrink: 0; }
    .step-summary { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 500; text-transform: none; letter-spacing: normal; color: var(--text-tertiary); }
    .step-actions { display: flex; align-items: center; gap: 2px; text-transform: none; letter-spacing: normal; flex-shrink: 0; }
    .step-action-btn { width: 24px; height: 24px; border-radius: 50%; border: none; background: transparent; color: var(--text-tertiary); display: flex; align-items: center; justify-content: center; cursor: pointer; transition: var(--transition); }
    .step-action-btn:hover, .step-action-btn:active { background: var(--surface); color: var(--text); }
    .step-action-remove:hover, .step-action-remove:active { color: var(--danger); background: var(--danger-bg); }
    .step-chevron { flex-shrink: 0; display: flex; color: var(--text-tertiary); transition: transform 150ms; margin-left: 2px; }
    .commute-step-body { margin-top: 10px; }

    /* Location input + its "preview in Maps" action share one row instead
       of the preview link sitting on its own line below — the Activity
       modal reuses this same icon-only row now too, for parity with the
       Commute step editor. */
    .location-input-row { display: flex; align-items: center; gap: 6px; }
    .location-input-row input { flex: 1; min-width: 0; }
    /* enhanceTripLocationField wraps the input in a .tz-combobox div (so
       its suggestion dropdown can be positioned relative to it) — that
       div, not the input directly, is now the flex child here, so it
       needs the flex:1 the rule above gave the input, or the row
       collapses back to the maps-preview button's own width. */
    .location-input-row .tz-combobox { flex: 1; min-width: 0; }
    .maps-preview-icon-btn { flex-shrink: 0; width: 36px; height: 36px; border-radius: var(--radius-md); border: 1px solid var(--border); background: var(--surface); color: var(--text-secondary); display: flex; align-items: center; justify-content: center; cursor: pointer; transition: var(--transition); }
    .maps-preview-icon-btn:hover { color: var(--text); border-color: var(--border-strong); }

    /* Time / Date / Timezone compacted into one row instead of three
       stacked ones — the single biggest source of scroll height per step. */
    /* Unequal thirds rather than a plain repeat(3) — "HH:MM" needs far
       less room than a native date input (which reserves space for its
       own calendar-icon affordance) or a city+abbreviation timezone
       label, and equal columns were clipping the date field's last
       digit at narrow widths. */
    .step-time-grid { display: grid; grid-template-columns: 0.8fr 1.15fr 1.25fr; gap: 6px; }
    .step-time-grid .field { margin-bottom: 0; min-width: 0; }
    .step-time-grid .field label { font-size: 0.6875rem; margin-bottom: 3px; }
    /* 16px despite this grid being deliberately tight elsewhere (labels stay
       9px): these are text-entry fields, and anything under 16px makes iOS
       Safari zoom the viewport on focus and never zoom back — which is far
       worse on a phone than a couple of tight columns. Measured at 360px: the
       time and date fields fit at 16px with zero clipping and the grid, panel
       and page all stay overflow-free. The timezone combobox does truncate its
       label a bit sooner (it already truncated at 13px — it's a search field,
       and the resolved zone is shown on the card anyway). */
    .step-time-grid input, .step-time-grid select, .step-time-grid .tz-search-input { font-size: 1rem; padding: 8px 6px; }
    .step-time-grid input[type="date"] { padding-right: 2px; }

    /* Same Time/Date/Timezone trio as .step-time-grid, but for the Activity
       modal's Start/End modules — roomier (this popup only ever shows two
       of these, never a scrollable list of them). On mobile this goes to a
       2-up layout (Time+Date, then Timezone spanning both) rather than a
       full one-per-row stack — three full rows per module was tall enough
       on its own to force scrolling past Location, which the compact-
       spacing spec explicitly asks to avoid. */
    .activity-time-grid { display: grid; grid-template-columns: 0.8fr 1.15fr 1.25fr; gap: 9px; }
    .activity-time-grid .field { margin-bottom: 0; min-width: 0; }
    .activity-time-module .time-group-label { margin-bottom: 6px; }
    @media (max-width: 480px) {
      .activity-time-grid { grid-template-columns: 1fr 1fr; gap: 7px; }
      .activity-time-grid .field:nth-child(3) { grid-column: 1 / -1; }
      .activity-time-grid .field label { margin-bottom: 3px; }
      .activity-time-grid input, .activity-time-grid select, .activity-time-grid .tz-search-input { padding: 8px; }
    }

    /* Location field inside the (now-compact) Edit Activity popup — same
       input, just less vertical breathing room than the default .field
       gives every other text field, and a tighter gap to Description. */
    .activity-location-field { margin-bottom: 11px; }
    .activity-location-field input { padding-top: 6px; padding-bottom: 6px; }

    .commute-add-step-btn { display: flex; align-items: center; justify-content: center; gap: 6px; width: 100%; margin-bottom: 15px; color: var(--accent); border-color: var(--border-strong); }

    /* Google Places autocomplete progressively replaces the plain location
       input when a GOOGLE_MAPS_API_KEY is configured (see app-core.js) —
       the original input is kept in the DOM as a hidden data store. */
    .place-autocomplete-el {
      width: 100%;
      /* Places UI Kit theming — these are the documented custom properties
         the shadow-DOM widget reads (they inherit through the shadow
         boundary), mapped onto our existing tokens so this tracks
         light/dark/auto automatically instead of needing its own copies. */
      --gmp-mat-color-surface: var(--surface);
      --gmp-mat-color-on-surface: var(--text);
      --gmp-mat-color-on-surface-variant: var(--text-secondary);
      --gmp-mat-color-primary: var(--accent);
      --gmp-mat-color-outline-decorative: var(--border-strong);
      --gmp-mat-color-secondary-container: var(--accent-bg);
      --gmp-mat-color-on-secondary-container: var(--accent);
      --gmp-mat-color-neutral-container: var(--surface-hover);
      --gmp-mat-color-on-neutral-container: var(--text);
      --gmp-mat-font-family: inherit;
      color-scheme: light dark;
      background-color: var(--surface);
      border: 1px solid var(--border);
      border-radius: var(--radius-md);
    }
    /* "Auto" (no [data-theme]) leaves color-scheme: light dark above, so the
       widget's own OS-driven fallback still matches system theme; manual
       light/dark picks pin it the same way the rest of the app is pinned. */
    :root[data-theme="light"] .place-autocomplete-el { color-scheme: light; }
    :root[data-theme="dark"] .place-autocomplete-el { color-scheme: dark; }

    /* Searchable timezone combobox — layers type-to-filter over the real
       <select> (kept in the DOM, just hidden, as the source of truth). */
    .tz-native-select { display: none; }
    .tz-combobox { position: relative; }
    .tz-search-input { width: 100%; background: var(--bg); border: 1px solid var(--border); border-radius: var(--radius-md); padding: 10px 12px; color: var(--text); font-family: inherit; font-size: 1rem; outline: none; transition: var(--transition); }
    .tz-search-input:focus { border-color: var(--text-secondary); }
    .time-group .tz-search-input { background: var(--surface); }
    .tz-dropdown { position: absolute; top: calc(100% + 4px); left: 0; right: 0; max-height: 220px; overflow-y: auto; background: var(--surface); border: 1px solid var(--border-strong); border-radius: var(--radius-md); box-shadow: var(--shadow-lg); z-index: 60; }
    .tz-option { padding: 10px 12px; font-size: 0.875rem; cursor: pointer; }
    .tz-option:hover, .tz-option.active { background: var(--accent-bg); color: var(--accent); }
    .tz-option-empty { padding: 10px 12px; font-size: 0.875rem; color: var(--text-tertiary); }

    .btn { padding: 10px 18px; border-radius: var(--radius-md); border: 1px solid var(--border); background: var(--surface); color: var(--text); font-weight: 600; font-size: 0.875rem; cursor: pointer; font-family: inherit; transition: var(--transition); position: relative; overflow: hidden; }
    .btn:active { background: var(--bg); }
    .btn-primary { background: var(--text); color: var(--surface); border-color: var(--text); }
    .btn-danger { background: var(--danger); color: white; border-color: var(--danger); }

    .attachments-section { margin-top: 8px; }
    .attachments-label { font-size: 0.6875rem; font-weight: 700; color: var(--text-secondary); text-transform: uppercase; letter-spacing: 0.08em; margin-bottom: 12px; display: flex; justify-content: space-between; }
    .attachments-grid { display: flex; gap: 10px; flex-wrap: wrap; margin-bottom: 12px; }
    .att-preview { position: relative; width: 80px; height: 80px; border-radius: var(--radius-md); overflow: hidden; border: 1px solid var(--border); background: var(--bg); flex-shrink: 0; }
    .att-preview img, .att-preview video { width: 100%; height: 100%; object-fit: cover; }
    .att-preview .file-icon { width: 100%; height: 100%; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px; color: var(--text-secondary); font-size: 0.75rem; }
    .att-preview .file-icon svg { width: 24px; height: 24px; }
    .att-remove { position: absolute; top: -6px; right: -6px; width: 20px; height: 20px; border-radius: 50%; background: var(--danger); color: white; border: 2px solid var(--surface); font-size: 0.6875rem; cursor: pointer; display: flex; align-items: center; justify-content: center; }
    .media-actions { display: flex; gap: 8px; flex-wrap: wrap; }
    .media-btn { padding: 8px 12px; border-radius: 20px; border: 1px solid var(--border); background: var(--surface); color: var(--text-secondary); font-size: 0.75rem; font-weight: 600; cursor: pointer; font-family: inherit; display: inline-flex; align-items: center; gap: 6px; transition: var(--transition); position: relative; overflow: hidden; }
    .media-btn:active { border-color: var(--text-tertiary); color: var(--text); }

    .recorder { display: none; align-items: center; gap: 12px; padding: 12px; background: var(--bg); border-radius: var(--radius-md); border: 1px solid var(--border); margin-top: 10px; }
    .recorder.active { display: flex; }
    .rec-dot { width: 10px; height: 10px; border-radius: 50%; background: var(--danger); animation: pulse 1.5s infinite; flex-shrink: 0; }
    @keyframes pulse { 0%,100%{opacity:1;transform:scale(1)} 50%{opacity:0.5;transform:scale(1.2)} }
    .rec-timer { font-variant-numeric: tabular-nums; font-weight: 600; font-size: 0.875rem; min-width: 44px; }
    .rec-wave { display: flex; align-items: center; gap: 2px; flex: 1; height: 20px; }
    .rec-bar { width: 2px; background: var(--text-tertiary); border-radius: 1px; animation: wave 1s infinite ease-in-out alternate; }
    .rec-bar:nth-child(2){height:40%;animation-delay:0.1s} .rec-bar:nth-child(3){height:70%;animation-delay:0.2s} .rec-bar:nth-child(4){height:50%;animation-delay:0.15s} .rec-bar:nth-child(5){height:80%;animation-delay:0.05s} .rec-bar:nth-child(6){height:35%;animation-delay:0.25s}
    @keyframes wave { from{height:20%} to{height:100%} }
    .rec-stop { padding: 6px 14px; border-radius: 20px; border: none; background: var(--danger); color: white; font-size: 0.75rem; font-weight: 700; cursor: pointer; font-family: inherit; }

    .lightbox-media { max-width: 100%; max-height: 70vh; border-radius: var(--radius-lg); display: block; margin: 0 auto; }

    .search-input-wrap { padding: 16px 24px; border-bottom: 1px solid var(--border); flex-shrink: 0; }
    .search-input-wrap input { width: 100%; background: transparent; border: none; font-size: 1.125rem; color: var(--text); font-family: inherit; outline: none; font-weight: 500; }
    .search-input-wrap input::placeholder { color: var(--text-tertiary); }
    .search-results { overflow-y: auto; -webkit-overflow-scrolling: touch; flex: 1; padding: 8px 0; }
    .search-section { padding: 8px 24px; font-size: 0.6875rem; font-weight: 700; color: var(--text-tertiary); text-transform: uppercase; letter-spacing: 0.12em; }
    .search-item { padding: 12px 24px; cursor: pointer; display: flex; align-items: center; gap: 12px; min-height: 52px; transition: background var(--transition); border-radius: 0; }
    .search-item:active { background: var(--bg); }
    .search-icon-wrap { width: 36px; height: 36px; border-radius: 50%; background: var(--bg); display: flex; align-items: center; justify-content: center; color: var(--text-secondary); flex-shrink: 0; }
    .search-body { flex: 1; min-width: 0; }
    .search-title { font-weight: 600; font-size: 0.875rem; margin-bottom: 1px; }
    .search-meta { font-size: 0.75rem; color: var(--text-tertiary); }

    .tag-row { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: center; gap: 8px; padding: 12px 0; border-bottom: 1px solid var(--border); }
    .tag-info { display: flex; align-items: center; gap: 10px; }
    .tag-dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; }
    .tag-name { font-size: 0.875rem; font-weight: 500; }

    /* Copy-to-day(s) checklist (see openCopyModal/renderCopyDayList) —
       14-16px vertical rhythm between rows via padding, not margin, so
       adjacent border-bottoms don't create a doubled line. */
    .copy-day-toolbar { display: flex; gap: 8px; margin-bottom: 12px; }
    .copy-day-row { display: flex; align-items: center; gap: 10px; padding: 8px 2px; border-bottom: 1px solid var(--border); cursor: pointer; }
    .copy-day-row:last-child { border-bottom: none; }
    .copy-day-label { font-size: 0.875rem; font-weight: 500; color: var(--text); }
    .copy-day-label em { font-style: normal; color: var(--text-tertiary); font-weight: 500; }
    /* Small-caps week-segment header, shown between day rows for longer
       trips (see renderCopyDayList) — a label, not a checkbox row. */
    .copy-week-header { font-size: 0.6875rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em; color: var(--text-tertiary); padding: 14px 2px 4px; }
    .copy-week-header:first-child { padding-top: 2px; }

    /* Template editor */
    .tpl-edit-cat { margin-bottom: 10px; }

    /* Template apply preview */
    .tpl-preview-cat { margin-bottom: 18px; }
    .tpl-preview-cat:last-child { margin-bottom: 0; }
    .tpl-preview-cat-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-bottom: 8px; }
    .tpl-preview-cat-head > span:first-child { font-size: 0.875rem; font-weight: 600; }
    .tpl-preview-cat-badge { font-size: 0.6875rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em; padding: 2px 8px; border-radius: 12px; white-space: nowrap; flex-shrink: 0; }
    .tpl-preview-cat-badge.new { color: var(--text-secondary); background: var(--badge-bg); }
    .tpl-preview-cat-badge.existing { color: var(--accent); background: var(--accent-bg); }
    .tpl-preview-items { display: flex; flex-direction: column; }
    .tpl-preview-item { display: flex; align-items: center; gap: 8px; padding: 6px 0; font-size: 0.8125rem; color: var(--text); border-bottom: 1px solid var(--border); }
    .tpl-preview-item:last-child { border-bottom: none; }
    .tpl-preview-item svg { width: 14px; height: 14px; flex-shrink: 0; }
    .tpl-preview-item.new svg { color: var(--accent); }
    .tpl-preview-item.have { color: var(--text-tertiary); }
    .tpl-preview-item.have svg { color: var(--text-tertiary); }
    .tpl-preview-item em { margin-left: auto; font-style: normal; font-size: 0.6875rem; color: var(--text-tertiary); flex-shrink: 0; }

    /* Snackbar position: bottom, in thumb reach, above the floating dock —
       these carry the Undo actions, so they live where the thumb already is.
       Desktop parks them bottom-right, above the FAB's corner. */
    .toast-container { position: fixed; left: 16px; right: 16px; bottom: calc(84px + env(safe-area-inset-bottom)); z-index: 2000; display: flex; flex-direction: column; align-items: center; gap: 8px; pointer-events: none; }
    @media (min-width: 1024px) { .toast-container { left: auto; right: 24px; bottom: 96px; align-items: flex-end; } }
    .toast { padding: 12px 18px; border-radius: var(--radius-lg); background: var(--surface); border: 1px solid var(--border); box-shadow: var(--shadow-lg); font-size: 0.8125rem; font-weight: 600; animation: toastBounceIn 380ms var(--ease-settle) forwards; pointer-events: auto; display: flex; align-items: center; gap: 8px; cursor: pointer; }
    .toast.ok { color: var(--text); }
    .toast.err { color: var(--danger); }
    .toast-action { margin-left: 4px; padding: 4px 10px; border-radius: 20px; border: none; background: var(--accent); color: #fff; font-size: 0.6875rem; font-weight: 700; cursor: pointer; font-family: inherit; text-transform: uppercase; letter-spacing: 0.04em; }
    /* Rise-and-settle entrance. The keyframes are unchanged, but with the
       overshoot removed from --ease-settle the toast now decelerates into
       place instead of springing past and bouncing back — the calmer arrival
       One UI uses. Name kept as-is to avoid churning the JS that references it. */
    @keyframes toastBounceIn { from { transform: translateY(14px) scale(0.94); opacity: 0; } to { transform: translateY(0) scale(1); opacity: 1; } }
    @media (prefers-reduced-motion: reduce) { .toast { animation: none; } }



    /* Top-level view cross-fade (see showView, app-core.js) — trip tabs
       and the home/trip switch. Small enough motion (6px, 220ms) to read
       as a hand-off between views rather than a distinct "slide"
       animation competing with the modal sheet's own transform. */
    .view-fade-in { animation: viewFadeIn 220ms cubic-bezier(0.4,0,0.2,1); }
    @keyframes viewFadeIn { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: translateY(0); } }

    /* Directional slide on trip-tab changes (§5 macro). Direction comes from
       TRIP_TABS order in app-core.js, so moving right along the dock brings the
       new view in from the right — matching the spatial model the swipe gesture
       already implies. The neutral vertical rise above is kept for home/trip
       switches and first entry, which aren't lateral moves.

       14px, not more: the view is full-width, so a large offset reads as the
       whole app lurching. Transform + opacity only, so it stays on the
       compositor. */
    .view-slide-next { animation: viewSlideNext 240ms cubic-bezier(0.4,0,0.2,1); }
    .view-slide-prev { animation: viewSlidePrev 240ms cubic-bezier(0.4,0,0.2,1); }
    @keyframes viewSlideNext { from { opacity: 0; transform: translateX(14px); } to { opacity: 1; transform: translateX(0); } }
    @keyframes viewSlidePrev { from { opacity: 0; transform: translateX(-14px); } to { opacity: 1; transform: translateX(0); } }
    /* RTL mirrors horizontal direction (§5 edge cases) — "next" is leftward
       when the writing direction is. */
    [dir="rtl"] .view-slide-next { animation-name: viewSlidePrev; }
    [dir="rtl"] .view-slide-prev { animation-name: viewSlideNext; }

    @media (prefers-reduced-motion: reduce) {
      .view-fade-in, .view-slide-next, .view-slide-prev { animation: none; }
    }

    /* Wave entrance for list/card rows — see waveIn() in app-core.js for
       the stagger math and the _animateListEntrance gate that limits this
       to fresh tab switches. "both" fill-mode holds the from-state during
       the animation-delay, so later rows don't flash visible before their
       turn. */
    .wave-in { animation: waveIn 560ms cubic-bezier(0.22,1,0.36,1) both; }
    @keyframes waveIn { from { opacity: 0; transform: translateY(14px); } to { opacity: 1; transform: translateY(0); } }
    @media (prefers-reduced-motion: reduce) { .wave-in { animation: none; } }

    /* Screen title. One UI anchors each screen with a large, bold, left-aligned
       title and then drops to a much calmer scale for everything beneath it —
       that size contrast is the point, and 22px/600 was far too close to the
       body scale to create it. 30px sits at the bottom of the 30–34 range so
       it still fits a 360px-wide screen without wrapping "My Trips" or a long
       localised equivalent. Extra bottom margin gives the generous section air
       One UI uses rather than packing content tight under the heading. */
    .home-title { font-size: 1.875rem; font-weight: 700; letter-spacing: -0.02em; line-height: 1.25; margin-bottom: 24px; }
    @media (max-width: 360px) { .home-title { font-size: 1.6875rem; } }
    .trip-card { cursor: pointer; position: relative; overflow: hidden; transform-origin: center; transition: transform 150ms var(--ease-settle); }
    @media (prefers-reduced-motion: reduce) { .trip-card { transition: none; } }
    .trip-card-header { display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 12px; }
    .trip-card-name { font-size: 1rem; font-weight: 600; letter-spacing: -0.01em; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .trip-card-meta { font-size: 0.75rem; color: var(--text-tertiary); font-weight: 500; margin-top: 4px; }
    .trip-card-pill { font-size: 0.8125rem; font-weight: 700; color: var(--text-secondary); background: var(--bg); padding: 6px 12px; border-radius: 20px; border: 1px solid var(--border); flex-shrink: 0; }
    .trip-card-location { font-size: 0.75rem; color: var(--text-secondary); font-weight: 500; margin-top: 2px; display: flex; align-items: center; gap: 4px; }
    .trip-card-dates { font-size: 0.6875rem; color: var(--text-tertiary); font-weight: 500; margin-top: 2px; }
    .trip-card-next { font-size: 0.6875rem; color: var(--accent); font-weight: 600; margin-top: 6px; }

    /* Overrides .card's own overflow:hidden (needed there for other card
       types, e.g. clipping rounded corners around full-bleed content —
       not needed here, nothing inside a day card bleeds past its own
       radius). Without this, .day-menu-dropdown (position:absolute,
       anchored to the "⋮" button near the card's top) gets clipped by
       the card's own bottom edge whenever the card isn't tall enough
       below that anchor to fit the whole dropdown — which has nothing to
       do with the dropdown's own CSS, only with how tall the card
       happens to be. A day with no activities (or any collapsed day,
       populated or not — .day-collapsible contributes zero height while
       hidden) is short enough to hit this; a long enough activities list
       pushes the card past the dropdown's height and the same bug stops
       being visible, without ever actually being fixed. Scoped to
       .itinerary-day specifically, not .card generally, to keep this
       out of every other card type's rendering. */
    /* ══ Day scrubber ═══════════════════════════════════════════════════
       Sticky under the app header for the whole Plan tab; bleeds over the
       scroller's padding so cards cannot peek out beside it while pinned.
       Fixed 74px tall (renderDayScrubber sets --scrubber-h to match) so the
       day headers' own sticky offset can sit just below it. */
    .day-scrubber {
      /* Its own bleed margin (-scroll-pad) already eats the padding part of
         --sticky-top, so only that much pins it flush - measured, like the
         token itself. */
      position: sticky; top: calc(-1 * var(--scroll-pad)); z-index: 6;
      box-sizing: border-box; height: 82px;
      margin: calc(-1 * var(--scroll-pad)) calc(-1 * var(--scroll-pad)) 12px;
      padding: 8px var(--scroll-pad);
      background: var(--bg);
      display: flex; gap: 6px; overflow-x: auto; overflow-y: hidden;
      scrollbar-width: none; -webkit-overflow-scrolling: touch; scroll-snap-type: x proximity;
    }
    .day-scrubber::-webkit-scrollbar { display: none; }
    /* display:flex above would beat the UA [hidden] rule (same specificity). */
    .day-scrubber[hidden] { display: none; }
    .day-chip {
      flex-shrink: 0; width: 52px; height: 66px; box-sizing: border-box; border-radius: 14px; scroll-snap-align: start;
      border: 1px solid var(--border); background: var(--surface); color: var(--text-secondary);
      display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 1px;
      font: inherit; cursor: pointer; padding: 0 4px; transition: background var(--transition), border-color var(--transition), color var(--transition);
    }
    .day-chip-wd { font-size: 0.6875rem; font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase; }
    .day-chip-num { font-size: 1rem; font-weight: 700; line-height: 1.1; color: var(--text); font-variant-numeric: tabular-nums; }
    .day-chip-city { max-width: 100%; font-size: 0.6875rem; font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
    /* The day's shape: a 06:00-24:00 occupancy bar in the type colours. */
    .day-shape { position: relative; display: block; height: 4px; border-radius: 2px; background: color-mix(in srgb, var(--text) 8%, transparent); overflow: hidden; }
    .day-shape.chip { width: 40px; margin-top: 3px; }
    .day-shape.wide { width: min(200px, 60%); flex-shrink: 1; }
    .day-shape i { position: absolute; top: 0; bottom: 0; background: var(--accent); }
    .day-shape i[data-type="food"] { background: var(--type-food); }
    .day-shape i[data-type="transit"] { background: var(--type-transit); }
    .day-shape i[data-type="outdoors"] { background: var(--type-outdoors); }
    .day-shape i[data-type="nightlife"] { background: var(--type-nightlife); }
    .day-shape-row { margin-top: 8px; display: flex; align-items: center; gap: 8px; }
    .day-shape-meta { font-size: 0.6875rem; color: var(--text-tertiary); white-space: nowrap; font-variant-numeric: tabular-nums; }
    /* Only while collapsed: expanded, the ruler itself is the shape. */
    .day-header[aria-expanded="true"] .day-shape-row { display: none; }
    .day-chip.drop { border: 2px dashed var(--accent); background: color-mix(in srgb, var(--accent) 10%, var(--surface)); color: var(--accent); transform: scale(1.06); }
    .day-chip.today { background: var(--accent-bg); border-color: var(--accent-light); color: var(--accent); }
    .day-chip.today .day-chip-num { color: var(--accent); }
    .day-chip.on { border-color: var(--accent); box-shadow: inset 0 0 0 1px var(--accent); }
    .day-chip:focus-visible { outline: none; box-shadow: var(--focus-ring); }
    @media (hover: hover) and (pointer: fine) { .day-chip:hover { background: var(--surface-hover); } }
    .itinerary-day { margin-bottom: 8px; overflow: visible; }
    .day-header { display: flex; align-items: flex-start; margin-bottom: 10px; gap: 8px; cursor: pointer; border-radius: var(--radius-sm); transform-origin: center; transition: background var(--transition), transform 150ms var(--ease-settle); position: relative; }
    .day-header:hover { background: var(--bg); }
    @media (prefers-reduced-motion: reduce) { .day-header { transition: background var(--transition); } }
    .day-chevron { flex-shrink: 0; display: flex; color: var(--text-tertiary); transition: transform 150ms; margin-top: 3px; }
    .day-title { font-size: 0.9375rem; font-weight: 600; letter-spacing: -0.01em; line-height: 1.3; color: var(--day-title-color); }
    /* Fixed two-line header: title, then a plain DD/MM/YYYY date — no
       activity/commute tallies or other metadata in this block. Weight
       bumped from 500 to 700 alongside the color change (was
       --text-tertiary, easy to skim past) — same size/spacing, so the
       date reads as more prominent without shifting the header's layout. */
    .day-date { font-size: 0.6875rem; color: var(--day-date-color); font-weight: var(--day-date-weight); margin-top: 2px; }
    /* The day's base, directly under the date. Reuses locLink()'s
       .activity-location markup so it matches every other location in the
       app (pin icon, maps preview on tap) and cannot drift from them; the
       wrapper only handles the line break and spacing. locLink already
       stops propagation, so tapping the base opens maps instead of
       collapsing the day. */
    .day-base { margin-top: 3px; }
    /* Default-timezone indicator (see openDayTimezoneModal/saveDayTimezone)
       — only rendered when a day has one set, inline next to the date. */
    .day-tz-pill { color: var(--text-tertiary); font-size: 0.8125rem; font-weight: 500; margin-left: 6px; }

    /* Smooth collapse/expand for a day's activity list (see
       toggleDayCollapse). Tried the grid-template-rows 0fr/1fr trick
       first (no JS measurement needed) but it measurably didn't collapse
       in testing — the track kept sizing to content regardless of the
       0fr/min-height:0 combination — so this uses the older, more
       reliably-supported max-height technique instead: animate toward a
       fixed cap comfortably above any realistic day's content instead of
       toward an actual measured height. Trade-off: a very short day's
       expand looks slightly front-loaded (the eased curve is timed
       against the full 0→4000px range, so it visually "arrives" before
       260ms elapses) — accepted as the simpler, no-JS option, and not
       noticeable in practice for this app's typical card heights. */
    .day-collapsible-wrap { max-height: 0; overflow: hidden; transition: max-height 260ms cubic-bezier(0.4,0,0.2,1); }
    .day-collapsible-wrap.expanded { max-height: 4000px; }
    @media (prefers-reduced-motion: reduce) { .day-collapsible-wrap { transition: none; } }

    /* Day-level outfit note/photo (see openDayModal/renderItinerary's
       outfitHtml) — sits between .day-header and .day-collapsible, so it
       stays visible even when the day is collapsed (a quick-glance thing,
       not itinerary detail). Only rendered at all when the day actually
       has a note or photo, matching .day-archived-section's "costs
       nothing when empty" rule below. */
    .day-outfit-row { display: flex; align-items: center; gap: 8px; margin: -2px 0 10px; padding: 6px 8px; border-radius: var(--radius-sm); background: var(--bg); cursor: pointer; }
    .day-outfit-row:hover { background: var(--surface-hover); }
    .day-outfit-thumb { width: 32px; height: 32px; border-radius: var(--radius-sm); object-fit: cover; flex-shrink: 0; border: 1px solid var(--border); }
    .day-outfit-note { font-size: 0.75rem; color: var(--text-secondary); font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

    /* Per-day archived section (see toggleDayArchivedSection/
       renderArchivedRow, app-plan.js) — sits below the active
       activities-list inside the same .day-collapsible, collapsed by
       default. Only rendered at all when the day actually has archived
       items, so it costs nothing on the (common) case of none. */
    .day-archived-section { margin-top: 10px; border-top: 1px dashed var(--border); padding-top: 8px; }
    .day-archived-toggle { display: flex; align-items: center; gap: 6px; width: 100%; padding: 6px 2px; background: none; border: none; font-family: inherit; font-size: 0.75rem; font-weight: 600; color: var(--text-tertiary); text-align: left; cursor: pointer; }
    .day-archived-toggle .day-chevron { margin-left: auto; }
    .day-archived-list { padding-top: 2px; }
    /* Shared by the per-day section above and the global Archived modal
       (#archivedItemsModal) — reuses .item/.it-text (the same generic
       icon+label+actions row packing items and gift rows already use)
       rather than a new row layout, with just a couple of small
       additions (icon color, the meta subline) layered on top. */
    .archived-row-icon { flex-shrink: 0; color: var(--text-tertiary); display: flex; }
    .archived-row-meta { font-size: 0.6875rem; color: var(--text-tertiary); margin-top: 1px; }
    .archived-day-group { margin-bottom: 18px; }
    .archived-day-group:last-child { margin-bottom: 0; }
    .archived-day-group-label { font-size: 0.6875rem; font-weight: 700; color: var(--text-secondary); text-transform: uppercase; letter-spacing: 0.06em; margin-bottom: 4px; }

    /* Day card's Duplicate/Edit/Delete overflow menu — collapsed behind
       one kebab button instead of three separate icon buttons (see
       toggleDayMenu/closeDayMenu in app-plan.js). */
    .day-menu { position: relative; }
    .day-menu-dropdown { position: absolute; top: calc(100% + 4px); right: 0; min-width: 150px; background: var(--surface); border: 1px solid var(--border-strong); border-radius: var(--radius-md); box-shadow: var(--shadow-lg); padding: 4px; z-index: 50; }
    .day-menu-item { display: flex; align-items: center; gap: 8px; width: 100%; padding: 8px 10px; border-radius: var(--radius-sm); background: none; border: none; font-family: inherit; font-size: 0.8125rem; font-weight: 500; color: var(--text); text-align: left; cursor: pointer; transition: var(--transition); }
    .day-menu-item:hover, .day-menu-item:active { background: var(--surface-hover); }
    .day-menu-item.danger { color: var(--danger); }
    .day-menu-item.danger:hover, .day-menu-item.danger:active { background: var(--danger-bg); }
    /* Set Default Timezone, disabled while a day has mixed timezones —
       see dayHasMixedTimezones. The title attribute already carries the
       explanation as a native tooltip. */
    .day-menu-item:disabled { color: var(--text-tertiary); cursor: not-allowed; }
    .day-menu-item:disabled:hover, .day-menu-item:disabled:active { background: none; }

    /* Activity/Commute card's own Move/Copy/Edit/Delete overflow menu —
       same pattern and visual language as .day-menu above (kept as a
       separate class, not reused, so its own open/close state — see
       toggleCardMenu/closeCardMenu in app-plan.js — can't collide with
       the day header's menu). Right-aligned under the kebab button, same
       as .day-menu-dropdown. */
    .card-menu { position: relative; }
    .card-menu-dropdown { position: absolute; top: calc(100% + 4px); right: 0; min-width: 150px; background: var(--surface); border: 1px solid var(--border-strong); border-radius: var(--radius-md); box-shadow: var(--shadow-lg); padding: 4px; z-index: 50; }
    .card-menu-item { display: flex; align-items: center; gap: 8px; width: 100%; padding: 8px 10px; border-radius: var(--radius-sm); background: none; border: none; font-family: inherit; font-size: 0.8125rem; font-weight: 500; color: var(--text); text-align: left; cursor: pointer; transition: var(--transition); }
    .card-menu-item:hover, .card-menu-item:active { background: var(--surface-hover); }
    /* Gift row overflow menu — reuses .card-menu wholesale; only the current
       priority needs a marker, since the three priorities are a radio group
       rather than a list of commands. */
    .card-menu-item.is-current { background: var(--accent-bg); color: var(--accent); font-weight: 700; }
    .gift-menu .card-menu-dropdown { min-width: 170px; }
    .card-menu-item.danger { color: var(--danger); }
    .card-menu-item.danger:hover, .card-menu-item.danger:active { background: var(--danger-bg); }

    /* user-select:none is a TOUCH guard - it stops a long-press turning into a
       text selection mid drag-to-reorder. Under a mouse there is no such
       conflict and blocking selection just means you cannot copy an address
       off a card, so the guard is scoped to coarse pointers below. */
    .activity { display: flex; align-items: flex-start; gap: 8px; cursor: pointer; min-height: 44px; touch-action: pan-y; position: relative; }
    @media (pointer: coarse) {
      .activity { user-select: none; -webkit-user-select: none; }
    }
    .activity.done { opacity: 0.6; }
    .activity.done .activity-title { text-decoration: line-through; color: var(--text-tertiary); }
    /* One universal color for start/end time on both Activity and Commute
       cards — was --text-secondary here with a --commute-row .activity-time
       override to --accent, so the two card types showed different colors
       for the same kind of element. Unified to --accent (Commute's former
       color) with no component-specific override. */
    .activity-time { font-size: 0.75rem; font-weight: 700; color: var(--accent); font-variant-numeric: tabular-nums; line-height: 1.2; }
    /* The struck (planned) half of a planned-vs-actual pair, when they
       differ — de-emphasized so the actual value that follows it reads as
       the current, relevant one. Only ever appears via
       plannedVsActualTimeHtml/plannedVsActualDurationHtml, which already
       omit it entirely when planned equals actual (no strikethrough in
       that case, per requirement). */
    .struck { text-decoration: line-through; color: var(--text-tertiary); font-weight: 500; opacity: 0.8; }
    /* Between a struck planned value and its actual replacement — without
       it the two sat one space apart and read as two unrelated numbers
       rather than "this became that". */
    .struck-arrow { color: var(--text-tertiary); font-size: 0.85em; margin: 0 3px; }
    /* Persistent marker for a point punched while an earlier one in the
       same row is still unpunched — the warning toast shown at punch time
       auto-dismisses, but this stays until the sequence is fixed. */
    .out-of-order-flag { display: inline-flex; color: var(--danger); flex-shrink: 0; }
    .out-of-order-flag svg { width: 13px; height: 13px; }
    /* Both Activity (2 points: start/end) and Commute (N points, one per
       step) now share the same dot-timeline layout — Activity no longer
       has its own separate timing row. */
    .activity-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 6px; }
    .activity-main { display: flex; align-items: center; justify-content: space-between; gap: 8px; min-width: 0; }
    .activity-title { font-size: 0.875rem; font-weight: 500; word-break: break-word; overflow-wrap: break-word; line-height: 1.35; display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
    .activity-desc { font-size: 0.75rem; color: var(--text-secondary); line-height: 1.4; word-break: break-word; overflow-wrap: break-word; white-space: pre-wrap; }
    /* Matches .activity-time's 12px exactly — time and location now read
       as one stacked block (see .commute-route-line below), so they need
       to carry equal visual weight rather than location reading as a
       smaller caption underneath. */
    .activity-location { font-size: 0.75rem; color: var(--text-secondary); display: inline-flex; align-items: center; gap: 4px; font-weight: 500; cursor: pointer; text-decoration: underline; text-decoration-color: transparent; transition: var(--transition); max-width: 100%; vertical-align: top; }
    .activity-location:hover { text-decoration-color: var(--text-secondary); }
    .activity-location svg { flex-shrink: 0; }
    /* Location text must always stay on one line — truncates with an
       ellipsis instead of wrapping to a second line for long place names.
       min-width:0 lets this flex item shrink below its own content width
       (the default min-width:auto would otherwise keep it — and the card
       around it — as wide as the untruncated text). */
    .activity-location-text { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }

    .trip-header-sub { font-size: 0.75rem; color: var(--text-tertiary); font-weight: 500; margin-top: 2px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

    /* Shared by both activity and commute rows — a small date caption used
       whenever an endpoint's date differs from the day it's filed under. */
    .date-badge { font-size: 0.6875rem; font-weight: 700; color: var(--text-tertiary); text-transform: uppercase; letter-spacing: 0.02em; flex-shrink: 0; }

    /* Dot timeline shared by Activity (2 points: start/end) and Commute (N
       points, one per step). Grid rows use 2*i+1 per point, so the odd
       rows in between (2*i) are free for a connector-label row — this is
       what lets "duration + mode between two dots" be an actual line with
       room for pills, not just a bare connector.
       Dots are TOP-aligned at a fixed offset (matching the first line's
       height) rather than centered in the row's full — possibly multi-
       line — height, so a dot's position never depends on how much its
       own content wraps. That fixed offset is what lets the connector use
       fixed-pixel margins to reach exactly from one dot to the next
       without overlapping either circle, regardless of variable row
       heights. Each dot doubles as the punch-checkbox for that point (see
       punchActivityEndpoint/punchCommuteStep) — sized a bit larger than a
       purely decorative marker would need, since it's now a real tap
       target. */
    .commute-route { display: grid; grid-template-columns: auto 1fr; column-gap: 8px; row-gap: 10px; align-items: start; }
    /* Stacked, not side-by-side: time+timezone on top, location directly
       below at a tight (xs) gap so the two read as one combined block —
       the line break itself is the separator, no "—" needed between
       them. */
    .commute-route-line { grid-column: 2; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
    .route-line-time { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
    .route-line-location { min-width: 0; }
    /* align-self:center is explicit here rather than relying on its track
       happening to be exactly content-sized under .commute-route's own
       align-items:start (which is what the two neighboring
       .commute-route-line rows need, for their own fixed-offset dot
       math) — this row should stay centered on purpose even if a future
       change ever makes its track taller than the pills themselves. */
    .commute-route-connector-label { grid-column: 2; align-self: center; min-width: 0; display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
    .commute-route-dot { grid-column: 1; justify-self: center; align-self: start; position: relative; z-index: 2; margin-top: 3px; width: 10px; height: 10px; border-radius: 50%; background: var(--accent); color: #FFFFFF; cursor: pointer; transition: var(--transition); display: flex; align-items: center; justify-content: center; }
    /* The visible dot stays small (matches the timeline's proportions),
       but its real tap target is much bigger — an invisible expanded hit
       area via ::before rather than padding, so it doesn't disturb the
       grid-row math the dot/connector alignment depends on. ~34px square,
       still short of the 44px ideal, but a large jump from 10px without
       reaching into neighboring rows' hit areas. */
    .commute-route-dot::before { content: ''; position: absolute; inset: -12px; }
    .commute-route-dot svg { width: 7px; height: 7px; display: block; }
    .commute-route-dot:hover { transform: scale(1.15); }
    .commute-route-dot:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
    .commute-route-dot.end { background: #FFFFFF; border: 1.5px solid var(--accent); }
    /* Punched (actual time logged) always wins over the start/end fill —
       a distinct success color so a logged point reads at a glance,
       regardless of whether it's a start, end, or mid-route step dot.
       dotPop is a one-shot bold spring — plays on the CSS transition from
       "no .punched" to ".punched" (a genuinely new animation instance per
       browser rules, no JS re-trigger needed), giving the app's most
       frequent interaction — checking something off — real punch. */
    .commute-route-dot.punched { background: var(--success); border-color: var(--success); animation: dotPop 420ms var(--ease-settle); }
    @keyframes dotPop { 0% { transform: scale(1); } 45% { transform: scale(1.28); } 100% { transform: scale(1); } }
    @media (prefers-reduced-motion: reduce) { .commute-route-dot.punched { animation: none; } }
    /* JS places this spanning from the previous dot's row through the
       connector-label row (grid-row: connectorRow-1 / row), stopping
       exactly at the external row-gap before this row's own track — same
       principle regardless of how many internal tracks/gaps it spans,
       since CSS grid folds internal gutters into a multi-track item's box
       automatically. margin-top (16px = 3px dot offset + 10px dot
       diameter + 3px breathing room) clears the previous dot before the
       line starts. margin-bottom is negative so the box grows past its
       own end, through the external 10px row-gap and 1px into the next
       row, stopping 3px short of that row's dot (same fixed 3px offset)
       — 10 + 1 = 11px. */
    .commute-route-connector { grid-column: 1; justify-self: center; align-self: stretch; position: relative; z-index: 1; width: 1.5px; margin-top: 16px; margin-bottom: -11px; background: var(--border-strong); }
    /* Only the two-stop-route case (both endpoints have a location) gets
       this — the plain connector between e.g. two mid-route stops with no
       location set stays exactly as before, inert. Widens the hit area
       sideways only (the line's own top/bottom already span exactly the
       right rows) rather than the dot's uniform inset, since the line is
       tall and only 1.5px wide — same ::before-overlay technique as
       .commute-route-dot, just shaped for a line instead of a point. */
    .commute-route-connector.clickable { cursor: pointer; }
    .commute-route-connector.clickable::before { content: ''; position: absolute; inset: 0 -8px; }
    .commute-route-connector.clickable:hover { background: var(--accent); }
    .commute-route-connector.clickable:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

    /* Activity card's own layout: title on top, then a strict three-row
       grid axis. Row 1 (above) holds each endpoint's PLANNED time and
       the PLANNED duration, struck through only where an actual value
       differs. Row 2 is the dot/connector-line axis itself. Row 3
       (below) holds ACTUAL time/duration cells — present only when an
       actual value exists and differs from planned; omitted entirely
       otherwise (see splitPlannedActualTimeHtml/
       splitPlannedActualDurationHtml). Every row-1/row-3 cell shares its
       grid COLUMN with the dot or line it belongs to (explicit grid-row/
       grid-column set in app-plan.js), so center-alignment to that dot
       is structural, not approximate. Then location/description/notes
       stacked below the whole axis. Deliberately not the same layout as
       .commute-route — Commute keeps its own vertical per-step layout. */
    .activity-axis-time { justify-self: center; display: flex; align-items: baseline; gap: 4px; min-width: 0; max-width: 100%; flex-wrap: wrap; justify-content: center; }
    /* A multi-day end (or start) time pairs a .date-badge with the
       time+timezone text in the same fixed 90px column — .activity-time's
       own nowrap/ellipsis rule below assumed just one short instant ever
       lands there, which the date-badge prefix breaks: there's often not
       room for both on one line, and ellipsis would silently cut off the
       timezone abbreviation rather than the (redundant, still-visible-as-
       the-badge) date. Scoped to .activity-axis-time specifically —
       Commute's own per-step time (.route-line-time .activity-time) never
       pairs with a date-badge this way and keeps its existing ellipsis
       fallback for its own, different overflow case (a struck-through
       actual-vs-planned arrow) untouched. flex-wrap above lets the badge
       and the time+tz text fall onto their own line instead of fighting
       for one; this lets the time+tz text itself wrap too, as a further
       fallback so nothing is ever lost to "…", just reflowed. */
    .activity-axis-time .activity-time { white-space: normal; overflow: visible; text-overflow: clip; word-break: break-word; text-align: center; }
    /* Same de-emphasis as .struck, plus a variant for the duration pill
       (dimmed rather than recolored, so its pill shape/background still
       reads as "duration" at a glance). Two-class selectors so this wins
       regardless of source order relative to .activity-time/.duration-badge. */
    .activity-time.struck { color: var(--text-tertiary); font-weight: 500; text-decoration: line-through; opacity: 0.8; }
    .duration-badge.struck { opacity: 0.55; text-decoration: line-through; }
    /* text-overflow:ellipsis, not wrapping, is the fallback when the
       widest realistic content (a struck-through actual time, e.g.
       "21:30 GMT+2 → 21:35 GMT+2") doesn't fit the fixed 90px column
       below — confirmed via measurement that without this, that text
       either overflowed the card's own border or, once clipped by an
       ancestor's overflow:hidden, disappeared with no visual trace.
       Truncating with "…" never wraps to a second line while still
       signaling there's more than what's showing. Doesn't engage for a
       single planned time, which always fits in 90px. */
    .activity-time { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; }
    /* The two end columns are a fixed, EQUAL width (not auto) — auto-
       sized columns tie each dot's position to that side's own time-
       text width, so a wider string on one end (e.g. a struck-through
       actual time) pushes that dot inward while the other stays put. A
       shared fixed width keeps both dots at an identical offset from
       the card edge regardless of what either side's time renders as.
       The line columns are minmax(16px,1fr) so they absorb all the
       leftover space, extending the lines edge-to-edge between the two
       fixed columns. margin-right pulls the whole grid back out to the
       card's true right padding edge: .activity-body shares its flex
       row with two 24px .del-btn icons (drag-grip + the Move/Copy/Edit/
       Delete kebab) — always exactly two, always that width — which
       permanently narrows .activity-body itself, even though those
       icons sit up by the title and never actually overlap the axis row
       below them. 64px is that reserved icon-cluster width measured
       directly (via getBoundingClientRect, confirming leftGap ===
       rightGap around the dots), not computed from the icon/gap sizes —
       re-measure and adjust this if the icon cluster's width ever
       changes (e.g. a third icon added back). */
    /* width:calc(100% + 64px), not margin-right:-64px — measured that a
       negative margin on a stretched grid item doesn't reliably feed its
       true rendered size back into the grid's own fr-track distribution
       (tracks stayed pinned to their minmax minimum, leaving ~15px of
       dead space and knocking the two dots out of symmetric alignment
       even though the box itself did visibly extend). An explicit width
       gives the grid a definite size up front, which the fr tracks
       measurably do resolve against correctly. */
    /* column-gap:0 — the line must visibly touch the dot with no gap.
       The duration pill's own breathing room from the lines comes from
       its margin (see .activity-axis-mid) instead of the row's gap. */
    /* minmax(4px,1fr), not 16px — with a wide duration pill (e.g.
       "1h 30m"/"5h 5m" together with both 90px end columns), the fixed
       content already claims more than the 64px-extended width leaves
       for the two lines at a 16px floor, which forced the grid to
       genuinely overflow its own box rather than shrink further —
       breaking dot symmetry as a side effect (confirmed via direct
       measurement: 90+16+16+content exceeded the container by ~1.2px,
       and the dots were correspondingly misaligned by several px). A
       lower floor gives the lines room to shrink first instead. */
    .activity-axis { display: grid; grid-template-columns: 90px minmax(4px, 1fr) auto minmax(4px, 1fr) 90px; align-items: center; column-gap: 0; row-gap: 4px; margin: 2px 0; width: calc(100% + 64px); }
    /* No 1fr columns in the single-dot case, so nothing fills the row on
       its own — center it explicitly instead, over the same reclaimed
       width as the two-dot case above (the calc(100% + 64px) width
       still applies, inherited from .activity-axis). */
    .activity-axis-single { grid-template-columns: 90px; justify-content: center; }
    /* align-self:start on the base .commute-route-dot rule is a
       vertical-grid artifact (keeps a Commute dot pinned to its own
       row's top edge in the Commute layout) that would otherwise also
       pin it to the top of this row's track — reset to center so it
       sits centered within row 2 alongside the connector lines/pill. */
    .activity-axis .commute-route-dot { margin-top: 0; justify-self: center; align-self: center; }
    .activity-axis-line { height: 1.5px; background: var(--border-strong); align-self: center; }
    /* One badge per cell now — planned duration (row 1) and actual
       duration (row 3) are separate grid cells sharing column 3, not a
       single cell with an internal divider — so this just needs to
       center whichever one badge it holds. margin gives it breathing
       room from the connector lines now that .activity-axis's own
       column-gap is 0 (needed so the lines touch the dots directly). */
    .activity-axis-mid { justify-self: center; display: flex; align-items: center; margin: 0 8px; }
    .activity-meta-stack { display: flex; flex-direction: column; gap: 2px; }

    /* Drag & drop activities/commutes between days — armed via the
       dedicated .drag-grip handle (see startDrag in app-plan.js), styled
       as a third icon alongside the move/delete .del-btn buttons rather
       than this .drag-handle rule, which the Packing tab still uses for
       its own item reordering (app-packing.js) — that rule stays as-is. */
    .drag-handle { display: flex; align-items: center; justify-content: center; width: 20px; min-height: 44px; margin: 0 -2px 0 -6px; color: var(--text-tertiary); cursor: grab; touch-action: none; flex-shrink: 0; }
    .drag-handle:active { cursor: grabbing; color: var(--text-secondary); }
    .dragging-source { opacity: 0.35; }
    .drag-ghost { position: fixed; z-index: 2000; pointer-events: none; margin: 0; background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-md); box-shadow: var(--shadow-lg); padding: 8px 12px; transform: scale(1.02); }
    .itinerary-day.drop-target { outline: 2px dashed var(--accent); outline-offset: 3px; background: var(--accent-bg); border-radius: var(--radius-lg); }
    .activity.drop-before { box-shadow: inset 0 2px 0 var(--accent); }
    .activity.drop-after { box-shadow: inset 0 -2px 0 var(--accent); }

    /* Budget */
    .budget-summary { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; margin-bottom: 16px; }
    @media (max-width: 480px) { .budget-summary { grid-template-columns: repeat(3, 1fr); gap: 6px; } }
    .budget-stat { text-align: center; padding: 12px 8px; background: var(--bg); border-radius: var(--radius-md); border: 1px solid var(--border); }
    .budget-stat-label { font-size: 0.6875rem; font-weight: 700; color: var(--text-secondary); text-transform: uppercase; letter-spacing: 0.08em; margin-bottom: 4px; }
    .budget-stat-value { font-size: 1.125rem; font-weight: 600; letter-spacing: -0.02em; }
    .budget-stat-value.spent { color: var(--danger); }
    .budget-stat-value.remain { color: var(--text); }
    .budget-stat-value.total { color: var(--text-secondary); }

    .budget-chart { margin-bottom: 16px; }
    .budget-chart-bar { display: flex; height: 10px; border-radius: 6px; overflow: hidden; background: var(--track-bg); }
    .budget-chart-seg { height: 100%; transition: var(--transition); }
    .budget-chart-legend { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 10px; }
    .budget-chart-legend-item { display: flex; align-items: center; gap: 6px; font-size: 0.6875rem; color: var(--text-secondary); font-weight: 500; }
    .budget-chart-dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; }

    .payer-rollup { margin-bottom: 16px; }
    .payer-rollup-label { font-size: 0.6875rem; font-weight: 700; color: var(--text-secondary); text-transform: uppercase; letter-spacing: 0.08em; margin-bottom: 8px; }
    .payer-rollup-list { display: flex; flex-wrap: wrap; gap: 8px; }
    .payer-chip { display: inline-flex; align-items: center; gap: 4px; font-size: 0.75rem; font-weight: 500; color: var(--text-secondary); background: var(--accent-bg); border: 1px solid var(--border); border-radius: 20px; padding: 5px 12px; }
    .payer-chip b { color: var(--text); font-weight: 700; }

    .budget-toolbar { display: flex; justify-content: space-between; align-items: center; gap: 8px; margin-bottom: 14px; flex-wrap: wrap; }
    .expense-group { margin-bottom: 12px; }
    .expense-group-header { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; padding: 0 4px; }
    .expense-group-label { font-size: 0.6875rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.12em; color: var(--text-secondary); padding: 2px 8px; border-radius: 20px; border: 1px solid var(--border); background: var(--surface); flex-shrink: 0; }
    .expense-group-total { font-size: 0.75rem; font-weight: 600; color: var(--text); margin-left: auto; }

    .expense { display: flex; align-items: center; gap: 10px; padding: 8px 0; border-bottom: 1px solid var(--border); cursor: pointer; min-height: 44px; }
    .expense:last-child { border-bottom: none; }
    .expense-amount { font-size: 0.875rem; font-weight: 600; color: var(--text); font-variant-numeric: tabular-nums; min-width: 64px; text-align: right; }
    .expense-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
    .expense-desc { font-size: 0.8125rem; font-weight: 500; color: var(--text); word-break: break-word; }
    .expense-meta { font-size: 0.6875rem; color: var(--text-tertiary); font-weight: 500; }
    .expense-original { font-size: 0.6875rem; color: var(--text-tertiary); font-weight: 500; }
    .expense-actions { display: flex; gap: 2px; flex-shrink: 0; }

    .budget-empty { text-align: center; padding: 32px 16px; color: var(--text-tertiary); }
    .budget-empty svg { width: 32px; height: 32px; stroke-width: 1; opacity: 0.3; margin-bottom: 8px; }

    .currency-select { background: var(--surface); border: 1px solid var(--border); color: var(--text); padding: 5px 24px 5px 8px; border-radius: 20px; font-size: 0.75rem; outline: none; cursor: pointer; appearance: none; background-image: url("data:image/svg+xml,%3Csvg width='10' height='6' viewBox='0 0 10 6' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M1 1L5 5L9 1' stroke='%23A0A0A0' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E"); background-repeat: no-repeat; background-position: right 8px center; font-family: inherit; font-weight: 600; }

    .fx-badge { font-size: 0.6875rem; font-weight: 700; color: var(--text-tertiary); background: var(--bg); padding: 1px 6px; border-radius: 10px; border: 1px solid var(--border); flex-shrink: 0; }
    .payment-badge { font-size: 0.6875rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em; color: var(--text-secondary); background: var(--bg); padding: 1px 6px; border-radius: 10px; border: 1px solid var(--border); flex-shrink: 0; }
    .fx-refresh { display: inline-flex; align-items: center; gap: 3px; font-size: 0.6875rem; color: var(--text-tertiary); cursor: pointer; background: none; border: none; padding: 0; font-family: inherit; }
    .fx-refresh:active { color: var(--text); }
    .fx-status { font-size: 0.6875rem; color: var(--text-tertiary); font-weight: 500; }

    .location-row { background: var(--bg); border: 1px solid var(--border); border-radius: var(--radius-md); padding: 14px; margin-bottom: 12px; }
    .location-row:last-child { margin-bottom: 0; }
    .location-row .field { margin-bottom: 12px; }
    .location-row .field:last-child { margin-bottom: 0; }
    .loc-dates-row { display: flex; gap: 12px; }
    .loc-dates-row .field { flex: 1; }
    @media (max-width: 480px) { .loc-dates-row { flex-direction: column; gap: 12px; } .location-row { padding: 12px; } }

    /* Add-Activity/Add-Commute row shown after every card (see the addRow
       helper in renderItinerary) — two equal buttons side by side, each
       styled the same as the old single "+ Commute" slot it replaces. */
    .add-item-row { display: flex; align-items: stretch; gap: 8px; padding: 4px 0; }
    .add-item-btn { flex: 1; display: flex; align-items: center; justify-content: center; gap: 6px; padding: 6px 0; border: none; background: none; font-family: inherit; color: var(--text-tertiary); font-size: 0.75rem; font-weight: 600; cursor: pointer; transition: var(--transition); position: relative; overflow: hidden; }
    .add-item-btn:hover { color: var(--text-secondary); }
    .add-item-btn svg { width: 14px; height: 14px; }
    /* One shared outer-box rule for both card types — border width/color,
       radius, padding, and margin come from a single declaration so they
       can't drift apart. Fill vs. no-fill is the only difference, layered
       on top below: Activity carries the solid tint, Commute stays
       unfilled with a dotted stroke instead (reads as "in transit"
       between two filled Activity stops, rather than a stop in its own
       right). */
    .activity, .commute-row { border: 1.5px solid var(--border-strong); border-radius: var(--radius-md); padding: 8px 10px; margin: 2px 0; }
    .activity { background: var(--bg); }
    /* A Commute row also carries the base .activity class (see
       renderCommute), so its background must be cancelled explicitly
       here — otherwise the .activity fill above would still apply. */
    .commute-row { background: none; border-style: dotted; }
    /* Mode and duration are the same size/shape but visually distinct
       categories (how vs. how long) — Color Schema 1's green/magenta,
       fixed across both themes like --danger already is, since they're
       vivid enough to read on either surface. */
    .commute-mode-badge { font-size:0.6875rem;font-weight:700;text-transform:uppercase;letter-spacing:0.06em;padding:2px 8px;border-radius:12px;background:rgba(38,133,0,0.12);color:var(--success);border:1px solid var(--border);white-space:nowrap; }
    /* Small linked-notes count shown on a card — Activity's sits inline in
       the title row; Commute's gets its own row below the dot timeline
       since there's no equivalent single title line to piggyback on. */
    /* Note slot on activity/commute cards — sits beside the kebab; colored
       when a note exists so "this card has a note" reads at a glance. */
    .card-menu { display: flex; align-items: flex-start; }
    .card-note-btn.linked { color: var(--accent); }
    /* Link-to-activity picker (noteLinkModal, built in openNoteLinkPicker) */
    .link-pick-day { font-size: 0.6875rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.4px; color: var(--text-tertiary); margin: 14px 2px 6px; }
    .link-pick-day:first-child { margin-top: 0; }
    .link-pick-row { display: flex; align-items: center; gap: 10px; padding: 10px; border: 1px solid var(--border); border-radius: var(--radius-md); margin-bottom: 6px; cursor: pointer; transition: var(--transition); }
    .link-pick-row:hover { background: var(--bg); }
    .link-pick-row.disabled { opacity: 0.5; cursor: default; }
    .link-pick-row svg { flex-shrink: 0; color: var(--text-tertiary); }
    .link-pick-main { min-width: 0; flex: 1; }
    .link-pick-title { font-size: 0.8125rem; font-weight: 600; overflow-wrap: break-word; }
    .link-pick-sub { font-size: 0.6875rem; color: var(--text-tertiary); }
    /* Brief pulse used to draw the eye to a card after jumpToActivity
       scrolls it into view from a note's backlink. */
    .activity.flash-highlight { animation: flashHighlight 1.6s ease-out; }
    @keyframes flashHighlight { 0%, 15% { background: var(--accent-bg); } 100% { background: transparent; } }
    .time-col { display:flex;flex-direction:column;align-items:center;gap:6px; }
    .time-col-label { font-size:0.6875rem;font-weight:700;color:var(--text-secondary);text-transform:uppercase;letter-spacing:0.08em; }
    .time-scroll { height:180px;overflow-y:auto;scroll-snap-type:y mandatory;-webkit-overflow-scrolling:touch;border-radius:var(--radius-md);background:var(--bg);border:1px solid var(--border);width:64px;scrollbar-width:none; }
    .time-scroll::-webkit-scrollbar { display:none; }
    .time-scroll-item { height:36px;display:flex;align-items:center;justify-content:center;font-size:1rem;font-weight:500;color:var(--text-secondary);scroll-snap-align:center;cursor:pointer;transition:var(--transition); }
    .time-scroll-item.on { color:var(--text);font-weight:700;font-size:1.125rem;background:var(--surface); }
    .time-scroll-item:active { background:var(--surface-hover); }
    .time-input-wrap { position:relative; }
    .time-input-wrap input { padding-right:28px; }
    .time-input-wrap .time-trigger { position:absolute;right:8px;top:50%;transform:translateY(-50%);width:20px;height:20px;display:flex;align-items:center;justify-content:center;color:var(--text-tertiary);pointer-events:none; }
    /* Total elapsed time, computed from real UTC instants (not clock-digit
       subtraction) so it's correct across a timezone/DST crossing. Color
       Schema 1 magenta — same size/shape as .commute-mode-badge (how long
       vs. how), distinct color so the two read as separate categories. */
    .duration-badge { font-size:0.6875rem;font-weight:700;color:#D515B9;background:rgba(213,21,185,0.12);padding:2px 8px;border-radius:12px;border:1px solid var(--border);white-space:nowrap;flex-shrink:0; }
    /* Ticket-purchased quick toggle — own row directly under the pills
       (see ticketRowHtml), not shoehorned into .duration-badge/
       .commute-mode-badge's own precisely-tuned grid cell. A SQUARE
       checkbox (vs. .commute-route-dot's circle) so it reads as a
       checkbox, not a third timeline point; same expanded-hit-area
       ::before trick as the dots for a comfortable tap target despite
       the small visible box. .ticket-badge reuses .duration-badge/
       .commute-mode-badge's exact size/shape/padding so all three read
       as one pill family — Color Schema 1 burnt orange-red, the one
       palette color not already claimed by those two, keeps "ticket"
       reading as its own category rather than a mode or a duration. */
    .ticket-row { display: flex; align-items: center; gap: 6px; margin-top: 4px; }
    .ticket-checkbox { position: relative; width: 14px; height: 14px; border-radius: 4px; border: 1.5px solid var(--border-strong); background: var(--surface); color: #FFFFFF; cursor: pointer; transition: var(--transition); display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
    .ticket-checkbox::before { content: ''; position: absolute; inset: -10px; }
    .ticket-checkbox svg { width: 9px; height: 9px; display: block; }
    .ticket-checkbox:hover { border-color: #DA3A16; }
    .ticket-checkbox:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
    .ticket-checkbox.checked { background: #DA3A16; border-color: #DA3A16; }
    .ticket-badge { font-size:0.6875rem;font-weight:700;color:#DA3A16;background:rgba(218,58,22,0.12);padding:2px 8px;border-radius:12px;border:1px solid var(--border);white-space:nowrap;flex-shrink:0;cursor:pointer;display:inline-flex;align-items:center;justify-content:center; }
    .ticket-badge svg { width: 11px; height: 11px; display: block; }
    .time-wrap { display:flex;align-items:center;gap:6px; }
    /* Trip Tools sheet */
    .tool-row { display: flex; align-items: center; gap: 12px; padding: 13px 4px; border-bottom: 1px solid var(--border); cursor: pointer; transition: var(--transition); position: relative; overflow: hidden; }
    .tool-row:last-child { border-bottom: none; }
    .tool-row:active { background: var(--surface-hover); }
    .tool-icon { display: flex; align-items: center; justify-content: center; color: var(--text-tertiary); flex-shrink: 0; }
    .tool-label { font-size: 0.875rem; font-weight: 500; }
    .tool-storage { padding: 4px; }

    /* Small on/off switch — used by the Auto-Adjust Later Items toggle.
       Real checkbox underneath (visually hidden, not display:none) so it
       stays keyboard/screen-reader operable; the track+knob is purely
       decorative on top of it. */
    .switch { position: relative; display: inline-flex; width: 38px; height: 22px; }
    .switch input { position: absolute; inset: 0; opacity: 0; margin: 0; cursor: pointer; z-index: 1; }
    .switch-track { position: absolute; inset: 0; background: var(--border-strong); border-radius: 999px; transition: var(--transition); pointer-events: none; }
    .switch-track::after { content: ''; position: absolute; top: 2px; left: 2px; width: 18px; height: 18px; background: var(--surface); border-radius: 50%; transition: var(--transition); box-shadow: var(--shadow); }
    .switch input:checked + .switch-track { background: var(--accent); }
    .switch input:checked + .switch-track::after { transform: translateX(16px); }
    .switch input:focus-visible + .switch-track { outline: 2px solid var(--accent); outline-offset: 2px; }
    .storage-line { font-size: 0.6875rem; color: var(--text-tertiary); padding: 10px 0; border-bottom: 1px solid var(--border); }
    .storage-line.warn { color: var(--danger); }
    .storage-line b { color: var(--text-secondary); font-weight: 700; }

    /* Trip status banner: "next up" + pre-departure packing reminder. */
    .status-banner { background: var(--accent-bg); border: 1px solid var(--border); border-radius: var(--radius-md); padding: 10px 12px; margin-bottom: 12px; display: flex; flex-direction: column; gap: 6px; font-size: 0.8125rem; line-height: 1.4; }
    .status-line { font-size: 0.78125rem; color: var(--text-secondary); font-weight: 500; display: flex; align-items: center; gap: 7px; }
    .status-line[role="button"] { cursor: pointer; }
    .status-line b { color: var(--text); font-weight: 700; }
    .status-dot { width: 6px; height: 6px; border-radius: 50%; flex-shrink: 0; background: var(--accent); }
    .status-dot.warn { background: var(--danger); }

    /* Packing: quantity stepper + traveler assignment + filter chips */
    .qty-stepper { display: flex; align-items: center; gap: 2px; flex-shrink: 0; }
    .qty-btn { width: 18px; height: 18px; border-radius: 6px; border: 1px solid var(--border); background: var(--surface); color: var(--text-secondary); font-size: 0.8125rem; line-height: 1; display: flex; align-items: center; justify-content: center; cursor: pointer; font-family: inherit; }
    .qty-btn:active { background: var(--bg); }
    .qty-btn:disabled { opacity: 0.35; cursor: default; }
    .qty-val { min-width: 16px; text-align: center; font-size: 0.75rem; font-weight: 600; color: var(--text-secondary); font-variant-numeric: tabular-nums; }
    .item-traveler { flex-shrink: 0; max-width: 62px; background: var(--bg); border: 1px solid var(--border); border-radius: 20px; color: var(--text-secondary); font-size: 0.6875rem; font-weight: 600; padding: 3px 5px; outline: none; font-family: inherit; }
    .filter-chip-row { display: flex; gap: 6px; overflow-x: auto; margin-bottom: 12px; -webkit-overflow-scrolling: touch; scrollbar-width: none; }
    .filter-chip-row::-webkit-scrollbar { display: none; }

    /* Packing: item priority (flag button + left-edge color accent) and
       drag-to-reorder (reuses .drag-handle/.dragging-source/.drag-ghost from
       the itinerary drag implementation; drop-target/drop-before/drop-after
       are generalized here to work on both .card and .item). */
    .pri-btn { width: 18px; height: 18px; min-width: 18px; flex-shrink: 0; border-radius: 5px; border: 1px solid var(--border); background: var(--surface); color: var(--text-tertiary); display: flex; align-items: center; justify-content: center; cursor: pointer; padding: 0; font-family: inherit; }
    .pri-btn:active { background: var(--bg); }
    .pri-btn-high { color: #fff; background: var(--priority-high); border-color: var(--priority-high); }
    .pri-btn-medium { color: #fff; background: var(--priority-medium); border-color: var(--priority-medium); }
    .pri-btn-low { color: #fff; background: var(--priority-low); border-color: var(--priority-low); }
    /* The inset box-shadow below draws its 3px colored line flush against
       .item's own left edge — .item itself has no horizontal padding, so
       without this the checkbox (the first flex child) starts at that same
       edge and sits right on top of the color bar instead of beside it.
       Scoped to just the pri-* variants so plain (no-priority) rows, which
       have no bar to clear, keep their original flush-left layout. */
    .item.pri-high, .item.pri-medium, .item.pri-low { padding-left: 8px; }
    .item.pri-high { box-shadow: inset 3px 0 0 var(--priority-high); }
    .item.pri-medium { box-shadow: inset 3px 0 0 var(--priority-medium); }
    .item.pri-low { box-shadow: inset 3px 0 0 var(--priority-low); }
    .card.drop-target { outline: 2px dashed var(--accent); outline-offset: 3px; background: var(--accent-bg); border-radius: var(--radius-lg); }
    .item.drop-before { box-shadow: inset 0 2px 0 var(--accent); }
    .item.drop-after { box-shadow: inset 0 -2px 0 var(--accent); }

    /* Inline "are you sure?" row swap (gift delete) — same .item wrapper
       as the normal row, so height/border/alignment don't shift, just the
       content. A plain fade (no directional rise/spring, unlike the
       toast's toastBounceIn) reads as an in-place content change rather than something
       new arriving — .gift-row-fade replays the same fade on the specific
       row that reverts back to normal (cancel/click-away) so neither
       direction is an abrupt swap. Respects prefers-reduced-motion via the
       existing global rule below. */
    .gift-delete-confirm, .gift-row-fade { animation: giftRowFade 150ms ease-out; }
    @keyframes giftRowFade { from { opacity: 0; } to { opacity: 1; } }
    /* Delete pinned to the row's top-right corner instead of sitting last
       in the inline icon cluster — .item is already position:relative, so
       the button anchors to the row itself; padding-right keeps the
       qty/priority/duplicate cluster from sliding under it. */
    /* ══ Gifts: groups, places, money ═══════════════════════════════════ */
    .gift-group { padding: 14px 16px; margin-bottom: 10px; }
    .gift-group-head { display: flex; align-items: center; gap: 10px; }
    .gift-group-head.place { align-items: baseline; }
    .gift-avatar { flex-shrink: 0; width: 34px; height: 34px; border-radius: 50%; background: var(--accent-bg); color: var(--accent); display: flex; align-items: center; justify-content: center; font-size: 0.8125rem; font-weight: 700; }
    .gift-group-main { flex: 1; min-width: 0; display: flex; flex-direction: column; }
    .gift-group-name { font-size: 0.9375rem; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
    .gift-group-head.place .gift-group-name { flex: 1; min-width: 0; }
    .gift-group-sub { font-size: 0.6875rem; color: var(--text-tertiary); }
    .gift-group-pin { flex-shrink: 0; display: flex; color: var(--accent); align-self: center; }
    .gift-group-money { flex-shrink: 0; font-size: 0.75rem; font-weight: 700; color: var(--text-secondary); font-variant-numeric: tabular-nums; }
    .gift-when { flex-shrink: 0; font-size: 0.6875rem; font-weight: 600; color: var(--text-secondary); background: var(--badge-bg); padding: 2px 8px; border-radius: 10px; white-space: nowrap; }
    .gift-when.past { color: var(--danger); background: var(--danger-bg); }
    .gift-group-warn { margin-top: 6px; font-size: 0.6875rem; font-weight: 600; color: var(--danger); }
    .gift-progress { flex-shrink: 0; width: 54px; height: 6px; border-radius: 3px; background: color-mix(in srgb, var(--text) 8%, transparent); overflow: hidden; }
    .gift-progress i { display: block; height: 100%; background: var(--accent); }
    .gift-progress i.done { background: var(--success); }
    .gift-group-rows { margin-top: 6px; }
    .gift-group-rows .item:last-child { border-bottom: 0; }
    /* The row's second line: where, per-one price, a note marker. */
    .gift-sub { display: flex; align-items: center; gap: 2px; flex-wrap: wrap; font-size: 0.6875rem; font-weight: 500; color: var(--text-tertiary); margin-top: 1px; }
    .gift-sub svg { width: 11px; height: 11px; vertical-align: -1px; }
    .gift-where { display: inline-flex; align-items: center; gap: 3px; }
    .gift-where.past { color: var(--danger); font-weight: 600; }
    .gift-sep { margin: 0 4px; opacity: 0.6; }
    .gift-note-dot { display: inline-flex; color: var(--text-tertiary); }
    .gift-price { flex-shrink: 0; font-size: 0.8125rem; font-weight: 700; font-variant-numeric: tabular-nums; }
    .gift-price.paid { color: var(--success); }
    /* The money card. */
    .gift-summary { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg); padding: 16px; box-shadow: var(--shadow-lg); margin-bottom: 12px; }
    .gift-summary-top { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
    .gift-summary-spent { font-size: 1.625rem; font-weight: 700; letter-spacing: -0.01em; font-variant-numeric: tabular-nums; }
    .gift-summary-of { font-size: 0.8125rem; color: var(--text-secondary); }
    .gift-summary-left { margin-left: auto; font-size: 0.75rem; font-weight: 600; color: var(--warning); font-variant-numeric: tabular-nums; }
    .gift-bar { margin-top: 10px; height: 10px; border-radius: 5px; background: color-mix(in srgb, var(--text) 8%, transparent); overflow: hidden; display: flex; }
    .gift-bar .spent { background: var(--success); }
    .gift-bar .planned { background: repeating-linear-gradient(135deg, var(--warning) 0 4px, color-mix(in srgb, var(--warning) 45%, transparent) 4px 8px); }
    .gift-summary-legend { margin-top: 8px; display: flex; align-items: center; gap: 12px; font-size: 0.6875rem; font-weight: 600; color: var(--text-secondary); }
    .gift-summary-legend span { display: inline-flex; align-items: center; gap: 5px; }
    .gift-summary-legend i { width: 10px; height: 4px; border-radius: 2px; }
    .gift-summary-legend i.spent { background: var(--success); }
    .gift-summary-legend i.planned { background: var(--warning); }
    .gift-summary-rest { margin-left: auto; color: var(--text-tertiary); font-variant-numeric: tabular-nums; }
    .gift-summary-rest.over { color: var(--danger); }
    .gift-budget-btn { margin-top: 10px; font: inherit; font-size: 0.6875rem; font-weight: 600; color: var(--accent); background: transparent; border: 0; padding: 4px 0; cursor: pointer; }
    .gift-budget-btn:focus-visible { outline: none; box-shadow: var(--focus-ring); }
    .gift-post-row { margin-top: 10px; padding-top: 10px; border-top: 1px solid var(--border); display: flex; align-items: center; gap: 8px; cursor: pointer; }
    .gift-post-icon { display: flex; color: var(--accent); flex-shrink: 0; }
    .gift-post-label { flex: 1; min-width: 0; font-size: 0.75rem; font-weight: 600; color: var(--text-secondary); }
    .gift-post-label span { display: block; font-size: 0.6875rem; font-weight: 500; color: var(--text-tertiary); }
    /* The bought log. */
    .gift-log { padding: 14px 16px; }
    .gift-log-head { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; margin-bottom: 4px; }
    .gift-log-total { font-size: 0.9375rem; font-weight: 600; font-variant-numeric: tabular-nums; }
    .gift-log-sub { font-size: 0.75rem; color: var(--text-secondary); }
    .gift-log-export { margin-left: auto; font: inherit; font-size: 0.6875rem; font-weight: 600; color: var(--accent); background: transparent; border: 0; cursor: pointer; padding: 4px 0; }
    .gift-log-export:focus-visible { outline: none; box-shadow: var(--focus-ring); }
    .gift-log-row { display: flex; align-items: center; gap: 10px; padding: 10px 0; border-bottom: 1px solid var(--border); cursor: pointer; }
    .gift-log-row:last-child { border-bottom: 0; }
    .gift-log-row:focus-visible { outline: none; box-shadow: var(--focus-ring); }
    .gift-log-icon { flex-shrink: 0; width: 40px; height: 40px; border-radius: 10px; background: var(--accent-bg); color: var(--accent); display: flex; align-items: center; justify-content: center; }
    .gift-log-main { flex: 1; min-width: 0; }
    .gift-log-item { display: block; font-size: 0.875rem; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
    .gift-log-meta { display: block; font-size: 0.6875rem; color: var(--text-tertiary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
    .gift-log-price { flex-shrink: 0; font-size: 0.8125rem; font-weight: 700; font-variant-numeric: tabular-nums; }
    .gift-tools { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 12px; }
    .gift-tools .chip { display: inline-flex; align-items: center; gap: 6px; }
    /* The detail sheet. */
    .gift-detail-body { padding-top: 8px; }
    .gift-detail-top { display: flex; align-items: flex-start; gap: 10px; }
    .gift-detail-title-wrap { flex: 1; min-width: 0; }
    .gift-detail-title { font-size: 1.25rem; font-weight: 700; letter-spacing: -0.01em; line-height: 1.25; }
    .gift-detail-sub { font-size: 0.8125rem; color: var(--text-secondary); margin-top: 2px; }
    .gift-detail-pri { flex-shrink: 0; font-size: 0.6875rem; font-weight: 700; padding: 3px 9px; border-radius: 10px; }
    .gift-detail-pri.pri-high { color: var(--priority-high); background: color-mix(in srgb, var(--priority-high) 12%, transparent); }
    .gift-detail-pri.pri-medium { color: var(--priority-medium); background: color-mix(in srgb, var(--priority-medium) 12%, transparent); }
    .gift-detail-pri.pri-low { color: var(--priority-low); background: color-mix(in srgb, var(--priority-low) 12%, transparent); }
    .gift-detail-actions { margin-top: 14px; display: flex; gap: 8px; }
    .gift-detail-actions .btn { flex: 1; }
    .gift-detail-actions .btn:last-child { flex: 0 0 auto; }
    .gift-detail-media { margin-top: 16px; display: flex; gap: 8px; align-items: stretch; }
    .gift-detail-photo, .gift-detail-photo-add { width: 84px; height: 84px; flex-shrink: 0; border-radius: var(--radius-sm); object-fit: cover; }
    .gift-detail-photo { cursor: pointer; border: 1px solid var(--border); }
    .gift-detail-photo-add { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px; border: 1px dashed var(--border-strong); background: transparent; color: var(--text-tertiary); font: inherit; font-size: 0.6875rem; font-weight: 600; cursor: pointer; }
    .gift-detail-note { flex: 1; min-width: 0; text-align: left; border-radius: var(--radius-sm); border: 1px solid var(--border); background: transparent; font: inherit; font-size: 0.8125rem; color: var(--text-secondary); line-height: 1.5; padding: 10px 12px; cursor: pointer; }
    .gift-detail-note-empty { color: var(--text-tertiary); }
    .gift-detail-field { margin-top: 14px; }
    .gift-detail-label { font-size: 0.6875rem; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; color: var(--text-tertiary); }
    .gift-detail-value { margin-top: 4px; font-size: 0.875rem; display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
    .gift-detail-value svg { width: 14px; height: 14px; }
    .gift-detail-hint { color: var(--text-tertiary); font-size: 0.8125rem; }
    .gift-detail-link { display: inline-flex; align-items: center; gap: 6px; font: inherit; font-size: 0.875rem; font-weight: 600; color: var(--accent); background: transparent; border: 0; padding: 0; cursor: pointer; }
    .gift-detail-link:focus-visible { outline: none; box-shadow: var(--focus-ring); }
    .gift-detail-pack { margin-top: 16px; padding-top: 12px; border-top: 1px solid var(--border); display: flex; align-items: center; gap: 8px; cursor: pointer; }
    .gift-detail-pack-icon { display: flex; color: var(--accent); flex-shrink: 0; }
    .gift-detail-pack-label { flex: 1; min-width: 0; font-size: 0.875rem; font-weight: 600; }
    .gift-detail-pack-label span { display: block; font-size: 0.6875rem; font-weight: 500; color: var(--text-tertiary); }
    /* Shopping mode - the dark, big-tap screen. */
    .modal.gift-shop .modal-panel { max-height: 100dvh; height: 100dvh; border-radius: 0; background: #0F0F11; color: #F5F3F8; border: 0; }
    .shop-body { padding: 28px 24px calc(24px + env(safe-area-inset-bottom)); display: flex; flex-direction: column; min-height: 100%; }
    .shop-top { display: flex; align-items: center; justify-content: space-between; }
    .shop-label { font-size: 0.75rem; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; color: #B58CFF; }
    .shop-done { font: inherit; font-size: 0.8125rem; color: rgba(255,255,255,0.6); background: transparent; border: 0; cursor: pointer; padding: 6px 0; }
    .shop-count { margin-top: 16px; font-size: 2.125rem; font-weight: 700; letter-spacing: -0.02em; line-height: 1.1; }
    .shop-sub { margin-top: 4px; font-size: 0.8125rem; color: rgba(255,255,255,0.6); }
    .shop-chips { margin-top: 18px; display: flex; gap: 8px; flex-wrap: wrap; }
    .shop-chip { font: inherit; font-size: 0.75rem; font-weight: 600; padding: 7px 12px; border-radius: 14px; border: 1px solid rgba(255,255,255,0.18); background: transparent; color: #F5F3F8; cursor: pointer; }
    .shop-chip.on { background: #B58CFF; color: #0F0F11; border-color: #B58CFF; font-weight: 700; }
    .shop-chip:focus-visible, .shop-tick:focus-visible, .shop-buy:focus-visible, .shop-add:focus-visible { outline: none; box-shadow: 0 0 0 3px rgba(181,140,255,0.5); }
    .shop-list { margin-top: 10px; }
    .shop-item { display: flex; align-items: center; gap: 12px; padding: 14px 0; border-bottom: 1px solid rgba(255,255,255,0.08); }
    .shop-item.done { opacity: 0.45; }
    .shop-item.done .shop-item-title { text-decoration: line-through; }
    .shop-tick { flex-shrink: 0; width: 30px; height: 30px; border-radius: 9px; border: 2px solid rgba(255,255,255,0.3); background: transparent; color: #0F0F11; display: flex; align-items: center; justify-content: center; cursor: pointer; padding: 0; }
    .shop-tick.on { background: #7CD66B; border-color: #7CD66B; }
    .shop-main { flex: 1; min-width: 0; }
    .shop-item-title { font-size: 1.0625rem; font-weight: 600; }
    .shop-qty { color: #B58CFF; }
    .shop-item-sub { font-size: 0.8125rem; color: rgba(255,255,255,0.55); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
    .shop-buy { flex-shrink: 0; font: inherit; font-size: 0.6875rem; font-weight: 700; color: #0F0F11; background: #B58CFF; border: 0; border-radius: 10px; padding: 6px 10px; cursor: pointer; min-height: 32px; }
    .shop-empty { padding: 24px 0; font-size: 0.875rem; color: rgba(255,255,255,0.5); }
    .shop-add { margin-top: auto; font: inherit; font-size: 0.9375rem; font-weight: 700; color: #0F0F11; background: #B58CFF; border: 0; border-radius: 16px; padding: 14px; cursor: pointer; }
    .shop-foot { margin-top: 10px; font-size: 0.75rem; color: rgba(255,255,255,0.45); text-align: center; }
    /* Quick add. */
    /* 1rem, not smaller: anything a finger types into must clear the 16px
       floor or iOS zooms the page on focus (the suite guards this). */
    .gift-quick-input { width: 100%; box-sizing: border-box; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 1rem; line-height: 1.6; resize: vertical; }
    .gift-quick-preview:not(:empty) { margin-top: 12px; }
    .gift-quick-label { font-size: 0.6875rem; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; color: var(--text-tertiary); margin-bottom: 4px; }
    .gift-quick-row { display: flex; align-items: center; gap: 8px; padding: 8px 0; border-bottom: 1px solid var(--border); font-size: 0.8125rem; }
    .gift-quick-row:last-child { border-bottom: 0; }
    .gift-quick-tick { display: flex; color: var(--success); flex-shrink: 0; }
    .gift-quick-row.bad .gift-quick-tick { color: var(--warning); }
    .gift-quick-main { flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
    .gift-quick-main span { color: var(--text-tertiary); }
    .gift-quick-warn { color: var(--warning); font-weight: 600; }
    .gift-quick-qty { flex-shrink: 0; font-size: 0.75rem; font-weight: 600; color: var(--text-secondary); }
    .gift-quick-place { flex-shrink: 0; font-size: 0.6875rem; font-weight: 600; color: var(--accent); background: var(--accent-bg); border-radius: 10px; padding: 2px 8px; }
    .gift-quick-any { flex-shrink: 0; font-size: 0.6875rem; color: var(--text-tertiary); }
    .gift-item { padding-right: 28px; }
    .gift-del-btn { position: absolute; top: 4px; right: 0; }

    /* Budget settle-up */
    .settle-hint { font-size: 0.78125rem; color: var(--text-tertiary); padding: 10px 2px; margin-bottom: 8px; }
    .settle-hint a { color: var(--accent); font-weight: 600; text-decoration: underline; }
    .settle-card { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg); padding: 14px 16px; margin-bottom: 16px; }
    .settle-head { font-size: 0.8125rem; font-weight: 700; color: var(--text); margin-bottom: 10px; display: flex; flex-direction: column; gap: 2px; }
    .settle-sub { font-size: 0.6875rem; font-weight: 500; color: var(--text-tertiary); text-transform: none; letter-spacing: 0; }
    .settle-balances { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 10px; }
    .settle-chip { font-size: 0.6875rem; font-weight: 600; padding: 4px 10px; border-radius: 20px; border: 1px solid var(--border); }
    .settle-chip.pos { color: var(--success); }
    .settle-chip.neg { color: var(--danger); }
    .settle-transfers { display: flex; flex-direction: column; gap: 6px; padding-top: 10px; border-top: 1px solid var(--border); }
    .settle-transfer { font-size: 0.78125rem; color: var(--text-secondary); }
    .settle-transfer b { color: var(--text); }

    @media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; } }

    /* Printable output for all four tabs — strips interactive chrome, forces
       one plain column, and only prints whichever tab is currently open since
       the other three views stay display:none in the DOM. */
    @media print {
      html, body, .app, .main-scroll { height: auto !important; overflow: visible !important; }
      body { background: #fff !important; color: #000 !important; }
      .app-header, .dock, .fab, .toast-container, .modal, .tooltip, .status-banner,
      #templatesBar, .add-form, .progress-card,
      .filter-chip-row, .notes-toolbar, .add-item-row, .budget-toolbar,
      .del-btn, .drag-handle, .item-traveler, button, select, .fx-refresh { display: none !important; }
      .main-scroll { padding: 0 !important; }
      /* Sticky is enabled below 1023px, and a printed page falls in that band —
         a pinned header would repeat or overlap across page breaks. */
      .day-header, .tag-group-header { position: static !important; }
      .grid { display: block !important; }
      .card { border: none !important; box-shadow: none !important; background: transparent !important; padding: 0 0 18px !important; break-inside: avoid; }

      .cat-header, .day-header, .expense-group-header, .note-head { border-bottom: 1px solid #000; padding-bottom: 4px; margin-bottom: 6px; }
      .cat-title, .cat-name-text, .cat-badge, .day-title, .expense-group-label, .expense-group-total, .note-title { color: #000 !important; background: transparent !important; }
      .cat-title, .cat-name-text, .day-title, .note-title { font-size: 0.875rem; font-weight: 700; }

      .item, .activity, .expense { border: none !important; padding: 3px 0 !important; }
      .it-text, .activity-title, .activity-desc, .activity-time,
      .expense-desc, .expense-amount, .note-content { color: #000 !important; }

      .cb { border: 1.5px solid #000 !important; background: transparent !important; }
      .cb.on { background: #000 !important; }
      .cb svg { color: #fff !important; }

      .tag-group-label, .expense-group-label, .commute-mode-badge,
      .payment-badge, .fx-badge, .duration-badge, .ticket-badge, .note-tag { background: transparent !important; border: 1px solid #000 !important; color: #000 !important; }
      .settle-card, .budget-chart-bar, .payer-rollup { border: 1px solid #000 !important; }
      .settle-chip.pos, .settle-chip.neg { color: #000 !important; }
    }

/* ---- Keyboard shortcuts sheet -------------------------------------------
   Shortcuts only exist for a physical keyboard, so the row that opens this
   list is hidden on touch rather than offering something unreachable. */
.shortcuts-row { display: none; }
@media (hover: hover) and (pointer: fine) {
  .shortcuts-row { display: flex; }
}
.shortcut-list { display: flex; flex-direction: column; gap: 2px; }
.shortcut-row {
  display: flex;
  align-items: flex-start;
  gap: 14px;
  padding: 10px 2px;
  border-bottom: 1px solid var(--border);
}
.shortcut-row:last-child { border-bottom: none; }
.shortcut-keys {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  gap: 3px;
  min-width: 92px;
  color: var(--text-tertiary);
  font-size: 0.75rem;
}
.shortcut-list kbd {
  display: inline-block;
  min-width: 22px;
  padding: 3px 7px;
  border: 1px solid var(--border-strong);
  border-bottom-width: 2px;
  border-radius: 7px;
  background: var(--surface);
  color: var(--text);
  font-family: inherit;
  font-size: 0.75rem;
  font-weight: 600;
  line-height: 1.3;
  text-align: center;
}
.shortcut-label {
  display: flex;
  flex-direction: column;
  gap: 2px;
  font-size: 0.875rem;
  line-height: 1.35;
}
.shortcut-when { color: var(--text-tertiary); font-size: 0.6875rem; }

    /* ══ Large desktop (1440px+) ═══════════════════════════════════════════ */
    @media (min-width: 1440px) {
      html { font-size: 18px; }
      /* Money and times line up when digits share a width. */
      #budgetOrganized { font-variant-numeric: tabular-nums; }
    }

    /* ══ Desktop density ════════════════════════════════════════════════════
       Touch sizing (48px rows, thumb padding) reads bloated under a pointer.
       Same gate as the hover affordances: a fine pointer earns tighter rows,
       and row actions calm down to hover-reveal - the classic desktop trade
       of discoverability for density. opacity (not display/visibility) so
       the controls stay focusable and assistive tech still sees them;
       :focus-within brings them back for keyboard users. */
    @media (hover: hover) and (pointer: fine) {
      .item { min-height: 36px; }
      .activity { min-height: 36px; }
      .expense { min-height: 36px; padding: 6px 0; }
      .tool-row { padding: 9px 4px; }
      .card { padding: 14px 16px; }
      .itinerary-day { margin-bottom: 6px; }

      .note-actions { opacity: 0; transition: opacity var(--transition); }
      .note:hover .note-actions, .note:focus-within .note-actions { opacity: 1; }
      .item > .del-btn, .item .gift-del-btn { opacity: 0; transition: opacity var(--transition); }
      /* !important because some rows dim these with an inline opacity:0.5 -
         those rest at their designed dimness instead of 0, and every variant
         sharpens fully on hover. */
      .item:hover > .del-btn, .item:focus-within > .del-btn,
      .item:hover .gift-del-btn, .item:focus-within .gift-del-btn { opacity: 1 !important; }
      /* The activity/commute corner cluster rests at its inline 0.5 and
         sharpens on row hover (inline style needs the !important). */
      .activity:hover .card-menu .del-btn, .activity:focus-within .card-menu .del-btn { opacity: 1 !important; }
    }

    /* Sticky section headers while scrolling (Contacts/Calendar pattern):
       a day's header pins to the top until its own card ends, then the next
       day pushes it away - sticky inside the card gives that hand-off for
       free. Phone widths only: the 1440px+ layout puts these lists in CSS
       multicol, where sticky is unreliable, and desktop has less need.
       Negative margins bleed the header over the card's own padding so
       activities can't peek out beside it while pinned; z-index sits above
       the rows but far below the kebab dropdowns (z 50). */
    @media (max-width: 1023px) {
      .day-header {
        /* Below the day scrubber when it is shown (--scrubber-h, 0 otherwise). */
        position: sticky; top: calc(var(--sticky-top) + var(--scrubber-h, 0px)); z-index: 5;
        background: var(--surface);
        margin-left: calc(-1 * var(--card-pad)); margin-right: calc(-1 * var(--card-pad));
        padding-left: var(--card-pad); padding-right: var(--card-pad);
      }
      .tag-group-header { position: sticky; top: var(--sticky-top); z-index: 5; background: var(--bg); }
    }

    /* ══ Tooltips ═══════════════════════════════════════════════════════════
       Native title="" tooltips are slow, unstyled and never appear for
       keyboard users. These replace them on fine pointers AND on focus, so
       every hover hint has a keyboard path. Built from the same title text,
       so there is one source of truth and nothing to keep in sync. */
    .tooltip {
      position: fixed; z-index: 3000; pointer-events: none;
      max-width: 260px; padding: 6px 9px;
      border-radius: var(--radius-sm);
      background: var(--text); color: var(--surface);
      font-size: 0.75rem; font-weight: 600; line-height: 1.35;
      box-shadow: var(--shadow-lg);
      opacity: 0; transform: translateY(2px);
      transition: opacity 120ms ease, transform 120ms ease;
      display: flex; align-items: center; gap: 7px;
    }
    .tooltip.show { opacity: 1; transform: translateY(0); }
    .tooltip kbd {
      padding: 1px 5px; border-radius: 5px;
      background: var(--surface); color: var(--text);
      font-family: inherit; font-size: 0.6875rem; font-weight: 700;
    }
    @media (prefers-reduced-motion: reduce) { .tooltip { transition: none; } }

    /* ══ Installed-window title bar (Window Controls Overlay) ═══════════════
       When the installed app opts into WCO the OS stops drawing its own title
       bar, so the app header becomes it: it has to sit inside the free strip
       the OS leaves (env(titlebar-area-*)) and be draggable, while the actual
       controls stay clickable via no-drag. Guarded by the display-mode media
       query, so a browser tab is completely unaffected. */
    @media (display-mode: window-controls-overlay) {
      .app-header {
        position: fixed; top: env(titlebar-area-y, 0);
        left: env(titlebar-area-x, 0);
        width: env(titlebar-area-width, 100%);
        height: env(titlebar-area-height, auto);
        display: flex; align-items: center;
        padding-top: 0; padding-bottom: 0;
        -webkit-app-region: drag; app-region: drag;
      }
      /* Anything interactive must opt back out of the drag region. */
      .app-header button, .app-header select, .app-header input, .app-header a {
        -webkit-app-region: no-drag; app-region: no-drag;
      }
      .app { padding-top: env(titlebar-area-height, 0); }
    }

    /* ══ Master–detail: record editors dock as a right-hand pane ════════════
       WHY THIS IS PURE CSS. openModal() only adds .open — it also pushes the
       back/Esc layer, saves focus and applies the dialog semantics. Re-styling
       .open into a docked pane therefore keeps every one of those behaviours
       (and every test that asserts them) untouched: nothing about the modal
       CONTRACT changes, only where the panel is drawn. A JS rewrite here would
       have put the layer stack, focus restore, printing and the back gesture
       all at risk to achieve the same picture.

       Only the five RECORD editors convert. Confirms, pickers, the search
       sheet and the tag manager stay centred dialogs: they are transient
       questions, not a record you sit and edit beside its list.

       Stacking (activity ➝ Add Note) leaves both panes docked with the newer
       on top; Esc pops exactly one layer, revealing the one beneath, which is
       the same mental model the layer stack already gives everywhere else. */
    /* Guarded on :has(): the docking only makes sense if the master column can
       react by yielding width. Without :has() support the pane would cover the
       list with no scrim, so such browsers keep the centred dialog instead. */
    @supports selector(:has(*)) {
    @media (min-width: 1280px) {
      :root { --detail-w: 460px; --app-max: 1400px; }

      /* Docked to the APP's right edge, not the viewport's — on a wide
         monitor the centred app would otherwise be trailed by a pane floating
         off in the corner. */
      #noteModal, #activityModal, #commuteModal, #expenseModal, #giftModal {
        inset: 0 max(0px, calc((100vw - var(--app-max)) / 2)) 0 auto;
        width: var(--detail-w);
      }
      /* No scrim: the list beside it stays live and readable. That is the
         whole point of a detail pane rather than a modal. */
      #noteModal .modal-backdrop, #activityModal .modal-backdrop,
      #commuteModal .modal-backdrop, #expenseModal .modal-backdrop,
      #giftModal .modal-backdrop { display: none; }

      #noteModal .modal-panel, #activityModal .modal-panel,
      #commuteModal .modal-panel, #expenseModal .modal-panel,
      #giftModal .modal-panel {
        margin: 0; width: 100%; max-width: none;
        height: 100%; max-height: none;
        border-radius: 0; border-top: none; border-right: none; border-bottom: none;
        border-left: 1px solid var(--border-strong);
        box-shadow: var(--shadow-lg);
        transform: translateX(100%);
        transition: transform 220ms var(--ease-settle);
      }
      #noteModal.open .modal-panel, #activityModal.open .modal-panel,
      #commuteModal.open .modal-panel, #expenseModal.open .modal-panel,
      #giftModal.open .modal-panel { transform: translateX(0); }

      /* The list gives up the width rather than being covered — this is what
         makes it master-DETAIL instead of an overlay that happens to be on
         the right. :has() is what lets the ancestor react to the open pane
         without any JS bookkeeping to keep in sync. */
      .app { transition: padding-right 220ms var(--ease-settle); }
      body:has(#noteModal.open) .app,
      body:has(#activityModal.open) .app,
      body:has(#commuteModal.open) .app,
      body:has(#expenseModal.open) .app,
      body:has(#giftModal.open) .app { padding-right: var(--detail-w); }

      /* Snackbars would otherwise land underneath the pane. */
      body:has(#noteModal.open) .toast-container,
      body:has(#activityModal.open) .toast-container,
      body:has(#commuteModal.open) .toast-container,
      body:has(#expenseModal.open) .toast-container,
      body:has(#giftModal.open) .toast-container {
        right: calc(24px + var(--detail-w));
      }

      @media (prefers-reduced-motion: reduce) {
        .app { transition: none; }
        #noteModal .modal-panel, #activityModal .modal-panel,
        #commuteModal .modal-panel, #expenseModal .modal-panel,
        #giftModal .modal-panel { transition: none; }
      }
    }
    }

    /* ══ AMOLED black ═══════════════════════════════════════════════════════
       --bg was already #000; on an OLED panel the pixels still drawing power
       are the SURFACES (cards, header, dock, inputs). This drops them to true
       black and leans on the existing hairline borders for separation, which
       is why it is opt-in: borders doing all the work is a deliberate look,
       not automatically better. Composes with dark mode rather than being a
       fourth theme, so it needs both the media-query and the explicit-dark
       case. */
    @media (prefers-color-scheme: dark) {
      :root[data-amoled="on"]:not([data-theme="light"]) {
        --surface: #000000;
        --surface-hover: #0C0C0E;
        --dock-bg: rgba(0,0,0,0.92);
        --border: rgba(255,255,255,0.14);
        --border-strong: rgba(255,255,255,0.24);
      }
    }
    :root[data-amoled="on"][data-theme="dark"] {
      --surface: #000000;
      --surface-hover: #0C0C0E;
      --dock-bg: rgba(0,0,0,0.92);
      --border: rgba(255,255,255,0.14);
      --border-strong: rgba(255,255,255,0.24);
    }

    /* ══ S Pen hover ════════════════════════════════════════════════════════
       A phone answers (hover: hover) with "none", so the desktop affordance
       block never applies - yet an S Pen genuinely hovers. data-pen is set
       only after a real pen event is observed (app-core setupPenHover), so
       this can never switch on for a plain touch device and leave sticky
       hover behind. Deliberately a small curated set, not a copy of the
       desktop block: a pen wants to preview and target, not to be told about
       every control it passes. */
    :root[data-pen="on"] .item:hover,
    :root[data-pen="on"] .activity:hover,
    :root[data-pen="on"] .tool-row:hover,
    :root[data-pen="on"] .note:hover,
    :root[data-pen="on"] .expense:hover { background: var(--bg); }
    :root[data-pen="on"] .btn:hover,
    :root[data-pen="on"] .btn-sm:hover,
    :root[data-pen="on"] .chip:hover { border-color: var(--text-tertiary); }
    :root[data-pen="on"] .icon-btn:hover,
    :root[data-pen="on"] .del-btn:hover { background: var(--bg); color: var(--text); }
    :root[data-pen="on"] .day-header:hover { background: var(--bg); }

    /* ── Sign-in gate ────────────────────────────────────────────────────
       Visible by default in the markup (see index.html) so a signed-out load
       cannot paint a trip before the script runs. Not a .modal: it must never
       pick up the modal backdrop's click-to-dismiss, and it is deliberately
       outside the history/layer stack, so Back and Esc cannot open the app.
       Inputs inherit .inp's 1rem, which clears the 16px floor that stops iOS
       zooming the page on focus. */
    #authGate {
      position: fixed; inset: 0; z-index: 9999;
      display: flex; align-items: center; justify-content: center;
      background: var(--bg); padding: 20px;
      padding-top: calc(20px + env(safe-area-inset-top));
      padding-bottom: calc(20px + env(safe-area-inset-bottom));
      overflow-y: auto;
    }
    .auth-gate-panel { width: 100%; max-width: 420px; padding: 22px 18px; }
    .auth-gate-brand { display: flex; align-items: center; gap: 12px; margin-bottom: 10px; }
    .auth-gate-brand img { border-radius: 10px; }
    .auth-gate-brand h3 { font-size: 1.375rem; font-weight: 700; letter-spacing: -0.01em; margin: 0; }
    .auth-intro { color: var(--text-secondary); font-size: 0.875rem; margin: 0 0 14px; }
    .auth-gate-actions { display: flex; justify-content: space-between; gap: 8px; margin-top: 14px; }
    .auth-error {
      color: var(--danger); font-size: 0.8125rem; margin-bottom: 10px;
      background: var(--danger-bg, transparent); border-radius: var(--radius-md);
    }
    .auth-email { font-size: 0.8125rem; color: var(--text-secondary); word-break: break-all; }
    /* The banner for the state the brief calls out: signed out AFTER having
       synced, i.e. edits piling up locally that nothing is carrying away. */
    .sync-warn-banner {
      background: var(--danger-bg, rgba(220,38,38,.08));
      border: 1px solid var(--danger);
      color: var(--danger);
      border-radius: var(--radius-md);
      padding: 10px 12px; margin-bottom: 12px;
      font-size: 0.8125rem;
    }

    /* ══ Plan: Agenda view ══════════════════════════════════════════════════
       Hour ruler on the left, blocks placed and sized by planned time on the
       right. The punch dots are the timeline's own .commute-route-dot,
       re-anchored to the block's corners; everything else about them (hit
       area, punched colour and pop) is inherited so both views log the same
       way. */
    :root { --agenda-hour: 48px; }
    .plan-view-toggle { display: flex; gap: 4px; margin-right: auto; }
    .agenda { display: grid; grid-template-columns: 44px minmax(0, 1fr); column-gap: 8px; margin: 2px 0 4px; }
    .agenda-ruler { display: flex; flex-direction: column; }
    .agenda-hour { height: var(--agenda-hour); font-size: 0.6875rem; font-weight: 600; color: var(--text-tertiary); text-align: right; font-variant-numeric: tabular-nums; line-height: 1; padding-right: 2px; }
    .agenda-grid {
      position: relative;
      background-image: repeating-linear-gradient(to bottom, var(--border) 0 1px, transparent 1px var(--agenda-hour));
    }
    .agenda-block {
      position: absolute; box-sizing: border-box; z-index: 1;
      border-radius: var(--radius-sm); background: var(--accent-bg);
      border: 1px solid var(--accent-light); border-left: 3px solid var(--accent); cursor: pointer;
      transition: box-shadow var(--transition);
      /* Read-only timeline: nothing hangs off the corners any more, and the
         plan-vs-actual panel slides in INSIDE the block, which clips it.
         pan-y leaves vertical scrolling to the page. */
      overflow: hidden; touch-action: pan-y;
    }
    /* The panel: fills the block, parked off its right edge; a left swipe
       (or the control) pulls it across, leaving a 24px sliver of the block
       to swipe or tap it back. Visible only while open or being dragged. */
    .agenda-compare { position: absolute; inset: 0; box-sizing: border-box; z-index: 2; visibility: hidden; transform: translateX(100%); transition: transform 200ms var(--ease-settle); background: var(--surface); border-left: 1px dashed var(--border-strong); padding: 4px 10px; overflow: hidden; display: flex; flex-direction: column; justify-content: center; gap: 1px; font-size: 0.6875rem; font-variant-numeric: tabular-nums; color: var(--text-secondary); }
    .agenda-compare.show { visibility: visible; }
    .agenda-block.open .agenda-compare { transform: translateX(24px); }
    .agenda-block.dragging .agenda-compare { transition: none; }
    @media (prefers-reduced-motion: reduce) { .agenda-compare { transition: none; } }
    .agenda-compare .cmp-row { display: flex; gap: 8px; align-items: baseline; white-space: nowrap; }
    .agenda-compare .cmp-k { flex-shrink: 0; width: 46px; font-size: 0.6875rem; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; color: var(--text-tertiary); }
    .agenda-compare .cmp-plan { color: var(--text); font-weight: 600; }
    .agenda-compare .cmp-logged { color: var(--success); font-weight: 600; }
    .agenda-compare .cmp-delta { color: var(--warning); font-weight: 600; }
    .agenda-compare.compact { flex-direction: row; align-items: center; gap: 4px; white-space: nowrap; }
    .agenda-compare.compact .cmp-k { width: auto; }
    /* Desktop / keyboard control for the same panel; touch uses the swipe. */
    .agenda-cmp-btn { display: none; flex-shrink: 0; width: 22px; height: 22px; align-items: center; justify-content: center; border: 0; border-radius: 50%; background: transparent; color: var(--text-tertiary); cursor: pointer; padding: 0; margin: -3px -4px -3px 0; }
    .agenda-cmp-btn:focus-visible { outline: none; box-shadow: var(--focus-ring); display: inline-flex; }
    @media (hover: hover) and (pointer: fine) {
      .agenda-cmp-btn { display: inline-flex; opacity: 0; transition: opacity var(--transition); }
      .agenda-block:hover .agenda-cmp-btn, .agenda-block:focus-within .agenda-cmp-btn, .agenda-block.open .agenda-cmp-btn { opacity: 1; }
      .agenda-cmp-btn:hover { background: var(--surface-hover); color: var(--accent); }
    }
    /* Done: the stripe and glyph go green with a check, instead of fading
       the whole block to 60% - a finished item is still an item. */
    /* Colour by type: stripe, glyph and a 6% tint from the type hue. Done,
       live and clash rules below still win. */
    .agenda-block[data-type="food"] { --type: var(--type-food); }
    .agenda-block[data-type="transit"] { --type: var(--type-transit); }
    .agenda-block[data-type="outdoors"] { --type: var(--type-outdoors); }
    .agenda-block[data-type="nightlife"] { --type: var(--type-nightlife); }
    .agenda-block[data-type] { border-left-color: var(--type, var(--accent)); border-color: color-mix(in srgb, var(--type, var(--accent)) 45%, transparent); border-left-color: var(--type, var(--accent)); background: color-mix(in srgb, var(--type, var(--accent)) 6%, var(--surface)); }
    .agenda-block[data-type] .agenda-glyph { color: var(--type, var(--accent)); }
    .agenda-legend { margin-top: 5px; display: flex; flex-wrap: wrap; gap: 4px 10px; font-size: 0.6875rem; font-weight: 600; color: var(--text-secondary); }
    .agenda-legend-item { display: inline-flex; align-items: center; gap: 5px; }
    .agenda-legend-item i { width: 8px; height: 8px; border-radius: 2px; background: var(--accent); }
    .agenda-legend-item[data-type="food"] i { background: var(--type-food); }
    .agenda-legend-item[data-type="transit"] i { background: var(--type-transit); }
    .agenda-legend-item[data-type="outdoors"] i { background: var(--type-outdoors); }
    .agenda-legend-item[data-type="nightlife"] i { background: var(--type-nightlife); }
    .agenda-block.done { opacity: 0.75; border-color: color-mix(in srgb, var(--success) 40%, transparent); border-left-color: var(--success); background: color-mix(in srgb, var(--success) 6%, var(--surface)); }
    .agenda-block:focus-visible { outline: none; box-shadow: var(--focus-ring); }
    /* Title first, with a type glyph; time and place on one muted line. */
    .agenda-body { height: 100%; padding: 6px 8px 4px 9px; overflow: hidden; display: flex; flex-direction: column; gap: 2px; }
    .agenda-head { display: flex; align-items: center; gap: 6px; min-width: 0; }
    .agenda-glyph { display: flex; flex-shrink: 0; color: var(--accent); }
    .agenda-glyph svg { width: 14px; height: 14px; }
    .agenda-block.done .agenda-glyph, .agenda-block.done[data-type] .agenda-glyph { color: var(--success); }
    .agenda-tag.drift { color: var(--warning); background: color-mix(in srgb, var(--warning) 12%, transparent); font-variant-numeric: tabular-nums; }
    .agenda-title { flex: 1; min-width: 0; font-size: 0.8125rem; font-weight: 600; line-height: 1.3; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
    .agenda-meta { padding-left: 20px; font-size: 0.6875rem; font-weight: 500; color: var(--text-secondary); line-height: 1.3; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
    /* A commute's route: both names give way, the arrow never does. */
    .agenda-route { display: flex; align-items: baseline; gap: 5px; min-width: 0; }
    .agenda-route-from, .agenda-route-to { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .agenda-route-from { flex: 1 1 auto; }
    .agenda-route-to { flex: 1 1 auto; }
    .agenda-route-arrow { flex-shrink: 0; color: var(--text-tertiary); }
    .agenda-sep { font-size: 0.6875rem; color: var(--text-tertiary); margin: 0 4px; }
    /* Live-day chips on the title row. */
    .agenda-tag, .agenda-next { margin-left: auto; flex-shrink: 0; font-size: 0.6875rem; font-weight: 700; letter-spacing: 0.02em; padding: 1px 6px; border-radius: 10px; white-space: nowrap; line-height: 1.5; }
    .agenda-tag { color: var(--accent); background: var(--accent-bg); }
    .agenda-tag.over { color: var(--warning); background: color-mix(in srgb, var(--warning) 12%, transparent); }
    .agenda-next { color: var(--accent); background: var(--surface); border: 1px solid var(--accent-light); }
    .agenda-block.live { background: var(--surface); border-color: var(--accent); box-shadow: 0 4px 16px color-mix(in srgb, var(--accent) 18%, transparent); z-index: 4; }
    /* Under ~45 minutes there is room for one line, not three: time and
       title sit side by side, and the location is dropped (it is one tap
       away in the editor). */
    .agenda-block.compact .agenda-body { flex-direction: row; align-items: center; gap: 8px; padding: 0 12px 0 9px; }
    .agenda-block.compact .agenda-head { flex: 1; }
    .agenda-block.compact .agenda-title { font-size: 0.75rem; }
    .agenda-block.compact .agenda-meta { display: none; }
    /* A one-hour block holds a single line now: centre it. Taller blocks keep
       reading from the top, where the eye lands. */
    .agenda-block.brief .agenda-body { justify-content: center; padding-top: 4px; }
    .agenda-actual { position: absolute; width: 4px; border-radius: 2px; background: var(--success); z-index: 2; pointer-events: none; }
    /* Folded free hours: one ruler row ("···") and a dashed strip that
       says how long, and opens on tap. Exactly one hour row tall so the
       grid's 48px hour lines stay aligned below it. */
    .agenda-hour.fold { display: flex; align-items: center; justify-content: flex-end; letter-spacing: 0.1em; }
    .agenda-fold { position: absolute; left: 0; right: 0; box-sizing: border-box; display: flex; align-items: center; gap: 8px; padding: 0 10px; border-radius: 10px; border: 1px dashed var(--border-strong); background: var(--surface); font: inherit; font-size: 0.6875rem; color: var(--text-secondary); cursor: pointer; z-index: 1; text-align: left; }
    .agenda-fold b { font-weight: 700; color: var(--text); }
    .agenda-fold span { color: var(--text-tertiary); }
    .agenda-fold svg { margin-left: auto; width: 14px; height: 14px; color: var(--text-tertiary); flex-shrink: 0; }
    .agenda-fold:focus-visible, .agenda-refold:focus-visible { outline: none; box-shadow: var(--focus-ring); }
    .agenda-refold { position: absolute; right: 0; transform: translateY(4px); display: inline-flex; align-items: center; gap: 4px; font: inherit; font-size: 0.6875rem; font-weight: 600; color: var(--text-tertiary); background: var(--surface); border: 1px dashed var(--border-strong); border-radius: 10px; padding: 2px 8px; cursor: pointer; z-index: 2; /* above the gap label that fills the opened hours */ }
    .agenda-refold svg { width: 12px; height: 12px; transform: rotate(180deg); }
    /* Live day: the past is tinted, the clock is a line with its time on
       the ruler. The line sits above the blocks, the tint under them. */
    .agenda-past { position: absolute; left: 0; right: 0; top: 0; background: color-mix(in srgb, var(--text) 3%, transparent); border-radius: 0 0 8px 8px; z-index: 0; pointer-events: none; }
    .agenda-now { position: absolute; left: -8px; right: 0; height: 0; border-top: 2px solid var(--danger); z-index: 5; pointer-events: none; }
    .agenda-now::before { content: ''; position: absolute; left: -4px; top: -5px; width: 8px; height: 8px; border-radius: 50%; background: var(--danger); }
    .agenda-now-label { position: absolute; left: -52px; top: -9px; width: 44px; box-sizing: border-box; text-align: center; font-size: 0.6875rem; font-weight: 700; line-height: 1.5; color: #FFFFFF; background: var(--danger); border-radius: 8px; font-variant-numeric: tabular-nums; }
    /* The day header's live line, under the date/base. */
    .day-live { margin-top: 4px; display: inline-flex; align-items: center; gap: 6px; font-size: 0.6875rem; font-weight: 600; color: var(--text-secondary); }
    .day-live::before { content: ''; width: 6px; height: 6px; border-radius: 50%; background: var(--success); flex-shrink: 0; }
    .day-live.behind { color: var(--warning); }
    .day-live.behind::before { background: var(--warning); }
    /* Trip strip above the day cards, and the day's own summary line
       (fourth line under the title: truncates, never wraps). */
    .plan-trip-summary { font-size: 0.75rem; font-weight: 500; color: var(--text-secondary); padding: 8px 12px; margin-bottom: 12px; border-radius: var(--radius-sm); background: var(--surface); border: 1px solid var(--border); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-variant-numeric: tabular-nums; }
    .plan-trip-summary b, .day-summary b { color: var(--warning); font-weight: 700; }
    .day-summary { margin-top: 4px; font-size: 0.6875rem; font-weight: 500; color: var(--text-secondary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-variant-numeric: tabular-nums; }
    /* Clashes: counted in the header, flagged on the intruding block. */
    .cat-badge.clash { color: var(--danger); background: var(--danger-bg); }
    .cat-badge.plan { color: var(--warning); background: color-mix(in srgb, var(--warning) 12%, transparent); }
    /* Missing transfer: an amber rail between two places, with the fix on it. */
    .agenda-transfer { position: absolute; left: 0; right: 0; z-index: 2; pointer-events: none; }
    .agenda-transfer .agenda-rail { border-left-color: var(--warning); }
    .agenda-connector-chip.transfer { border-color: var(--warning); color: var(--warning); max-width: calc(100% - 30px); }
    .agenda-transfer-add { flex-shrink: 0; margin-left: 4px; padding: 1px 7px; border-radius: 10px; background: var(--warning); color: #FFFFFF; font-weight: 700; }
    .agenda-block.clash { border-left-color: var(--danger); }
    .agenda-tag.clash { color: var(--danger); background: var(--danger-bg); }
    /* Free time: a quiet label in the gap, tappable to fill it. */
    /* A pill in the middle of the gap, not a button filling it: the empty
       ruler around it stays tappable (tap-to-add places a ghost there). */
    .agenda-gap { position: absolute; left: 50%; transform: translate(-50%, -50%); box-sizing: border-box; display: flex; flex-direction: column; align-items: center; gap: 0; border: 0; background: transparent; font: inherit; font-size: 0.6875rem; font-weight: 600; color: var(--text-tertiary); cursor: pointer; z-index: 1; border-radius: 10px; font-variant-numeric: tabular-nums; padding: 4px 10px; white-space: nowrap; }
    .agenda-gap span { font-weight: 500; opacity: 0.8; }
    .agenda-gap:focus-visible { outline: none; box-shadow: var(--focus-ring); }
    @media (hover: hover) and (pointer: fine) { .agenda-gap:hover { background: var(--surface-hover); } }
    /* Now / Next, above today's ruler. */
    .agenda-hero { margin: 2px 0 12px; border-radius: var(--radius-sm); background: var(--accent-bg); border: 1px solid var(--accent-light); overflow: hidden; }
    .agenda-hero-row { display: flex; align-items: center; gap: 10px; padding: 10px 12px; }
    .agenda-hero-row.now { background: var(--surface); border-bottom: 1px solid var(--border); }
    .agenda-hero-label { flex-shrink: 0; font-size: 0.6875rem; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; color: var(--accent); min-width: 34px; }
    .agenda-hero-row.next .agenda-hero-label { color: var(--text-tertiary); }
    .agenda-hero-main { flex: 1; min-width: 0; }
    .agenda-hero-title { font-size: 0.9375rem; font-weight: 600; line-height: 1.3; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
    .agenda-hero-sub { font-size: 0.75rem; color: var(--text-secondary); font-variant-numeric: tabular-nums; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
    .agenda-hero-sub b { color: var(--text); font-weight: 600; }
    .agenda-hero-sub b.over { color: var(--warning); }
    /* Density control: a second chip group in the toggle row. */
    .agenda-density { margin-right: 0; }
    /* .plan-view-toggle is display:flex, which would beat the UA [hidden] rule. */
    .agenda-density[hidden] { display: none; }
    /* Short commutes as connectors: a dashed rail in the gutter and a chip
       beside it; the blocks around keep their full width. */
    .agenda-connector { position: absolute; left: 0; right: 0; z-index: 2; pointer-events: none; }
    .agenda-rail { position: absolute; left: 11px; top: 0; bottom: 0; border-left: 2px dashed var(--type-transit); }
    .agenda-connector.done .agenda-rail { border-left-color: var(--success); }
    .agenda-connector-chip { position: absolute; left: 26px; max-width: calc(100% - 30px); transform: translateY(-50%); pointer-events: auto; display: inline-flex; align-items: center; gap: 6px; padding: 2px 9px 2px 7px; border-radius: 12px; background: var(--surface); border: 1px solid var(--border); box-shadow: 0 1px 3px rgba(0,0,0,0.06); font: inherit; font-size: 0.6875rem; font-weight: 600; color: var(--text-secondary); white-space: nowrap; overflow: hidden; cursor: pointer; line-height: 16px; }
    .agenda-connector-chip .agenda-glyph { color: var(--type-transit); }
    .agenda-connector.done .agenda-connector-chip .agenda-glyph { color: var(--success); }
    .agenda-connector-text { min-width: 0; overflow: hidden; text-overflow: ellipsis; display: inline-flex; align-items: baseline; gap: 4px; }
    .agenda-connector-text .agenda-route { display: inline-flex; }
    .agenda-connector-chip .agenda-tag, .agenda-connector-chip .agenda-next { margin-left: 2px; }
    .agenda-connector-chip:focus-visible { outline: none; box-shadow: var(--focus-ring); }
    /* Ideas from Notes, offered in a free stretch. */
    .agenda-ideas-row { position: absolute; left: 0; right: 0; z-index: 1; }
    .agenda-ideas { display: flex; align-items: center; gap: 6px; overflow-x: auto; scrollbar-width: none; padding: 2px 2px 2px 10px; }
    .agenda-ideas::-webkit-scrollbar { display: none; }
    .agenda-ideas-label { flex-shrink: 0; font-size: 0.6875rem; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; color: var(--accent); }
    .agenda-idea { flex-shrink: 0; display: inline-flex; align-items: center; gap: 4px; padding: 4px 10px; border-radius: 12px; border: 1px solid var(--accent-light); background: var(--accent-bg); font: inherit; font-size: 0.75rem; font-weight: 600; color: var(--accent); cursor: pointer; white-space: nowrap; }
    .agenda-idea svg { width: 12px; height: 12px; }
    .agenda-idea:focus-visible { outline: none; box-shadow: var(--focus-ring); }
    /* The hero's Live button. */
    .agenda-hero { position: relative; }
    .agenda-hero-live { position: absolute; top: 8px; right: 10px; z-index: 1; font: inherit; font-size: 0.6875rem; font-weight: 700; letter-spacing: 0.04em; color: #FFFFFF; background: var(--accent); border: 0; border-radius: 12px; padding: 3px 10px; cursor: pointer; }
    .agenda-hero-live:focus-visible { outline: none; box-shadow: var(--focus-ring); }
    .agenda-hero-row.now .agenda-hero-main { padding-right: 52px; }
    /* Desktop columns: how many days sit side by side (1280px and up). */
    .agenda-cols { display: none; }
    @media (min-width: 1280px) {
      .agenda-cols { display: flex; }
      .agenda-cols[hidden] { display: none; }
      #itineraryOrganized[data-cols="1"] { columns: 1; }
      #itineraryOrganized[data-cols="2"] { columns: 2; column-gap: 20px; }
      #itineraryOrganized[data-cols="3"] { columns: 3; column-gap: 20px; }
      #itineraryOrganized[data-cols] > * { break-inside: avoid; }
    }
    /* Live screen: full height, dark, big type. */
    .modal.live .modal-panel { max-height: 100dvh; height: 100dvh; border-radius: 0; background: #0F0F11; color: #F5F3F8; border: 0; }
    .live-body { padding: 24px 24px calc(24px + env(safe-area-inset-bottom)); display: flex; flex-direction: column; min-height: 100%; }
    .live-top { display: flex; align-items: center; justify-content: space-between; }
    .live-label { font-size: 0.75rem; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; color: #B58CFF; }
    .live-done { font: inherit; font-size: 0.8125rem; color: rgba(255,255,255,0.6); background: transparent; border: 0; cursor: pointer; padding: 6px 0; }
    .live-clock { margin-top: 16px; font-size: 4rem; font-weight: 700; letter-spacing: -0.02em; line-height: 1; font-variant-numeric: tabular-nums; }
    .live-sub { margin-top: 6px; font-size: 0.8125rem; font-weight: 600; color: #7CD66B; }
    .live-sub.behind { color: #F0B06A; }
    .live-card { margin-top: 18px; border-radius: 18px; background: #1B1B20; padding: 16px; }
    .live-eyebrow { font-size: 0.6875rem; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; color: rgba(255,255,255,0.5); }
    .live-card .live-eyebrow { color: #B58CFF; }
    .live-card-title { margin-top: 4px; font-size: 1.5rem; font-weight: 600; line-height: 1.2; }
    .live-card-sub { margin-top: 2px; font-size: 0.8125rem; color: rgba(255,255,255,0.6); }
    .live-card-sub b.over { color: #F0B06A; }
    .live-actions { margin-top: 14px; display: flex; gap: 8px; }
    .live-btn { flex: 1; font: inherit; font-size: 0.875rem; font-weight: 600; padding: 12px; border-radius: 14px; border: 1px solid rgba(255,255,255,0.18); background: transparent; color: #F5F3F8; cursor: pointer; }
    .live-btn.primary { background: #B58CFF; color: #0F0F11; border-color: #B58CFF; font-weight: 700; }
    .live-btn:focus-visible { outline: none; box-shadow: 0 0 0 3px rgba(181,140,255,0.5); }
    .live-card.leave { margin-top: 12px; display: flex; align-items: center; gap: 14px; }
    .live-card.leave .live-eyebrow { color: rgba(255,255,255,0.5); }
    .live-leave-main { flex: 1; min-width: 0; }
    .live-leave-num { font-size: 2.125rem; font-weight: 700; line-height: 1.1; color: #7CD66B; font-variant-numeric: tabular-nums; }
    .live-leave-num.late { color: #F0B06A; }
    .live-leave-ring { flex-shrink: 0; width: 64px; height: 64px; border-radius: 50%; border: 4px solid #7CD66B; display: flex; align-items: center; justify-content: center; color: #7CD66B; }
    .live-leave-ring.late { border-color: #F0B06A; color: #F0B06A; }
    .live-leave-ring svg { width: 22px; height: 22px; }
    .live-item { display: flex; align-items: center; gap: 12px; padding: 12px 0; border-bottom: 1px solid rgba(255,255,255,0.08); }
    .live-item.dim { opacity: 0.55; }
    .live-item .agenda-glyph { color: #B58CFF; }
    .live-item .agenda-glyph[data-type="food"] { color: #E8A85C; } .live-item .agenda-glyph[data-type="transit"] { color: #9A9AAB; } .live-item .agenda-glyph[data-type="outdoors"] { color: #7CD66B; } .live-item .agenda-glyph[data-type="nightlife"] { color: #D77BC0; }
    .live-item-main { flex: 1; min-width: 0; }
    .live-item-title { font-size: 0.9375rem; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
    .live-item-sub { font-size: 0.75rem; color: rgba(255,255,255,0.6); }
    .live-foot { margin-top: auto; padding-top: 24px; font-size: 0.75rem; color: rgba(255,255,255,0.45); text-align: center; }
    /* Untimed tray: chips above the ruler; tap one to open it. */
    .agenda-tray { margin: 2px 0 12px; display: flex; align-items: center; gap: 6px; overflow-x: auto; scrollbar-width: none; }
    .agenda-tray::-webkit-scrollbar { display: none; }
    .agenda-tray-label { flex-shrink: 0; font-size: 0.6875rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.08em; color: var(--text-tertiary); margin-right: 2px; }
    .agenda-tray-chip { flex-shrink: 0; display: inline-flex; align-items: center; gap: 5px; padding: 5px 10px; border-radius: 14px; border: 1px dashed var(--accent-light); background: var(--surface); font: inherit; font-size: 0.75rem; font-weight: 600; color: var(--text); cursor: pointer; max-width: 200px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
    .agenda-tray-chip[data-type="food"] { border-color: var(--type-food); } .agenda-tray-chip[data-type="food"] .agenda-glyph { color: var(--type-food); }
    .agenda-tray-chip[data-type="transit"] { border-color: var(--type-transit); } .agenda-tray-chip[data-type="transit"] .agenda-glyph { color: var(--type-transit); }
    .agenda-tray-chip[data-type="outdoors"] { border-color: var(--type-outdoors); } .agenda-tray-chip[data-type="outdoors"] .agenda-glyph { color: var(--type-outdoors); }
    .agenda-tray-chip[data-type="nightlife"] { border-color: var(--type-nightlife); } .agenda-tray-chip[data-type="nightlife"] .agenda-glyph { color: var(--type-nightlife); }
    .agenda-tray-chip:focus-visible { outline: none; box-shadow: var(--focus-ring); }
    /* Drag handles. The grip (right edge) moves, the bottom edge resizes;
       both are touch-action: none so a finger's drag is ours while the block
       body keeps pan-y for page scrolling. Always visible on touch (no hover
       to reveal them), hover-revealed with a fine pointer. */
    .agenda-grip { position: absolute; top: 0; bottom: 0; right: 0; width: 22px; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px; touch-action: none; cursor: grab; z-index: 3; opacity: 0.55; }
    .agenda-grip i { width: 4px; height: 4px; border-radius: 50%; background: var(--text-tertiary); }
    .agenda-block.compact .agenda-grip { flex-direction: row; gap: 2px; width: 26px; }
    .agenda-resize { position: absolute; left: 24px; right: 24px; bottom: 0; height: 10px; touch-action: none; cursor: ns-resize; z-index: 3; }
    .agenda-resize::after { content: ''; position: absolute; left: 50%; bottom: 2px; width: 28px; height: 3px; margin-left: -14px; border-radius: 2px; background: var(--text-tertiary); opacity: 0.35; }
    .agenda-block.compact .agenda-resize { display: none; }
    @media (hover: hover) and (pointer: fine) {
      .agenda-grip, .agenda-resize::after { opacity: 0; transition: opacity var(--transition); }
      .agenda-block:hover .agenda-grip, .agenda-block:focus-within .agenda-grip { opacity: 0.7; }
      .agenda-block:hover .agenda-resize::after { opacity: 0.5; }
      .agenda-body { cursor: grab; }
    }
    .agenda-block .agenda-head { padding-right: 18px; }
    .agenda-block.lifting { box-shadow: 0 12px 28px rgba(35,0,76,0.22); z-index: 8; opacity: 0.96; transition: none; }
    .agenda-block.lifting .agenda-grip { cursor: grabbing; }
    .agenda-drag-bubbles { position: absolute; inset: 0; pointer-events: none; z-index: 9; }
    .agenda-drag-bubble { position: absolute; left: -56px; width: 48px; transform: translateY(-50%); text-align: center; font-size: 0.6875rem; font-weight: 700; line-height: 1.6; color: #FFFFFF; background: var(--accent); border-radius: 8px; font-variant-numeric: tabular-nums; }
    .agenda-drag-bubble[hidden] { display: none; }
    .agenda-tray-chip { touch-action: none; }
    .agenda-drag-clone { position: fixed; z-index: 100; pointer-events: none; transform: rotate(-2deg); box-shadow: 0 10px 24px rgba(35,0,76,0.22); border-style: solid; border-color: var(--accent); }
    .agenda-ghost.drop { pointer-events: none; }
    /* Tap-to-add ghost: a placement, not an item, until tapped again. */
    .agenda-ghost { position: absolute; left: 0; right: 0; box-sizing: border-box; z-index: 3; border-radius: var(--radius-sm); border: 2px dashed var(--accent-light); background: color-mix(in srgb, var(--accent) 5%, transparent); display: flex; align-items: center; gap: 6px; padding: 0 12px; font: inherit; font-size: 0.8125rem; font-weight: 600; color: var(--accent); cursor: pointer; text-align: left; }
    .agenda-ghost svg { width: 14px; height: 14px; flex-shrink: 0; }
    .agenda-ghost small { margin-left: auto; font-size: 0.6875rem; font-weight: 500; color: var(--text-tertiary); }
    .agenda-ghost:focus-visible { outline: none; box-shadow: var(--focus-ring); }
    /* Day-done card (the hero's evening form). */
    .agenda-hero.done { background: color-mix(in srgb, var(--success) 6%, var(--surface)); border-color: color-mix(in srgb, var(--success) 40%, transparent); }
    .agenda-hero-row.done { background: transparent; border-bottom: 1px solid var(--border); }
    .agenda-hero-row.done .agenda-glyph { color: var(--success); }
    .agenda-hero-time { flex-shrink: 0; font-size: 0.6875rem; font-weight: 700; color: var(--text-secondary); font-variant-numeric: tabular-nums; }
    .agenda-hero-sub b.early { color: var(--success); }
    .agenda-hero-row.skipped { align-items: flex-start; }
    .agenda-skip { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; font-size: 0.8125rem; }
    .agenda-skip + .agenda-skip { margin-top: 6px; }
    .agenda-skip-title { font-weight: 600; }
    .agenda-skip-btn { font: inherit; font-size: 0.6875rem; font-weight: 600; color: var(--accent); background: var(--surface); border: 1px solid var(--accent-light); border-radius: 10px; padding: 2px 8px; cursor: pointer; min-height: 24px; }
    .agenda-skip-btn:focus-visible { outline: none; box-shadow: var(--focus-ring); }
    .agenda-block.flash-highlight { animation: flashHighlight 1.6s ease-out; }
    @media (hover: hover) and (pointer: fine) {
      .agenda-block:hover { box-shadow: var(--shadow-lg); }
    }

    /* ── Packing ──────────────────────────────────────────────────────────
       The slim row. Three things on it — tick it, read it, open it — with
       everything that used to sit alongside the name now behind the kebab.
       The whole row is the tap target for the tick, so the name is plain
       text rather than a contenteditable that would swallow the tap. */
    .pack-item { cursor: pointer; gap: 10px; padding-right: 2px; }
    .pack-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; padding: 3px 0; }
    .pack-name { font-size: 0.875rem; color: var(--text); overflow-wrap: anywhere; }
    .item.done .pack-name { text-decoration: line-through; color: var(--text-tertiary); }
    .pack-meta { font-size: 0.6875rem; font-weight: 500; color: var(--text-tertiary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
    .pack-more { flex-shrink: 0; opacity: 0.55; }
    .item.missing { box-shadow: inset 3px 0 0 var(--danger); padding-left: 8px; }
    .item.missing .pack-name { font-weight: 600; }
    .item.missing .pack-meta { color: var(--danger); }
    .pack-again { flex-shrink: 0; font: inherit; font-size: 0.6875rem; font-weight: 700; color: var(--accent); background: var(--surface); border: 1px solid var(--accent-light); border-radius: 10px; padding: 3px 8px; cursor: pointer; min-height: 26px; }
    .pack-again:focus-visible, .pack-more:focus-visible { outline: none; box-shadow: var(--focus-ring); }
    .pack-empty-line { padding: 12px 2px; font-size: 0.8125rem; color: var(--text-tertiary); }

    /* Tools row + grouping switch. */
    .pack-tools { display: flex; gap: 6px; overflow-x: auto; margin-bottom: 12px; -webkit-overflow-scrolling: touch; scrollbar-width: none; }
    .pack-tools::-webkit-scrollbar { display: none; }
    .pack-tools .chip { flex-shrink: 0; }

    /* The bar used by bag and person cards. */
    .pack-bar { flex-shrink: 0; width: 54px; height: 6px; border-radius: 3px; background: color-mix(in srgb, var(--text) 8%, transparent); overflow: hidden; }
    .pack-bar i { display: block; height: 100%; background: var(--accent); }
    .pack-bar i.done { background: var(--success); }

    /* Bags. */
    .pack-bag-head { display: flex; align-items: center; gap: 10px; margin-bottom: 10px; }
    .pack-bag-icon { display: flex; align-items: center; justify-content: center; width: 34px; height: 34px; border-radius: 11px; background: var(--accent-bg); color: var(--accent); flex-shrink: 0; }
    .pack-bag-icon svg { width: 18px; height: 18px; }
    .pack-bag-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
    .pack-bag-name { font-size: 0.9375rem; font-weight: 600; }
    .pack-bag-sub { font-size: 0.75rem; color: var(--text-tertiary); }
    .pack-bag-warn { display: flex; align-items: center; gap: 6px; font-size: 0.75rem; font-weight: 600; color: var(--warning); margin: -2px 0 10px; }
    .pack-bag-warn svg { flex-shrink: 0; }
    .pack-bag-row { padding: 12px 0; border-bottom: 1px solid var(--border); }
    .pack-bag-row:last-child { border-bottom: 0; }
    .pack-bag-nums { display: flex; align-items: flex-end; gap: 8px; margin-top: 8px; }
    .pack-bag-nums label { flex: 1; min-width: 0; font-size: 0.6875rem; font-weight: 700; color: var(--text-secondary); text-transform: uppercase; letter-spacing: 0.06em; display: flex; flex-direction: column; gap: 4px; }
    .pack-bag-nums .inp { text-align: center; }
    .pack-bag-nums .del-btn { flex-shrink: 0; margin-bottom: 4px; }
    .pack-bag-rowsub { margin-top: 6px; font-size: 0.75rem; color: var(--text-tertiary); }
    .pack-bag-rowsub.over { color: var(--warning); font-weight: 600; }

    /* Per traveller. */
    .pack-split { padding-bottom: 14px; }
    .pack-split-head { display: flex; align-items: baseline; justify-content: space-between; font-size: 0.6875rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.08em; color: var(--text-secondary); }
    .pack-split-pct { font-size: 1.125rem; letter-spacing: -0.01em; color: var(--text); }
    .pack-split-bar { display: flex; gap: 3px; height: 10px; margin-top: 10px; }
    .pack-split-seg { border-radius: 5px; background: color-mix(in srgb, var(--text) 8%, transparent); overflow: hidden; min-width: 6px; }
    .pack-split-seg i { display: block; height: 100%; background: var(--accent); }
    .pack-split-legend { display: flex; flex-wrap: wrap; gap: 4px 14px; margin-top: 10px; font-size: 0.75rem; color: var(--text-tertiary); }
    .pack-split-key b { color: var(--text); font-variant-numeric: tabular-nums; }
    .pack-person-head { display: flex; align-items: center; gap: 12px; margin-bottom: 10px; }
    .pack-ring { flex-shrink: 0; width: 44px; height: 44px; border-radius: 50%; display: flex; align-items: center; justify-content: center; background: conic-gradient(var(--accent) calc(var(--pct) * 1%), color-mix(in srgb, var(--text) 10%, transparent) 0); }
    .pack-ring b { display: flex; align-items: center; justify-content: center; width: 34px; height: 34px; border-radius: 50%; background: var(--surface); font-size: 0.6875rem; font-weight: 700; font-variant-numeric: tabular-nums; }
    .pack-ring b span { font-size: 0.5625rem; font-weight: 600; color: var(--text-tertiary); }
    .pack-person-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
    .pack-person-name { font-size: 0.9375rem; font-weight: 600; }
    .pack-person-sub { font-size: 0.75rem; color: var(--text-tertiary); }
    .pack-behind { flex-shrink: 0; font-size: 0.625rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em; color: var(--warning); background: color-mix(in srgb, var(--warning) 12%, transparent); border-radius: 10px; padding: 3px 8px; }

    /* The hero card: departure, coming home, repacking. */
    .pack-hero { padding: 16px; margin-bottom: 12px; }
    .pack-hero-eyebrow { font-size: 0.6875rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.08em; color: var(--accent); }
    .pack-hero-count { margin-top: 4px; font-size: 1.375rem; font-weight: 700; letter-spacing: -0.02em; line-height: 1.2; }
    .pack-hero-count .pack-missing { color: var(--danger); }
    .pack-hero-sub { margin-top: 4px; font-size: 0.8125rem; color: var(--text-secondary); }
    .pack-hero-btn, .pack-hero-row { margin-top: 12px; }
    .pack-hero-row { display: flex; gap: 8px; flex-wrap: wrap; }
    .pack-hero-row .btn { flex: 1; min-width: 120px; }
    .pack-hero.repacking { background: color-mix(in srgb, var(--accent) 5%, var(--surface)); }

    /* Suggestions from the itinerary. */
    .pack-suggest-top { display: flex; align-items: center; gap: 10px; padding: 12px 14px; margin-bottom: 8px; }
    .pack-suggest { padding: 14px; margin-bottom: 8px; }
    .pack-suggest-head { display: flex; align-items: center; gap: 10px; margin-bottom: 10px; }
    .pack-suggest-icon { display: flex; align-items: center; justify-content: center; color: var(--accent); flex-shrink: 0; }
    .pack-suggest-icon svg { width: 18px; height: 18px; }
    .pack-suggest-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
    .pack-suggest-title { font-size: 0.9375rem; font-weight: 600; }
    .pack-suggest-sub { font-size: 0.75rem; color: var(--text-tertiary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .pack-suggest-toggle { flex-shrink: 0; font: inherit; font-size: 0.8125rem; font-weight: 600; color: var(--accent); background: transparent; border: 0; padding: 6px 2px; cursor: pointer; }
    .pack-suggest-toggle:focus-visible, .pack-add-chip:focus-visible { outline: none; box-shadow: var(--focus-ring); }
    .pack-suggest-top.open { margin-bottom: 10px; }
    .pack-suggest-foot { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 12px; }
    .pack-suggest-foot .btn { flex: 1; min-width: 130px; }
    .pack-suggest-chips { display: flex; flex-wrap: wrap; gap: 6px; }
    .pack-add-chip { display: inline-flex; align-items: center; gap: 5px; font: inherit; font-size: 0.8125rem; font-weight: 600; color: var(--accent); background: var(--surface); border: 1px solid var(--accent-light); border-radius: 16px; padding: 7px 12px; cursor: pointer; min-height: 34px; }
    .pack-add-chip svg { width: 13px; height: 13px; flex-shrink: 0; }

    /* Save as template. */
    .pack-tpl-sum { font-size: 0.8125rem; font-weight: 600; color: var(--text-secondary); margin-bottom: 10px; }
    .pack-tpl-trim { display: flex; align-items: flex-start; gap: 8px; font-size: 0.875rem; cursor: pointer; }
    .pack-tpl-trim input { margin-top: 2px; flex-shrink: 0; }
    .pack-tpl-list { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 10px; }
    .pack-tpl-chip { font-size: 0.75rem; font-weight: 600; color: var(--text-secondary); background: var(--surface-hover); border-radius: 10px; padding: 3px 8px; }
    .pack-tpl-chip.out { text-decoration: line-through; color: var(--text-tertiary); }

    /* Paste a list. 1rem, not smaller: anything a finger types into must
       clear the 16px floor or iOS zooms the page on focus. */
    .pack-quick-input { width: 100%; box-sizing: border-box; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 1rem; line-height: 1.6; resize: vertical; background: var(--bg); border: 1px solid var(--border); border-radius: var(--radius-md); padding: 10px; color: var(--text); outline: none; }
    .pack-quick-input:focus { border-color: var(--border-strong); }
    .pack-quick-preview:not(:empty) { margin-top: 12px; }
    .pack-quick-label { font-size: 0.6875rem; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; color: var(--text-tertiary); margin-bottom: 4px; }
    .pack-quick-row { display: flex; align-items: center; gap: 8px; padding: 8px 0; border-bottom: 1px solid var(--border); font-size: 0.8125rem; }
    .pack-quick-row:last-child { border-bottom: 0; }
    .pack-quick-tick { display: flex; color: var(--success); flex-shrink: 0; }
    .pack-quick-row.bad .pack-quick-tick { color: var(--warning); }
    .pack-quick-main { flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
    .pack-quick-warn { color: var(--warning); font-weight: 600; }
    .pack-quick-qty { flex-shrink: 0; font-size: 0.75rem; font-weight: 600; color: var(--text-secondary); }
    .pack-quick-cat { flex-shrink: 0; font-size: 0.6875rem; font-weight: 600; color: var(--accent); background: var(--accent-bg); border-radius: 10px; padding: 2px 8px; }

    /* The item sheet's own controls — full size, because there is room here. */
    .pack-sheet-qty { display: flex; align-items: center; gap: 10px; }
    .pack-qty-btn { width: 44px; height: 44px; border-radius: 14px; border: 1px solid var(--border); background: var(--surface); color: var(--text); font: inherit; font-size: 1.25rem; font-weight: 600; cursor: pointer; display: flex; align-items: center; justify-content: center; }
    .pack-qty-btn:disabled { opacity: 0.35; cursor: default; }
    .pack-qty-btn:focus-visible, .pack-sheet-link:focus-visible { outline: none; box-shadow: var(--focus-ring); }
    .pack-qty-val { min-width: 40px; text-align: center; font-size: 1.125rem; font-weight: 700; font-variant-numeric: tabular-nums; }
    .pack-sheet-link { margin-top: 6px; font: inherit; font-size: 0.8125rem; font-weight: 600; color: var(--accent); background: transparent; border: 0; padding: 4px 0; cursor: pointer; }

    /* Departure mode — dark, big taps, read-mostly. */
    .modal.pack-go .modal-panel { max-height: 100dvh; height: 100dvh; border-radius: 0; background: #0F0F11; color: #F5F3F8; border: 0; }
    .go-body { padding: 28px 24px calc(24px + env(safe-area-inset-bottom)); display: flex; flex-direction: column; min-height: 100%; }
    .go-top { display: flex; align-items: center; justify-content: space-between; }
    .go-label { font-size: 0.75rem; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; color: #B58CFF; }
    .go-done { font: inherit; font-size: 0.8125rem; color: rgba(255,255,255,0.6); background: transparent; border: 0; cursor: pointer; padding: 6px 0; }
    .go-count { margin-top: 16px; font-size: 2.125rem; font-weight: 700; letter-spacing: -0.02em; line-height: 1.1; }
    .go-sub { margin-top: 4px; font-size: 0.8125rem; color: rgba(255,255,255,0.6); }
    .go-chips { margin-top: 18px; display: flex; gap: 8px; flex-wrap: wrap; }
    .go-chip { font: inherit; font-size: 0.75rem; font-weight: 600; padding: 7px 12px; border-radius: 14px; border: 1px solid rgba(255,255,255,0.18); background: transparent; color: #F5F3F8; cursor: pointer; }
    .go-chip.on { background: #B58CFF; color: #0F0F11; border-color: #B58CFF; font-weight: 700; }
    .go-chip:focus-visible, .go-tick:focus-visible, .go-done:focus-visible { outline: none; box-shadow: 0 0 0 3px rgba(181,140,255,0.5); }
    .go-list { margin-top: 10px; }
    .go-item { display: flex; align-items: center; gap: 12px; padding: 14px 0; border-bottom: 1px solid rgba(255,255,255,0.08); }
    .go-item.done { opacity: 0.45; }
    .go-item.done .go-item-title { text-decoration: line-through; }
    .go-tick { flex-shrink: 0; width: 30px; height: 30px; border-radius: 9px; border: 2px solid rgba(255,255,255,0.3); background: transparent; color: #0F0F11; display: flex; align-items: center; justify-content: center; cursor: pointer; padding: 0; }
    .go-tick.on { background: #7CD66B; border-color: #7CD66B; }
    .go-main { flex: 1; min-width: 0; }
    .go-item-title { font-size: 1.0625rem; font-weight: 600; }
    .go-qty { color: #B58CFF; }
    .go-item-sub { font-size: 0.8125rem; color: rgba(255,255,255,0.55); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
    .go-flag { flex-shrink: 0; font-size: 0.6875rem; font-weight: 700; color: #0F0F11; background: #F0B06A; border-radius: 10px; padding: 4px 8px; }
    .go-empty { padding: 24px 0; font-size: 0.875rem; color: rgba(255,255,255,0.5); }
    .go-foot { margin-top: auto; padding-top: 16px; font-size: 0.75rem; color: rgba(255,255,255,0.45); text-align: center; }

    @media print {
      .pack-tools, .pack-more, .pack-again, .pack-suggest-foot,
      .pack-suggest-top, .pack-suggest, .pack-hero { display: none !important; }
    }

    /* ── Notes ────────────────────────────────────────────────────────────
       Three things changed on the card: the body is clamped (a note that
       printed eight lines of door codes used to be taller than the four
       notes around it), the content is rendered rather than dumped, and the
       three footer buttons folded into one menu. */
    .note-content.rich { white-space: normal; }
    .note-content.rich p { margin: 0 0 5px; }
    .note-content.rich p:last-child { margin-bottom: 0; }
    .note-content.rich .note-gap { height: 5px; }
    .note-content.rich code { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 0.78125rem; background: var(--surface-hover); border-radius: 5px; padding: 1px 5px; color: var(--text); }
    .note-content.rich b { color: var(--text); font-weight: 600; }
    .note-content.rich a { color: var(--accent); font-weight: 600; text-decoration: underline; overflow-wrap: anywhere; }
    .note-h1, .note-h2, .note-h3 { font-size: 0.6875rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em; color: var(--text-tertiary); margin: 8px 0 3px; }
    .note-h1:first-child, .note-h2:first-child, .note-h3:first-child { margin-top: 0; }
    .note-list { margin: 0 0 5px; padding-left: 17px; }
    .note-list li { margin-bottom: 2px; }
    .note-callout { border-left: 3px solid var(--warning); background: color-mix(in srgb, var(--warning) 8%, transparent); border-radius: 0 8px 8px 0; padding: 7px 10px; margin: 6px 0; color: var(--text); }
    .note-task { display: flex; align-items: flex-start; gap: 9px; padding: 5px 0; }
    .note-task span { flex: 1; min-width: 0; }
    .note-task.done span { text-decoration: line-through; color: var(--text-tertiary); }
    .note-task-box { flex-shrink: 0; margin-top: 1px; width: 20px; height: 20px; border-radius: 7px; border: 2px solid var(--border-strong); background: transparent; color: #fff; display: flex; align-items: center; justify-content: center; cursor: pointer; padding: 0; }
    .note-task-box.on { background: var(--success); border-color: var(--success); }
    .note-task-box:focus-visible { outline: none; box-shadow: var(--focus-ring); }
    /* Four rendered lines, then a fade — the "More" button is the way down,
       not a scrollbar inside a card. */
    .note-content.clamped { position: relative; max-height: 5.2em; overflow: hidden; }
    .note-content.clamped::after { content: ''; position: absolute; left: 0; right: 0; bottom: 0; height: 1.6em; background: linear-gradient(to bottom, transparent, var(--surface)); }
    .note-more { font: inherit; font-size: 0.6875rem; font-weight: 700; color: var(--accent); background: transparent; border: 0; padding: 4px 6px; cursor: pointer; }
    .note-more:focus-visible { outline: none; box-shadow: var(--focus-ring); }
    .note-foot-meta { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .note-foot .note-menu { margin-left: auto; }
    .note-pinned { display: flex; align-items: center; color: var(--accent); flex-shrink: 0; margin-top: 1px; }
    .note-progress { flex-shrink: 0; font-size: 0.6875rem; font-weight: 700; color: var(--text-tertiary); background: var(--surface-hover); border-radius: 12px; padding: 2px 8px; font-variant-numeric: tabular-nums; }
    .note-filed { display: flex; flex-wrap: wrap; gap: 5px; margin-bottom: 7px; }
    .note-filed span { font-size: 0.6875rem; font-weight: 600; color: var(--text-secondary); background: var(--bg); border: 1px solid var(--border); border-radius: 10px; padding: 2px 8px; }
    .note-day-label { display: inline-flex; align-items: center; gap: 5px; text-transform: none; letter-spacing: 0.01em; font-size: 0.75rem; color: var(--accent); background: var(--accent-bg); border-color: color-mix(in srgb, var(--accent) 20%, transparent) !important; }
    .note-day-label svg { width: 12px; height: 12px; flex-shrink: 0; }

    /* Search and the view switch. */
    .note-search { display: flex; align-items: center; gap: 8px; padding: 8px 12px; border-radius: var(--radius-lg); border: 1px solid var(--border); background: var(--surface); margin-bottom: 10px; }
    .note-search:focus-within { border-color: var(--accent); }
    .note-search-icon { display: flex; color: var(--text-tertiary); flex-shrink: 0; }
    .note-search:focus-within .note-search-icon { color: var(--accent); }
    /* 1rem, not smaller: anything a finger types into must clear the 16px
       floor or iOS zooms the page on focus (the suite guards this). */
    .note-search-input { flex: 1; min-width: 0; font: inherit; font-size: 1rem; background: transparent; border: 0; outline: none; color: var(--text); padding: 2px 0; }
    .note-search-input::-webkit-search-cancel-button { display: none; }
    .note-search-count { flex-shrink: 0; font-size: 0.6875rem; font-weight: 700; color: var(--text-tertiary); font-variant-numeric: tabular-nums; }
    .note-search-clear { flex-shrink: 0; display: flex; align-items: center; justify-content: center; width: 26px; height: 26px; border-radius: 50%; border: 0; background: var(--surface-hover); color: var(--text-secondary); cursor: pointer; padding: 0; }
    .note-search-clear:focus-visible { outline: none; box-shadow: var(--focus-ring); }

    /* Pinned values. */
    .note-pins-head { display: flex; align-items: center; gap: 6px; font-size: 0.6875rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.08em; color: var(--text-tertiary); margin-bottom: 8px; }
    .note-pins-head svg { color: var(--accent); }
    .note-pins { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 14px; }
    .note-pin { flex: 1 1 44%; min-width: 0; box-sizing: border-box; background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg); padding: 9px 11px; box-shadow: var(--shadow); }
    .note-pin.wide { flex-basis: 100%; }
    .note-pin-cap { font-size: 0.625rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em; color: var(--text-tertiary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
    .note-pin-main { display: flex; align-items: center; gap: 8px; margin-top: 4px; }
    .note-pin-val { flex: 1; min-width: 0; text-align: left; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 0.9375rem; font-weight: 600; color: var(--text); background: transparent; border: 0; padding: 0; cursor: pointer; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
    .note-pin-copy { flex-shrink: 0; display: flex; align-items: center; justify-content: center; width: 30px; height: 30px; border-radius: 10px; border: 1px solid var(--accent-light); background: transparent; color: var(--accent); cursor: pointer; padding: 0; }
    .note-pin-val:focus-visible, .note-pin-copy:focus-visible { outline: none; box-shadow: var(--focus-ring); }
    .note-pin-row { display: flex; align-items: center; gap: 10px; padding: 10px 0; border-bottom: 1px solid var(--border); cursor: pointer; }
    .note-pin-row:last-child { border-bottom: 0; }
    .note-pin-row input { flex-shrink: 0; }
    .note-pin-label { flex-shrink: 0; max-width: 42%; font-size: 0.6875rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.05em; color: var(--text-tertiary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
    .note-pin-value { flex: 1; min-width: 0; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 0.875rem; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

    /* Quick capture. */
    .quick-note { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg); padding: 12px; box-shadow: var(--shadow); margin-bottom: 8px; }
    .quick-note:focus-within { border-color: var(--accent); }
    .quick-note-input { width: 100%; box-sizing: border-box; font: inherit; font-size: 1rem; line-height: 1.45; background: transparent; border: 0; outline: none; resize: none; color: var(--text); min-height: 24px; }
    .quick-note-row { display: flex; align-items: center; gap: 8px; margin-top: 10px; padding-top: 9px; border-top: 1px solid var(--border); }
    .quick-tag { font: inherit; font-size: 0.6875rem; font-weight: 700; border-radius: 12px; border: 1px solid; padding: 3px 9px; cursor: pointer; white-space: nowrap; }
    .quick-guessed { font-size: 0.6875rem; color: var(--text-tertiary); white-space: nowrap; }
    .quick-icon { display: flex; align-items: center; justify-content: center; width: 32px; height: 32px; border-radius: 50%; border: 0; background: transparent; color: var(--text-tertiary); cursor: pointer; padding: 0; }
    .quick-icon:nth-of-type(1) { margin-left: auto; }
    .quick-save { flex-shrink: 0; padding: 8px 16px; font-size: 0.8125rem; }
    .quick-save:disabled { opacity: 0.4; }
    .quick-tag:focus-visible, .quick-icon:focus-visible { outline: none; box-shadow: var(--focus-ring); }
    .quick-links { display: flex; gap: 6px; overflow-x: auto; margin-bottom: 14px; scrollbar-width: none; }
    .quick-links::-webkit-scrollbar { display: none; }
    .quick-links .chip { flex-shrink: 0; }

    /* Paste a booking, and import. */
    .note-paste-input { width: 100%; box-sizing: border-box; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 1rem; line-height: 1.55; resize: vertical; background: var(--bg); border: 1px solid var(--border); border-radius: var(--radius-md); padding: 10px; color: var(--text); outline: none; }
    .note-paste-input:focus { border-color: var(--border-strong); }
    .note-paste-preview:not(:empty) { margin-top: 12px; }
    .note-paste-head { font-size: 0.6875rem; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; color: var(--text-tertiary); margin-bottom: 4px; }
    .note-paste-row { display: flex; align-items: center; gap: 10px; padding: 8px 0; border-bottom: 1px solid var(--border); font-size: 0.8125rem; }
    .note-paste-row:last-of-type { border-bottom: 0; }
    .note-paste-key { flex-shrink: 0; width: 76px; font-size: 0.625rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.05em; color: var(--text-tertiary); }
    .note-paste-val { flex: 1; min-width: 0; font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
    .note-paste-row.miss .note-paste-val { color: var(--text-tertiary); font-weight: 400; }
    .note-paste-tick { flex-shrink: 0; display: flex; color: var(--success); }
    .note-paste-row.miss .note-paste-tick { color: var(--text-tertiary); }
    .note-import-row { display: flex; align-items: center; gap: 8px; padding: 8px 0; border-bottom: 1px solid var(--border); font-size: 0.8125rem; }
    .note-import-row:last-of-type { border-bottom: 0; }
    .note-import-title { flex: 1; min-width: 0; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
    .note-import-tag { flex-shrink: 0; font-size: 0.6875rem; font-weight: 600; color: var(--accent); background: var(--accent-bg); border-radius: 10px; padding: 2px 8px; }
    .note-import-lines { flex-shrink: 0; font-size: 0.6875rem; color: var(--text-tertiary); }

    /* Share. */
    .note-share-row { display: flex; align-items: center; gap: 10px; width: 100%; text-align: left; font: inherit; background: transparent; border: 0; border-bottom: 1px solid var(--border); padding: 11px 0; cursor: pointer; color: var(--text); }
    .note-share-row:focus-visible { outline: none; box-shadow: var(--focus-ring); }
    .note-share-dot { flex-shrink: 0; width: 18px; height: 18px; border-radius: 50%; border: 2px solid var(--border-strong); display: flex; align-items: center; justify-content: center; }
    .note-share-row.on .note-share-dot { border-color: var(--accent); }
    .note-share-row.on .note-share-dot::after { content: ''; width: 8px; height: 8px; border-radius: 50%; background: var(--accent); }
    .note-share-label { flex: 1; min-width: 0; font-size: 0.875rem; }
    .note-share-row.on .note-share-label { font-weight: 600; }
    .note-share-meta { flex-shrink: 0; font-size: 0.6875rem; color: var(--text-tertiary); max-width: 40%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
    .note-share-redact { display: flex; align-items: center; gap: 8px; margin-top: 12px; font-size: 0.8125rem; color: var(--text-secondary); cursor: pointer; }
    .note-share-preview { margin-top: 12px; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 0.71875rem; line-height: 1.7; color: var(--text-secondary); background: var(--bg); border: 1px solid var(--border); border-radius: var(--radius-md); padding: 12px; white-space: pre-wrap; max-height: 240px; overflow-y: auto; }
    .modal-sub { display: block; font-size: 0.75rem; font-weight: 500; color: var(--text-tertiary); margin-top: 2px; }

    /* The Plan's side of "notes belong to days". */
    .day-notes-pill { display: inline-flex; align-items: center; gap: 3px; flex-shrink: 0; font: inherit; font-size: 0.6875rem; font-weight: 700; color: var(--accent); background: var(--accent-bg); border: 1px solid color-mix(in srgb, var(--accent) 20%, transparent); border-radius: 12px; padding: 2px 7px; cursor: pointer; min-height: 24px; }
    .day-notes-pill svg { width: 12px; height: 12px; }
    .day-notes-pill:focus-visible { outline: none; box-shadow: var(--focus-ring); }

    /* Show it at the counter — dark, big, read-only. */
    .modal.note-counter .modal-panel { max-height: 100dvh; height: 100dvh; border-radius: 0; background: #0F0F11; color: #F5F3F8; border: 0; }
    .ctr-body { padding: 28px 24px calc(24px + env(safe-area-inset-bottom)); display: flex; flex-direction: column; min-height: 100%; }
    .ctr-top { display: flex; align-items: center; justify-content: space-between; }
    .ctr-label { font-size: 0.75rem; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; color: #B58CFF; }
    .ctr-done { font: inherit; font-size: 0.8125rem; color: rgba(255,255,255,0.6); background: transparent; border: 0; cursor: pointer; padding: 6px 0; }
    .ctr-title { margin-top: 16px; font-size: 1.375rem; font-weight: 700; letter-spacing: -0.02em; line-height: 1.2; }
    .ctr-sub { margin-top: 4px; font-size: 0.8125rem; color: rgba(255,255,255,0.6); }
    .ctr-cap { margin-top: 24px; font-size: 0.6875rem; font-weight: 700; letter-spacing: 0.1em; text-transform: uppercase; color: rgba(255,255,255,0.45); }
    .ctr-main { margin-top: 8px; display: flex; align-items: center; gap: 12px; }
    .ctr-value { flex: 1; min-width: 0; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 2.25rem; font-weight: 700; letter-spacing: 0.04em; line-height: 1.15; overflow-wrap: anywhere; }
    .ctr-copy { flex-shrink: 0; display: flex; align-items: center; justify-content: center; width: 46px; height: 46px; border-radius: 15px; border: 0; background: #B58CFF; color: #0F0F11; cursor: pointer; padding: 0; }
    .ctr-tiles { margin-top: 18px; display: flex; flex-wrap: wrap; gap: 8px; }
    .ctr-tile { flex: 1 1 44%; min-width: 0; box-sizing: border-box; text-align: left; font: inherit; background: rgba(255,255,255,0.06); border: 0; border-radius: 14px; padding: 10px 12px; color: #F5F3F8; cursor: pointer; }
    .ctr-tile-cap { display: block; font-size: 0.625rem; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; color: rgba(255,255,255,0.45); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
    .ctr-tile-val { display: block; margin-top: 3px; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 1rem; font-weight: 700; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
    .ctr-photo { flex: 1; min-height: 140px; margin-top: 20px; border-radius: 18px; overflow: hidden; background: rgba(255,255,255,0.04); border: 1px solid rgba(255,255,255,0.08); display: flex; align-items: center; justify-content: center; }
    .ctr-photo img { width: 100%; height: 100%; object-fit: contain; cursor: pointer; }
    .ctr-photo-empty { display: flex; flex-direction: column; align-items: center; gap: 8px; color: rgba(255,255,255,0.35); font-size: 0.8125rem; }
    .ctr-photo-empty svg { width: 28px; height: 28px; }
    .ctr-foot-row { display: flex; gap: 10px; padding-top: 18px; }
    .ctr-btn { flex: 1; font: inherit; font-size: 0.9375rem; font-weight: 700; color: #0F0F11; background: #B58CFF; border: 0; border-radius: 16px; padding: 14px; cursor: pointer; }
    .ctr-btn.ghost { flex: 0 0 52px; display: flex; align-items: center; justify-content: center; background: transparent; border: 1px solid rgba(255,255,255,0.18); color: #F5F3F8; padding: 14px 0; }
    .ctr-copy:focus-visible, .ctr-tile:focus-visible, .ctr-btn:focus-visible, .ctr-done:focus-visible { outline: none; box-shadow: 0 0 0 3px rgba(181,140,255,0.5); }
    .ctr-foot { margin-top: 10px; font-size: 0.75rem; color: rgba(255,255,255,0.45); text-align: center; }

    @media print {
      .quick-note, .quick-links, .note-search, .note-more, .note-menu,
      .note-pins, .note-pins-head, #notesViewChips { display: none !important; }
      .note-content.clamped { max-height: none; }
      .note-content.clamped::after { display: none; }
    }

    /* The composer at rest: one line. It unfolds into the writing surface
       only when tapped, so the list is not pushed off the screen by a form
       nobody has asked for yet. */
    .quick-shut { display: flex; align-items: center; gap: 10px; width: 100%; text-align: left; font: inherit; font-size: 0.875rem; color: var(--text-tertiary); background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg); padding: 11px 14px; margin-bottom: 12px; cursor: pointer; box-shadow: var(--shadow); }
    .quick-shut-icon { display: flex; align-items: center; justify-content: center; color: var(--accent); flex-shrink: 0; }
    .quick-shut-icon svg { width: 16px; height: 16px; }
    .quick-shut:focus-visible { outline: none; box-shadow: var(--focus-ring); }
    /* With no photo the counter still needs its actions at the bottom. */
    .ctr-spacer { flex: 1; min-height: 20px; }

    /* ══ Desktop ═══════════════════════════════════════════════════════════
       The phone layout is the default and nothing below 1024 is touched.
       1024 buys the labelled header, the status bar, the rail's extras and
       the desktop-only affordances; 1280 additionally buys the right-hand
       pane, because a 300px column and a readable measure need 1280 between
       them. The two-column newspaper flow that used to kick in at 1440 is
       gone: reading order ran down then across, so the next day was at the
       top of the far column, and one expanded day unbalanced the whole page. */

    /* Anything that only makes sense with a window. */
    .wide-only { display: none !important; }
    .btn-label { display: none; }
    .dock-count { display: none; }
    .status-bar { display: none; }
    .bulk-bar { display: none; }
    .header-new { display: none; }
    .trip-aside[hidden] { display: none !important; }

    @media (min-width: 1024px) {
      .wide-only { display: inline-flex !important; }

      /* Labels instead of four unlabelled circles. */
      .icon-btn.labelled { width: auto; min-width: 0; border-radius: var(--radius-md); padding: 0 12px; gap: 7px; }
      .btn-label { display: inline; font-size: 0.8125rem; font-weight: 600; }
      .header-new { display: inline-flex; align-items: center; gap: 6px; padding: 8px 14px; font-size: 0.8125rem; }

      /* The floating plus is a phone idiom; the header carries it here. */
      .fab { display: none !important; }

      /* The rail earns its width. */
      .dock { width: 240px; }
      .rail-block { display: flex; flex-direction: column; gap: 6px; padding: 0 2px 6px; }
      .rail-bottom { margin-top: auto; padding-top: 12px; gap: 4px; }
      .rail-cap { font-size: 0.625rem; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; color: var(--text-tertiary); padding: 2px 12px 0; }
      .rail-trip { display: flex; flex-direction: column; gap: 1px; text-align: left; font: inherit; background: var(--bg); border: 1px solid var(--border); border-radius: var(--radius-md); padding: 8px 11px; cursor: pointer; color: var(--text); }
      .rail-trip-cap { font-size: 0.625rem; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; color: var(--text-tertiary); }
      .rail-trip-row { display: flex; align-items: center; gap: 6px; min-width: 0; }
      .rail-trip-name { flex: 1; min-width: 0; font-size: 0.8125rem; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
      .rail-trip-row svg { flex-shrink: 0; color: var(--text-tertiary); }
      .rail-trip-sub { font-size: 0.6875rem; color: var(--text-tertiary); }
      .rail-search { display: flex; align-items: center; gap: 8px; text-align: left; font: inherit; font-size: 0.8125rem; color: var(--text-tertiary); background: transparent; border: 1px solid var(--border); border-radius: var(--radius-md); padding: 7px 11px; cursor: pointer; }
      .rail-search span { flex: 1; }
      .rail-search kbd, .rail-kbd { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 0.625rem; border: 1px solid var(--border); border-radius: 4px; padding: 1px 5px; background: var(--bg); color: var(--text-tertiary); }
      .rail-trip:focus-visible, .rail-search:focus-visible { outline: none; box-shadow: var(--focus-ring); }
      .rail-today { background: var(--bg); border: 1px solid var(--border); border-radius: var(--radius-md); padding: 9px 11px; margin-bottom: 6px; }
      .rail-today .rail-cap { padding: 0; }
      .rail-today-city { font-size: 0.8125rem; font-weight: 600; margin-top: 3px; }
      .rail-today-lines { margin-top: 4px; font-size: 0.6875rem; color: var(--text-secondary); line-height: 1.65; }
      .rail-today-lines .warn { color: var(--danger); font-weight: 600; }
      .rail-foot { display: flex; align-items: center; gap: 6px; font-size: 0.6875rem; color: var(--text-tertiary); padding: 2px 4px; }
      .rail-foot svg { flex-shrink: 0; }
      .dock-count { display: block; margin-left: auto; font-size: 0.6875rem; font-weight: 700; color: var(--text-tertiary); flex-shrink: 0; }
      .dock-btn.on .dock-count { color: color-mix(in srgb, var(--surface) 75%, transparent); }

      /* The status bar: counts, where you are, whether it is saved. */
      .status-bar { display: flex; align-items: center; gap: 18px; height: 30px; flex-shrink: 0; grid-column: 1 / -1; background: var(--surface); border-top: 1px solid var(--border); padding: 0 20px; font-size: 0.6875rem; color: var(--text-tertiary); }
      .status-bar .status-sync { display: inline-flex; align-items: center; gap: 5px; }
      .status-bar .status-right { margin-left: auto; display: inline-flex; align-items: center; gap: 5px; }
      .app { grid-template-rows: auto minmax(0, 1fr) auto; }

      /* Controls keep their own width: a search field 1100px wide is not a
         better search field. */
      #notesSearch .note-search, #packQuickHint, .quick-note, .quick-shut { max-width: 560px; }
      #templatesBar { max-width: 620px; }
      .notes-toolbar .filter-bar { max-width: 320px; }
      .budget-toolbar { max-width: 720px; }
    }

    /* ── The right-hand pane ─────────────────────────────────────────────── */
    @media (min-width: 1280px) {
      .trip-layout { display: flex; align-items: flex-start; gap: 20px; }
      .trip-main { flex: 1; min-width: 0; max-width: 780px; }
      .trip-aside { width: 300px; flex-shrink: 0; position: sticky; top: 0; }
      .aside-card { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg); padding: 12px 14px; margin-bottom: 12px; box-shadow: var(--shadow); }
      .aside-cap { font-size: 0.625rem; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; color: var(--text-tertiary); }
      .aside-stays { margin-top: 6px; }
      .aside-stay { display: flex; align-items: center; gap: 10px; padding: 4px 0; font-size: 0.75rem; }
      .aside-stay span { width: 84px; flex-shrink: 0; color: var(--text-tertiary); }
      .aside-stay b { flex: 1; min-width: 0; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
      .aside-big { display: flex; align-items: baseline; gap: 8px; margin-top: 4px; font-size: 1.375rem; font-weight: 700; letter-spacing: -0.02em; }
      .aside-big span { font-size: 0.75rem; font-weight: 500; color: var(--text-tertiary); letter-spacing: 0; }
      .aside-bars { display: flex; gap: 3px; margin-top: 9px; }
      .aside-bars span { height: 7px; border-radius: 4px; background: var(--accent); opacity: 0.75; min-width: 4px; }
      .aside-bars span.peak { background: var(--warning); opacity: 1; }
      .aside-warn { margin-top: 7px; font-size: 0.6875rem; font-weight: 600; color: var(--warning); }
      .aside-attn { margin-top: 6px; }
      .aside-attn-row { display: flex; align-items: flex-start; gap: 8px; padding: 4px 0; font-size: 0.75rem; }
      .aside-attn-row .dot { flex-shrink: 0; width: 7px; height: 7px; border-radius: 50%; margin-top: 5px; background: var(--warning); }
      .aside-attn-row.bad .dot { background: var(--danger); }
      .aside-track { height: 7px; border-radius: 4px; background: color-mix(in srgb, var(--text) 8%, transparent); margin-top: 9px; overflow: hidden; }
      .aside-track i { display: block; height: 100%; background: var(--accent); }

      /* The same pane, showing the selected item instead of the summary. */
      .aside-detail { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg); padding: 14px; box-shadow: var(--shadow); }
      .aside-detail-top { display: flex; align-items: flex-start; gap: 8px; }
      .aside-detail-top .aside-cap { flex: 1; min-width: 0; }
      .aside-close { flex-shrink: 0; display: flex; align-items: center; justify-content: center; width: 24px; height: 24px; border-radius: 50%; border: 0; background: transparent; color: var(--text-tertiary); cursor: pointer; padding: 0; }
      .aside-close:focus-visible { outline: none; box-shadow: var(--focus-ring); }
      .aside-detail-title { margin-top: 3px; font-size: 1.0625rem; font-weight: 700; letter-spacing: -0.01em; line-height: 1.25; }
      .aside-rows { margin-top: 10px; }
      .aside-row { display: flex; align-items: baseline; gap: 10px; padding: 5px 0; border-bottom: 1px solid var(--border); font-size: 0.75rem; }
      .aside-row:last-child { border-bottom: 0; }
      .aside-row span { width: 58px; flex-shrink: 0; color: var(--text-tertiary); }
      .aside-row b { flex: 1; min-width: 0; font-weight: 600; overflow-wrap: anywhere; }
      .aside-desc { margin-top: 10px; font-size: 0.75rem; color: var(--text-secondary); line-height: 1.5; }
      .aside-actions { display: flex; gap: 8px; margin-top: 12px; }
      .aside-actions .btn { flex: 1; padding: 8px 10px; font-size: 0.75rem; }
      .aside-hint { margin-top: 9px; font-size: 0.625rem; color: var(--text-tertiary); text-align: center; }

      /* The selected card says so on the list side too. */
      .activity.is-selected, .agenda-block.is-selected, .week-block.is-selected { box-shadow: 0 0 0 2px var(--accent); }
    }

    /* ── Budget as a table (>=1024) ──────────────────────────────────────── */
    .budget-table-wrap { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg); box-shadow: var(--shadow); overflow-x: auto; }
    .budget-table { width: 100%; border-collapse: collapse; font-variant-numeric: tabular-nums; }
    .budget-table th { text-align: left; padding: 0; border-bottom: 1px solid var(--border); }
    .budget-table th button { width: 100%; text-align: left; font: inherit; font-size: 0.625rem; font-weight: 700; letter-spacing: 0.07em; text-transform: uppercase; color: var(--text-tertiary); background: transparent; border: 0; padding: 12px; cursor: pointer; white-space: nowrap; }
    .budget-table th.num button { text-align: right; }
    .budget-table th.sorted button { color: var(--accent); }
    .budget-table th button:focus-visible { outline: none; box-shadow: var(--focus-ring); }
    .budget-table th.act { width: 40px; }
    /* The actions column has no visible heading, but it still needs one for
       anyone reading the table with a screen reader. */
    .sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; border: 0; }
    .budget-table td { padding: 9px 12px; border-bottom: 1px solid var(--border); font-size: 0.8125rem; }
    .budget-table td.num { text-align: right; white-space: nowrap; }
    .budget-table td.dim { color: var(--text-secondary); }
    .budget-table td.nowrap { white-space: nowrap; }
    .budget-table td.strong { font-weight: 600; }
    .budget-table td.act { width: 40px; padding: 4px 8px; }
    .budget-table tbody tr { cursor: pointer; }
    .budget-table tbody tr:nth-child(even) { background: var(--surface-hover); }
    .budget-table tbody tr:hover { background: var(--accent-bg); }
    .budget-table tbody tr:focus-visible { outline: none; box-shadow: inset 0 0 0 2px var(--accent); }
    .budget-table tfoot td { border-bottom: 0; padding: 12px; font-size: 0.6875rem; font-weight: 700; letter-spacing: 0.05em; text-transform: uppercase; color: var(--text-tertiary); }
    .budget-table tfoot td.num { font-size: 1rem; letter-spacing: 0; text-transform: none; color: var(--text); }
    .budget-cat-pill { font-size: 0.6875rem; font-weight: 600; color: var(--accent); background: var(--accent-bg); border-radius: 10px; padding: 2px 9px; white-space: nowrap; }
    .budget-orig { display: block; font-size: 0.625rem; font-weight: 500; color: var(--text-tertiary); }
    .budget-table-hint { margin-top: 10px; font-size: 0.75rem; color: var(--text-tertiary); }

    /* ── Select many, act once (>=1024) ──────────────────────────────────── */
    @media (min-width: 1024px) {
      .bulk-bar { display: flex; align-items: center; gap: 8px; position: fixed; left: 50%; bottom: 26px; transform: translateX(-50%); z-index: 900; background: var(--text); color: var(--surface); border-radius: var(--radius-lg); padding: 10px 14px; box-shadow: 0 16px 40px rgba(0,0,0,0.28); max-width: calc(100vw - 48px); overflow-x: auto; }
      .bulk-bar[hidden] { display: none; }
      .bulk-bar b { font-size: 0.8125rem; white-space: nowrap; }
      .bulk-sep { width: 1px; height: 20px; background: color-mix(in srgb, var(--surface) 22%, transparent); flex-shrink: 0; }
      .bulk-bar button { font: inherit; font-size: 0.75rem; font-weight: 600; color: color-mix(in srgb, var(--surface) 90%, transparent); background: transparent; border: 1px solid color-mix(in srgb, var(--surface) 22%, transparent); border-radius: 12px; padding: 5px 11px; cursor: pointer; white-space: nowrap; flex-shrink: 0; }
      .bulk-bar button:hover { background: color-mix(in srgb, var(--surface) 12%, transparent); }
      .bulk-bar button.danger { color: #FFB4A2; border-color: rgba(255,180,162,0.4); }
      .bulk-bar button.bulk-x { border: 0; padding: 4px; display: flex; }
      .bulk-bar button:focus-visible { outline: none; box-shadow: 0 0 0 2px var(--surface); }
      /* A shift-click selects rows, not the page's text. */
      .pack-item { user-select: none; -webkit-user-select: none; }
      .item.is-picked { background: var(--accent-bg); box-shadow: inset 3px 0 0 var(--accent); }
      .item.is-picked .pack-name { font-weight: 600; }
    }
    .bulk-option { display: block; width: 100%; text-align: left; font: inherit; font-size: 0.875rem; background: transparent; border: 0; border-bottom: 1px solid var(--border); padding: 11px 2px; cursor: pointer; color: var(--text); }
    .bulk-option:last-child { border-bottom: 0; }
    .bulk-option:hover { color: var(--accent); }
    .bulk-option:focus-visible { outline: none; box-shadow: var(--focus-ring); }

    /* ── The week grid (>=1024) ──────────────────────────────────────────── */
    .week-top { display: flex; align-items: center; gap: 10px; margin-bottom: 10px; }
    .week-range { font-size: 0.8125rem; color: var(--text-tertiary); }
    .week-nav { margin-left: auto; display: flex; gap: 6px; }
    .week-nav .chip:disabled { opacity: 0.4; cursor: default; }
    .week-grid { display: flex; background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg); box-shadow: var(--shadow); overflow: hidden; }
    .week-axis { width: 54px; flex-shrink: 0; border-right: 1px solid var(--border); position: relative; padding-top: 52px; height: calc(var(--week-h) + 52px); }
    .week-hour { position: absolute; right: 8px; font-size: 0.625rem; font-weight: 700; color: var(--text-tertiary); font-variant-numeric: tabular-nums; transform: translateY(-50%); }
    .week-col { flex: 1; min-width: 0; border-right: 1px solid var(--border); }
    .week-col:last-child { border-right: 0; }
    .week-col.is-today .week-head { background: var(--accent-bg); }
    .week-head { height: 52px; box-sizing: border-box; border-bottom: 1px solid var(--border); padding: 8px 10px; }
    .week-day { font-size: 0.6875rem; font-weight: 700; color: var(--text-secondary); }
    .week-col.is-today .week-day { color: var(--accent); }
    .week-city { font-size: 0.75rem; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
    .week-untimed { font-size: 0.625rem; color: var(--text-tertiary); padding: 3px 10px; border-bottom: 1px solid var(--border); }
    .week-body { position: relative; height: var(--week-h); }
    .week-line { position: absolute; left: 0; right: 0; height: 1px; background: var(--border); }
    .week-block { position: absolute; left: 5px; right: 5px; text-align: left; font: inherit; border-radius: 9px; padding: 5px 8px; box-sizing: border-box; overflow: hidden; cursor: pointer; background: var(--accent-bg); border: 1px solid color-mix(in srgb, var(--accent) 28%, transparent); box-shadow: inset 3px 0 0 var(--accent); color: var(--text); }
    .week-block.tight { display: flex; align-items: center; gap: 7px; padding: 4px 8px; }
    .week-block.done { opacity: 0.5; }
    .week-block .week-time { display: block; font-size: 0.625rem; font-weight: 700; color: var(--accent); font-variant-numeric: tabular-nums; flex-shrink: 0; }
    .week-block .week-title { display: block; font-size: 0.6875rem; font-weight: 600; line-height: 1.3; }
    .week-block.tight .week-title { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
    .week-block.flight { background: color-mix(in srgb, var(--success) 9%, transparent); border-color: color-mix(in srgb, var(--success) 30%, transparent); box-shadow: inset 3px 0 0 var(--success); }
    .week-block.flight .week-time { color: var(--success); }
    .week-block.train { background: color-mix(in srgb, #007FAD 9%, transparent); border-color: color-mix(in srgb, #007FAD 30%, transparent); box-shadow: inset 3px 0 0 #007FAD; }
    .week-block.train .week-time { color: #007FAD; }
    .week-block.boat, .week-block.road { background: color-mix(in srgb, #B0325A 9%, transparent); border-color: color-mix(in srgb, #B0325A 30%, transparent); box-shadow: inset 3px 0 0 #B0325A; }
    .week-block.boat .week-time, .week-block.road .week-time { color: #B0325A; }
    .week-block:focus-visible { outline: none; box-shadow: 0 0 0 2px var(--accent); }
    .week-foot { margin-top: 10px; font-size: 0.75rem; color: var(--text-tertiary); text-align: center; }

    /* ── The palette's verbs ─────────────────────────────────────────────── */
    .search-item.cmd .search-title b { color: var(--accent); }
    .search-item.cmd.is-first { background: var(--accent-bg); box-shadow: inset 3px 0 0 var(--accent); }
    .cmd-kbd { flex-shrink: 0; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 0.625rem; color: var(--text-tertiary); border: 1px solid var(--border); border-radius: 4px; padding: 1px 5px; background: var(--surface); }

    @media print {
      .status-bar, .bulk-bar, .trip-aside, .rail-block, .week-top, .week-foot { display: none !important; }
      .trip-layout { display: block !important; }
      .trip-main { max-width: none !important; }
    }
