

@font-face { font-family: "Quicksand"; src: url(../../assets/fonts/de003d1dee5e.woff2) format("woff2"); font-weight: 400; font-style: normal; font-display: swap; }
@font-face { font-family: "Quicksand"; src: url(../../assets/fonts/7f3287c48fb9.woff2) format("woff2"); font-weight: 500; font-style: normal; font-display: swap; }
@font-face { font-family: "Quicksand"; src: url(../../assets/fonts/7a387287234d.woff2) format("woff2"); font-weight: 600; font-style: normal; font-display: swap; }
@font-face { font-family: "Quicksand"; src: url(../../assets/fonts/b5926932572a.woff2) format("woff2"); font-weight: 700; font-style: normal; font-display: swap; }

/* ============================================================================
   Lyfe Private Sessions, Design System v2
   Premium, dense SaaS-operations aesthetic (Datadog/Dynatrace inspired):
   dark chrome (sidebar + topbar), light content surfaces, crisp sans type,
   restrained accent color, line-icon system, tight consistent spacing.
   ========================================================================== */
:root {
  /* Tells the browser which palette to use for chrome it renders itself, 
     the date/time picker popup, number-input spinner, native <select>
     dropdown list, scrollbars and form-field autofill. Without this the
     browser assumes a light UI regardless of our own theme, so those
     controls show dark, hard-to-read text against a white popup even while
     the rest of the page is in dark mode. */
  color-scheme: light;
  /* ---- Chrome (dark sidebar/topbar), warm charcoal, not cold navy-black,
     so the "efficient/operational" admin shell still reads as Lyfe rather
     than a copied Datadog/Linear-style analytics tool. ---- */
  --chrome-bg: #1B1512;
  --chrome-bg-raised: #221B17;
  --chrome-border: rgba(255,244,230,0.08);
  --chrome-text: #B2A99E;
  --chrome-text-hover: #F2EAE0;
  --chrome-text-active: #FFFFFF;
  --chrome-active-bg: rgba(232,103,42,0.20);

  /* ======================================================================
     Lyfe SUNLIT EDITORIAL
     Lamu sunrise, warm architecture, ocean and vegetation, never a cool
     enterprise grey. Nothing in the interface is pure #FFFFFF.

     The palette is TWO-TIER, and the distinction is load-bearing:

       BRAND tier  (--brand-*)  the exact specified hues. Fills, gradients,
                                borders, chart series, large colour areas, 
                                anywhere the colour is seen AS colour.
       INK tier    (--ink-*)    the same hues darkened only as far as
                                legibility demands. Used whenever a colour
                                carries TEXT or an ICON on a light ground.

     This split exists because brand hues are chosen to look good as surfaces,
     and almost none of them survive as small text: measured against these
     grounds the specified tangerine reads 2.09:1 and sun gold 1.19:1, far
     under the 4.5:1 floor. Darkening every usage would dull the brand;
     darkening only the text keeps the palette exactly as designed where it
     is actually visible. Every pairing below is verified, not estimated.
     ---------------------------------------------------------------------- */

  /* ---- Brand tier, surfaces, fills, gradients ---- */
  --brand-tangerine: #F47A3D;
  --brand-gold:      #F8C94C;
  --brand-coral:     #EF6A5B;
  --brand-lagoon:    #39AFA3;
  --brand-lagoon-deep: #247D76;
  --brand-palm:      #568B62;
  /* Deepened for richness (brief: "reds should use a deeper, richer red...
     apply the same principle across every accent colour"), every hue here
     EXCEPT the brand tangerine below, which stays exactly as specified. */
  --brand-botanical: #1B7A54;
  --brand-coastal:   #1D6F94;
  --brand-plum:      #7A3F82;

  /* ---- Ink tier, the same hues, safe as text/icons on light grounds ---- */
  --ink-tangerine: #9C4B1C;
  --ink-gold:      #7A5710;
  --ink-coral:     #8F2E22;
  --ink-lagoon:    #0B5C54;
  --ink-lagoon-deep: #094A44;
  --ink-palm:      #17532F;
  --ink-botanical: #12523A;
  --ink-coastal:   #16506B;
  --ink-plum:      #55215B;

  /* ---- Accent: primary action. The brand tangerine is kept EXACTLY as
     specified; what changes is the text on top of it. White on #F47A3D is
     2.73:1 and fails, while Lamu Earth on the same fill is 4.96:1 and passes
, so a filled tangerine button carries dark warm text. That keeps the
     specified hue intact instead of dulling it to make white work. ---- */
  --accent: #F47A3D;
  --accent-strong: #E05F2A;
  --accent-ink: #9C4B1C;          /* tangerine as text/icon on light grounds */
  --accent-soft: #FFF1DC;
  --accent-gold: #E8AC1E;
  --accent-contrast: #342D28;     /* text ON a filled accent surface */

  /* ---- Content surfaces, Layer 0 canvas through Layer 2 tint ---- */
  --page-bg: #F4F8FC;             /* Frosted white, a breath of cool blue */
  --surface: #FFFFFF;             /* Clean white cards on the frost */
  --surface-alt: #EDF3F9;         /* quiet fills: chips, table heads, keys */
  --surface-sand: #E7EEF6;        /* secondary sections */
  --surface-clay: #FFF1DC;        /* warm tint, kept for festival moments */
  --surface-mist: #F7F9FC;        /* operational/admin areas */
  --border: #E3EAF2;
  --border-strong: #CEDAE6;
  --text-primary: #342D28;        /* Lamu Earth */
  --text-secondary: #5A6472;      /* cool-neutral, tuned to clear 4.5:1 */
  --text-muted: #616B79;          /* cool-neutral, tuned to clear 4.5:1 */

  /* ---- Gradients, identity, used selectively ---- */
  --grad-sunrise:  linear-gradient(135deg, #EF6A5B 0%, #F47A3D 52%, #F8C94C 100%);
  --grad-golden:   linear-gradient(135deg, #F8C94C 0%, #F5A846 50%, #EF6A5B 100%);
  --grad-lagoon:   linear-gradient(135deg, #247D76 0%, #39AFA3 52%, #77CDBB 100%);
  --grad-botanical:linear-gradient(135deg, #276A51 0%, #568B62 52%, #9CBD83 100%);
  --grad-plum:     linear-gradient(135deg, #76516C 0%, #A85470 52%, #EF6A5B 100%);

  /* ---- Sunlit wash, the banner surface -----------------------------------
     The palette for large, calm, light-filled surfaces: page heroes, the front
     door, confirmation moments. Cream → Peach → Soft Tangerine → Warm Gold.

     It is built as several low-opacity radial layers over a cream ground, not
     as two bright stops at opposite ends of a linear gradient. Two reasons.
     First, opposite-end linears put their most saturated colour hard against
     an edge and band visibly across a wide panel; overlapping soft radials
     read as light falling on a surface instead. Second, the saturated layers
     can then be placed, right side, corners, off-canvas centres, so the left
     column where headlines and body copy sit stays near the cream base, and
     dark ink on it clears 4.5:1 with room to spare rather than needing scrims.

     Alphas are capped low on purpose: the warmest pixel anywhere on the wash
     lands around #F9E3BE, which is still light enough for the soft ink below.
     Raising them is what turns "sunlit" into "orange". */
  /* The four wash tones used to be a fixed warm palette (peach/coral/
     tangerine/gold) no matter which accent an organizer published, so the
     one hero surface every attendee sees first stayed a fixed orange gradient
     even on a purple or teal event theme. They now derive from the theme's
     own --accent/--secondary via color-mix, at the same lightness each
     literal tone was tuned to, so the wash genuinely re-hues itself with the
     rest of the product instead of being pasted on top of it. */
  --wash-base:      color-mix(in srgb, var(--accent) 6%,  #FFFDF8);
  --wash-peach:     color-mix(in srgb, var(--accent) 30%, white);
  --wash-coral:     color-mix(in srgb, var(--accent) 46%, white);
  --wash-tangerine: color-mix(in srgb, var(--accent) 64%, white);
  --wash-gold:      color-mix(in srgb, var(--secondary, var(--accent)) 40%, white);

  /* Ink for the wash. Warm rather than the cool page greys, so text belongs to
     the surface it sits on instead of looking pasted onto it. */
  --wash-ink:       #3A2F26;   /* headings, ~9.7:1 on the warmest point       */
  --wash-ink-soft:  #6A5949;   /* body and stats, ~4.9:1 on the warmest point */
  --wash-line:      color-mix(in srgb, var(--accent) 14%, #FFF4E4);   /* the wash's own hairline, on frost or white */

  /* The composed layer stack. Order is top-most first, as CSS expects.
     Each stop is now `color-mix(in srgb, <tone> alpha%, transparent)` rather
     than `rgba(var(--tone-rgb), alpha)`, since the tones above are full
     computed colors (from color-mix) rather than raw "r, g, b" triples. */
  --wash-sunlit:
    radial-gradient(118% 148% at 86% -22%, color-mix(in srgb, var(--wash-gold) 50%, transparent) 0%, color-mix(in srgb, var(--wash-gold) 20%, transparent) 33%, transparent 61%),
    radial-gradient(84% 118% at 101% 110%, color-mix(in srgb, var(--wash-coral) 46%, transparent) 0%, color-mix(in srgb, var(--wash-coral) 16%, transparent) 40%, transparent 71%),
    radial-gradient(58%  98% at 113%  60%, color-mix(in srgb, var(--wash-tangerine) 30%, transparent) 0%, transparent 66%),
    radial-gradient(70% 120% at  -8%  92%, color-mix(in srgb, var(--wash-peach) 62%, transparent) 0%, transparent 64%),
    linear-gradient(116deg, color-mix(in srgb, var(--wash-peach) 0%, transparent) 0%, color-mix(in srgb, var(--wash-peach) 30%, transparent) 44%, color-mix(in srgb, var(--wash-peach) 66%, transparent) 79%, color-mix(in srgb, var(--wash-peach) 38%, transparent) 100%),
    linear-gradient(135deg, color-mix(in srgb, var(--wash-base) 96%, white) 0%, var(--wash-base) 54%, color-mix(in srgb, var(--wash-base) 92%, var(--wash-peach) 8%) 100%);

  /* ---- Shadows, warm and soft. A neutral-black shadow over a cream canvas
     reads as grey dirt rather than depth. ---- */
  --shadow-1: 0 8px 24px rgba(28,45,70,0.06);
  --shadow-2: 0 16px 36px rgba(28,45,70,0.10);
  --shadow-pop: 0 20px 48px rgba(28,45,70,0.14);

  /* ---- Chart series, drawn from the Lyfe palette so a chart belongs to the
     same product as the card holding it. Ordered for maximum separation
     between neighbours rather than by hue family. ---- */
  --surface-1: #FFFDF8;
  --series-1: #1D6F94;   /* coastal   */
  --series-2: #F47A3D;   /* tangerine, brand, unchanged */
  --series-3: #0FA394;   /* lagoon    */
  --series-4: #E8AC1E;   /* gold      */
  --series-5: #8C3A5E;   /* plum-rose */
  --series-6: #217A46;   /* palm      */
  --series-7: #7A3F82;   /* plum      */
  --series-8: #E14F3E;   /* coral     */

  /* ---- Status, deliberately DISTINCT from the decorative brand hues, so
     tangerine never has to mean both "festival category" and "warning".
     These are the ink-safe values; --*-fill holds the fuller colour for
     dots, bars and solid surfaces. ---- */
  --status-good: #1E7A46;
  --status-good-fill: #22A35C;
  --status-warning: #9C5B0A;
  --status-warning-fill: #E08E1D;
  --status-serious: #B0431A;
  --status-critical: #C22A22;
  --status-critical-fill: #E2412F;
  --status-info: #2A6B8C;
  --status-info-fill: #3D91B8;
  --status-pending: #8A5A17;
  --status-neutral: #6F655C;
  --gridline: #E3EAF2;
  --muted: #616B79;

  /* ---- Geometry, one ladder, no arbitrary in-between values ---- */
  --radius-sm: 8px;
  --radius-md: 14px;      /* compact card, input */
  --radius-lg: 20px;      /* primary card */
  --radius-hero: 24px;    /* hero surfaces */
  --radius-pill: 999px;

  /* ---- Spacing scale, 4/8/12/16/20/24/32/40/48 ---- */
  --sp-1: 4px;  --sp-2: 8px;  --sp-3: 12px; --sp-4: 16px;
  --sp-5: 20px; --sp-6: 24px; --sp-8: 32px; --sp-10: 40px; --sp-12: 48px;
  --sidebar-w: 236px;
  --topbar-h: 56px;

  /* ---- Typography identity ----------------------------------------------
     One face, Quicksand (self-hosted), used everywhere in the product, 
     headings, the wallet balance figure, editorial moments and every piece
     of UI chrome and body copy alike. ---- */
  --font-display: "Quicksand", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  --font-ui: "Quicksand", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  --font-numeric-feature: "tnum" 1, "lnum" 1; /* tabular, lining figures for money */

  /* ---- Motion system: "calm glide", soft ease-out, no bounce, no
     decoration on urgent/money flows. One scale used everywhere. ---- */
  --ease-glide: cubic-bezier(0.22, 0.61, 0.36, 1);
  --ease-standard: cubic-bezier(0.4, 0, 0.2, 1);
  /* Fast interaction / standard UI / page-and-sheet / celebration. */
  --dur-fast: 0.16s;
  --dur-base: 0.22s;
  --dur-slow: 0.30s;
  --dur-celebrate: 0.45s;
}

/* ---- Live value updates (fees, payouts, totals, CTA amounts) -------------
   A number that replaces its own textContent snaps no matter how good the
   rest of the product's motion is, there is nothing for the eye to track
   the change with. `tickValue(el)` (core.js) is called anywhere a computed
   amount updates in place (withdrawal fee/payout, top-up CTA, quick-amount
   selection) and simply re-adds this class via a reflow, so a value that
   changes mid-typing keeps re-triggering the same short, calm motion instead
   of queuing up multiple animations or feeling delayed. Deliberately NOT
   applied to raw keypad digit entry (mobile Top Up/Withdraw), typing needs
   to feel instant, not animated. */
@keyframes valueTick { 0% { opacity: .5; transform: translateY(3px); } 100% { opacity: 1; transform: translateY(0); } }
.value-tick { animation: valueTick var(--dur-base) var(--ease-glide); }
@media (prefers-reduced-motion: reduce) { .value-tick { animation: none; } }

* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
body {
  font-family: var(--font-ui);
  color: var(--text-primary);
  background: var(--page-bg);
  -webkit-font-smoothing: antialiased;
  font-size: 14px;
}
h1, h2 { font-family: var(--font-display); margin: 0 0 4px; font-weight: 700; color: var(--text-primary); letter-spacing: -0.006em; }
h3, h4 { font-family: inherit; margin: 0 0 4px; font-weight: 700; color: var(--text-primary); letter-spacing: -0.01em; }
h1 { font-size: 24px; line-height: 1.22; }
h2 { font-size: 18px; line-height: 1.28; }
h3 { font-size: 14.5px; }
.money, .wbc-balance, .kpi-value { font-variant-numeric: tabular-nums; }
p { margin: 0 0 8px; line-height: 1.55; }
a { color: inherit; }
/* Buttons don't inherit text colour by default in browsers (the UA
   stylesheet gives them the `buttontext` keyword, which resolves to a fixed
   black regardless of theme), every plain-text button content (.kpi-value,
   .dnum, .ag-title, demo-card labels, and any future one) would otherwise go
   invisible the moment the surface it sits on goes dark. */
/* One motion baseline for every plain button in the product (tabs, segmented
   controls, filter chips, day/slot pickers, nav links, role switches, the
   ones that only ever carry a bespoke class, never `.btn`, and so never
   picked up its transition). Anything more specific declared elsewhere
   layers on top of this rather than replacing it, so a state change always
   eases instead of snapping, everywhere, without hunting down each control
   one at a time. */
button, .navlink, .side-link {
  font-family: inherit; cursor: pointer; color: inherit;
  transition: background-color var(--dur-fast) var(--ease-standard), color var(--dur-fast) var(--ease-standard),
    border-color var(--dur-fast) var(--ease-standard), box-shadow var(--dur-fast) var(--ease-standard),
    transform var(--dur-fast) var(--ease-standard), opacity var(--dur-fast) var(--ease-standard);
}
input, select, textarea { font-family: inherit; font-size: 13.5px; transition: border-color var(--dur-fast) var(--ease-standard), box-shadow var(--dur-fast) var(--ease-standard); }
/* Browser autofill (saved email/password suggestions) paints its own
   background + text colour outside normal CSS, defaulting to a light
   yellow/white fill with black text, invisible against a dark input in
   dark mode. The long transition delay is the standard trick to suppress
   Chromium's fill colour instead of matching it, and text-fill-color is
   the only property that can override the autofilled text colour itself. */
input:-webkit-autofill,
input:-webkit-autofill:hover,
input:-webkit-autofill:focus,
input:-webkit-autofill:active {
  -webkit-text-fill-color: var(--text-primary);
  caret-color: var(--text-primary);
  transition: background-color 9999s ease-in-out 0s, color 9999s ease-in-out 0s;
  box-shadow: 0 0 0 1000px var(--surface) inset;
}
::selection { background: var(--accent); color: #fff; }
.icon { display: inline-block; vertical-align: -3px; flex-shrink: 0; }
button, .navlink, .side-link { display: inline-flex; align-items: center; }

/* ---------------- App shell: sidebar + topbar + content ---------------- */
#appRoot { min-height: 100vh; }
.shell { display: flex; min-height: 100vh; }
.sidebar {
  width: var(--sidebar-w); flex-shrink: 0; background: var(--chrome-bg);
  border-right: 1px solid var(--chrome-border); position: sticky; top: 0; height: 100vh; overflow-y: auto;
  display: flex; flex-direction: column;
}
.sidebar .brand { display: flex; align-items: center; gap: 9px; padding: 16px 16px 14px; color: #fff; font-weight: 700; font-size: 14.5px; width: 100%; text-align: left; background: none; border: none; border-bottom: 1px solid var(--chrome-border); cursor: pointer; }
.sidebar .brand .mark { width: 26px; height: 26px; border-radius: 7px; background: linear-gradient(135deg, var(--accent), var(--accent-gold)); display: flex; align-items: center; justify-content: center; color: #fff; font-weight: 800; font-size: 13px; flex-shrink: 0; }
.sidebar-nav { padding: 10px 10px 18px; flex: 1; }
.side-group-label { font-size: 10.5px; text-transform: uppercase; letter-spacing: 0.08em; color: #9A8F82; font-weight: 700; margin: 16px 10px 6px; }
.side-group-label:first-child { margin-top: 4px; }
.side-link {
  display: flex; align-items: center; gap: 10px; padding: 8px 10px; border-radius: var(--radius-sm);
  color: var(--chrome-text); font-size: 13px; font-weight: 500; margin-bottom: 1px; border: none; background: transparent; width: 100%; text-align: left;
  border-left: 2px solid transparent;
}
.side-link .icon { opacity: .85; }
.side-link.active { background: var(--chrome-active-bg); color: var(--chrome-text-active); border-left-color: var(--accent); font-weight: 600; }
.side-link.active .icon { color: var(--accent-gold); opacity: 1; }
.side-link:hover:not(.active) { background: rgba(255,255,255,0.045); color: var(--chrome-text-hover); }
.sidebar-foot { padding: 12px 14px 16px; border-top: 1px solid var(--chrome-border); }
.role-pill-row { display: flex; flex-direction: column; gap: 3px; }
.role-pill-row .rp-label { font-size: 10px; text-transform: uppercase; letter-spacing: .08em; color: #9A8F82; margin-bottom: 4px; font-weight: 700; }
.role-pill { display: flex; align-items: center; gap: 8px; padding: 7px 9px; border-radius: var(--radius-sm); background: transparent; border: 1px solid transparent; color: var(--chrome-text); font-size: 12.5px; font-weight: 600; width: 100%; text-align: left; }
.role-pill.active { background: rgba(255,255,255,0.07); color: #fff; border-color: var(--chrome-border); }
.role-pill:hover:not(.active) { color: var(--chrome-text-hover); }

.main-col { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.topbar {
  height: var(--topbar-h); flex-shrink: 0; display: flex; align-items: center; gap: 14px; padding: 0 20px;
  background: var(--chrome-bg-raised); border-bottom: 1px solid var(--chrome-border); position: sticky; top: 0; z-index: 40;
}
.topbar .tb-search { flex: 1; max-width: 420px; }
.topbar .search-input { background: rgba(255,255,255,0.06); border: 1px solid var(--chrome-border); }
.topbar .search-input input { color: #fff; }
.topbar .search-input input::placeholder { color: #767c8c; }
.topbar .search-input .icon { color: #767c8c; }
.tb-spacer { flex: 1; }
.tb-actions { display: flex; align-items: center; gap: 8px; }
.tb-icon-btn { width: 34px; height: 34px; border-radius: var(--radius-sm); border: 1px solid var(--chrome-border); background: rgba(255,255,255,0.04); color: var(--chrome-text); display: flex; align-items: center; justify-content: center; position: relative; }
.tb-icon-btn:hover { background: rgba(255,255,255,0.09); color: #fff; }
.tb-icon-btn .dot { position: absolute; top: 5px; right: 6px; width: 7px; height: 7px; border-radius: 50%; background: var(--status-critical); border: 1.5px solid var(--chrome-bg-raised); }
.tb-demo-btn { display: flex; align-items: center; gap: 6px; padding: 0 12px; height: 34px; border-radius: var(--radius-sm); border: 1px solid var(--chrome-border); background: rgba(255,255,255,0.04); color: var(--chrome-text); font-size: 12.5px; font-weight: 600; }
.tb-demo-btn:hover { background: rgba(255,255,255,0.09); color: #fff; }

.content-area { flex: 1; padding: 22px 26px 70px; max-width: 1280px; width: 100%; }
.content-area.full { max-width: none; }
.container { max-width: 1180px; margin: 0 auto; padding: 24px 24px 60px; width: 100%; }

/* Breadcrumbs */
.crumbs { display: flex; align-items: center; gap: 6px; font-size: 12.5px; color: var(--text-muted); margin-bottom: 10px; flex-wrap: wrap; }
.crumbs button { background: none; border: none; color: var(--text-muted); font-size: 12.5px; font-weight: 500; padding: 0; }
.crumbs button:hover { color: var(--accent); text-decoration: underline; }
.crumbs .crumb-current { color: var(--text-primary); font-weight: 600; }
.crumbs .icon { opacity: .55; }

/* ---------------- Cards & grids ---------------- */
.card { background: var(--surface); border-radius: var(--radius-lg); padding: 20px; box-shadow: var(--shadow-1); border: 1px solid var(--border); }
.card-flat { background: var(--surface); border-radius: var(--radius-md); padding: 15px 16px; border: 1px solid var(--border); }
.card-flat.clickable { cursor: pointer; transition: box-shadow var(--dur-base) var(--ease-glide), border-color var(--dur-base) var(--ease-glide); }
.card-flat.clickable:hover { box-shadow: var(--shadow-2); border-color: var(--border-strong); }
.grid { display: grid; gap: 16px; }
.grid-2 { grid-template-columns: repeat(2, 1fr); }
.grid-3 { grid-template-columns: repeat(3, 1fr); }
.grid-4 { grid-template-columns: repeat(4, 1fr); }
.grid-6 { grid-template-columns: repeat(6, 1fr); }
@media (max-width: 980px) { .grid-4, .grid-3, .grid-6 { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 620px) { .grid-2, .grid-3, .grid-4, .grid-6 { grid-template-columns: 1fr; } }

/* ---------------- Buttons ---------------- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  padding: 9px 16px; border-radius: var(--radius-sm); font-weight: 600; font-size: 13px;
  border: 1px solid transparent; transition: transform var(--dur-fast) var(--ease-standard), box-shadow var(--dur-fast) var(--ease-standard), background var(--dur-fast) var(--ease-standard); text-decoration: none;
}
.btn:active { transform: scale(0.98); }
.btn-primary { background: var(--accent); color: var(--accent-contrast); }
.btn-primary:hover { background: var(--accent-strong); }
.btn-dark { background: var(--text-primary); color: #fff; }
.btn-dark:hover { background: #000; }
.btn-outline { background: var(--surface); border-color: var(--border-strong); color: var(--text-primary); }
.btn-outline:hover { background: var(--surface-alt); border-color: var(--accent); }
.btn-ghost { background: transparent; color: var(--text-secondary); border-color: transparent; }
.btn-ghost:hover { background: var(--surface-alt); color: var(--text-primary); }
.btn-danger { background: var(--status-critical); color: #fff; }
.btn-danger:hover { background: #b32e2e; }
.btn-sm { padding: 6px 12px; font-size: 12px; }
.btn-block { width: 100%; }
.btn[disabled] { opacity: 0.4; pointer-events: none; }

/* ---------------- Chips / status tags ---------------- */
.chip { display: inline-flex; align-items: center; gap: 5px; padding: 3px 9px; border-radius: 6px; font-size: 11px; font-weight: 700; white-space: nowrap; text-transform: uppercase; letter-spacing: 0.02em; }
.chip::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: currentColor; flex-shrink: 0; }
.chip-grey { background: #EDEEF3; color: #5B6072; }
.chip-amber { background: #FBEBD2; color: #8A5A06; }
.chip-orange { background: #F7DFD0; color: #96471F; }
.chip-blue { background: #E0EAF9; color: #17508F; }
.chip-teal { background: #D8F0E5; color: #0A6345; }
.chip-purple { background: #E6E2F6; color: #3F3092; }
.chip-green { background: #DCEFD6; color: #16691B; }
.chip-red { background: #F9D9D9; color: #A82B2B; }
.chip-dark-red { background: #F4C6C6; color: #8f1f1f; }

/* ---------------- KPI tiles ---------------- */
.kpi { background: var(--surface); border-radius: var(--radius-md); padding: 14px 16px; border: 1px solid var(--border); border-top: 2px solid var(--border); position: relative; }
.kpi.accent { border-top-color: var(--accent); }
.kpi .kpi-label { font-size: 11px; color: var(--text-muted); font-weight: 700; text-transform: uppercase; letter-spacing: 0.05em; display: flex; align-items: center; gap: 6px; }
.kpi .kpi-value { font-size: 24px; font-weight: 700; margin-top: 6px; letter-spacing: -0.02em; line-height: 1.15; overflow-wrap: anywhere; }
.kpi-strip .kpi .kpi-value { font-size: 21px; }
.kpi .kpi-sub { display: block; }
.kpi .kpi-label { display: flex; align-items: center; gap: 6px; }
.kpi .kpi-sub { font-size: 11.5px; color: var(--text-muted); margin-top: 2px; }

/* ---------------- Avatars ---------------- */
.avatar { border-radius: 8px; display: flex; align-items: center; justify-content: center; color: #fff; font-weight: 700; flex-shrink: 0; position: relative; overflow: hidden; }
.avatar.has-photo { background: var(--surface-alt); }
.avatar .av-photo { width: 100%; height: 100%; object-fit: cover; display: block; border-radius: inherit; }
.practitioner-card { background: var(--surface); border-radius: var(--radius-lg); overflow: hidden; box-shadow: var(--shadow-1); border: 1px solid var(--border); display: flex; flex-direction: column; transition: box-shadow var(--dur-base) var(--ease-glide); }
.practitioner-card:hover { box-shadow: var(--shadow-2); }
.practitioner-card .photo-band { height: 92px; display: flex; align-items: center; justify-content: center; }
.practitioner-card .photo-band .avatar { width: 60px; height: 60px; font-size: 20px; border-radius: 12px; border: 3px solid #fff; box-shadow: var(--shadow-1); }
.practitioner-card .pc-body { padding: 13px 15px 15px; display: flex; flex-direction: column; gap: 5px; flex: 1; }

/* ---------------- Tables ---------------- */
table.data-table { width: 100%; border-collapse: collapse; font-size: 13px; }
table.data-table th { text-align: left; font-size: 11px; text-transform: uppercase; letter-spacing: 0.05em; color: var(--text-muted); font-weight: 700; padding: 9px 12px; border-bottom: 1px solid var(--border); background: var(--surface-alt); }
table.data-table th:first-child { border-top-left-radius: var(--radius-sm); }
table.data-table th:last-child { border-top-right-radius: var(--radius-sm); }
table.data-table td { padding: 10px 12px; border-bottom: 1px solid var(--border); vertical-align: middle; }
/* Was a hardcoded near-white (#FAFAFD), correct-looking on the light theme
   by coincidence, but in dark mode every table row hover flashed bright
   white because this compound selector (table.data-table) is more specific
   than the theme-aware `.data-table tbody tr:hover { background:
   var(--surface-alt) }` rule below, so it always won regardless of theme. */
table.data-table tr:hover td { background: var(--surface-alt); }
table.data-table tr:last-child td { border-bottom: none; }
.table-wrap { overflow-x: auto; border-radius: var(--radius-md); border: 1px solid var(--border); }
.table-wrap table.data-table { border: none; }
.table-wrap table.data-table th { border-bottom: 1px solid var(--border); }

/* ---------------- Forms ---------------- */
.field { margin-bottom: 13px; }
.field label { display: block; font-size: 12px; font-weight: 600; color: var(--text-secondary); margin-bottom: 5px; }
.field input, .field select, .field textarea {
  width: 100%; padding: 9px 11px; border-radius: var(--radius-sm); border: 1px solid var(--border-strong); background-color: var(--surface); color: var(--text-primary);
}
.field input:focus, .field select:focus, .field textarea:focus { outline: 2px solid var(--accent); outline-offset: 1px; border-color: var(--accent); }
.field-row { display: flex; gap: 12px; }
.field-row .field { flex: 1; }
.checkline { display: flex; gap: 8px; align-items: flex-start; font-size: 12.5px; color: var(--text-secondary); margin-bottom: 8px; }
.checkline input { margin-top: 3px; }
select, input, textarea { font-size: 13px; }
.topbar select, .card-flat select { border-radius: var(--radius-sm); border: 1px solid var(--border-strong); padding: 7px 10px; background-color: var(--surface); }

/* ---------------------------------------------------------------------------
   Premium form controls, every <select>, <input type="range">, and
   <input type="file"> in the product, replacing raw OS chrome.

   The generic look complained about (flat white slider track with a stock
   blue OS thumb, a bare browser dropdown arrow, an unstyled "Choose File"
   button) comes from these three control types being invisible to the
   product's own input styling: `.field input` sets padding/border/background,
   but browsers largely ignore those properties for range and file inputs and
   render their native chrome inside the box instead, and no rule anywhere
   touches the select's built-in arrow. This section is the fix, applied once
   globally so it reaches every current and future instance, not just the
   one currently in Theme Studio. ---------------------------------------- */

/* Select, same border/surface treatment every input already gets, plus a
   custom chevron drawn in the product's own muted ink instead of the OS
   arrow. The open popup list itself remains OS-drawn (no browser lets a
   page restyle it), but the always-visible closed control now matches. */
select {
  appearance: none; -webkit-appearance: none; -moz-appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='%238D93A2' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 11px center; background-size: 13px;
  padding-right: 32px !important;
  cursor: pointer;
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
select:hover:not(:disabled) { border-color: var(--accent); }
select:disabled { cursor: not-allowed; opacity: 0.6; }
select::-ms-expand { display: none; }

/* Range slider, a slim filled track (accent up to the thumb, muted after)
   with a large, brand-ringed thumb that scales gently on hover/press. Firefox
   gets the fill for free via ::-moz-range-progress; WebKit has no such
   pseudo-element, so the fill there comes from a gradient stop driven by the
   --range-progress custom property, which the field's own oninput handler
   keeps in sync (see App._liveThemeField in views.js). */
.field input[type="range"], input[type="range"] {
  -webkit-appearance: none; appearance: none;
  width: 100%; height: 6px; padding: 0; margin: 10px 0;
  border: none; border-radius: 999px; cursor: pointer;
  background: linear-gradient(to right,
    var(--accent) 0%, var(--accent) var(--range-progress, 50%),
    var(--border-strong) var(--range-progress, 50%), var(--border-strong) 100%);
}
input[type="range"]::-webkit-slider-runnable-track { height: 6px; border-radius: 999px; background: transparent; }
input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none;
  width: 20px; height: 20px; margin-top: -7px; border-radius: 50%;
  background: #fff; border: 3px solid var(--accent);
  box-shadow: 0 2px 6px rgba(0,0,0,0.28);
  cursor: pointer; transition: transform 0.15s ease, box-shadow 0.15s ease;
}
input[type="range"]::-webkit-slider-thumb:hover { transform: scale(1.1); }
input[type="range"]::-webkit-slider-thumb:active { transform: scale(0.94); }
input[type="range"]::-moz-range-track { height: 6px; border-radius: 999px; background: var(--border-strong); border: none; }
input[type="range"]::-moz-range-progress { height: 6px; border-radius: 999px; background: var(--accent); }
input[type="range"]::-moz-range-thumb {
  width: 20px; height: 20px; border-radius: 50%; background: #fff; border: 3px solid var(--accent);
  box-shadow: 0 2px 6px rgba(0,0,0,0.28); cursor: pointer; transition: transform 0.15s ease;
}
input[type="range"]::-moz-range-thumb:hover { transform: scale(1.1); }
input[type="range"]:focus-visible { outline: none; }
input[type="range"]:focus-visible::-webkit-slider-thumb { box-shadow: 0 0 0 4px color-mix(in srgb, var(--accent) 28%, transparent), 0 2px 6px rgba(0,0,0,0.28); }
input[type="range"]:focus-visible::-moz-range-thumb { box-shadow: 0 0 0 4px color-mix(in srgb, var(--accent) 28%, transparent), 0 2px 6px rgba(0,0,0,0.28); }
input[type="range"]:disabled { opacity: 0.5; cursor: not-allowed; }

/* File input, the field wrapper's own border/background/padding is meant
   for a text-like control and just boxes-in the native file row, so a file
   input opts out of it and instead restyles its OS upload button to look
   like a real product button (outline style), with the "no file chosen"
   caption in the product's own muted-text colour beside it. */
.field input[type="file"], input[type="file"] {
  width: auto; border: none; background: none; padding: 6px 0;
  color: var(--text-secondary); font-size: 12.5px; display: inline-flex; align-items: center;
}
input[type="file"]::file-selector-button,
input[type="file"]::-webkit-file-upload-button {
  font-family: inherit; font-size: 13px; font-weight: 600; line-height: 1;
  padding: 9px 15px; margin-right: 12px; border-radius: var(--radius-sm);
  border: 1px solid var(--border-strong); background: var(--surface-alt, var(--surface)); color: var(--text-primary);
  cursor: pointer; transition: background 0.15s ease, border-color 0.15s ease;
}
input[type="file"]::file-selector-button:hover,
input[type="file"]::-webkit-file-upload-button:hover { background: var(--surface); border-color: var(--accent); }
input[type="file"]:disabled::file-selector-button,
input[type="file"]:disabled::-webkit-file-upload-button { opacity: 0.6; cursor: not-allowed; }

/* ---------------- Booking progress steps ---------------- */
.steps { display: flex; align-items: center; gap: 6px; margin-bottom: 20px; flex-wrap: wrap; }
.steps .step { display: flex; align-items: center; gap: 7px; font-size: 12px; font-weight: 600; color: var(--text-muted); }
.steps .step .num { width: 22px; height: 22px; border-radius: 50%; background: var(--surface-alt); color: var(--text-muted); display: flex; align-items: center; justify-content: center; font-size: 11.5px; border: 1px solid var(--border); }
.steps .step.active .num { background: var(--accent); color: #fff; border-color: var(--accent); }
.steps .step.done .num { background: var(--status-good); color: #fff; border-color: var(--status-good); }
.steps .step.active, .steps .step.done { color: var(--text-primary); }
.steps .sep { width: 18px; height: 1px; background: var(--border-strong); }

/* ---- Smart Match wizard: a focused quiz column, not a full-width sprawl.
   Keeps option buttons at a comfortable reading width instead of stretching
   short single-line choices across the entire content area. ---- */
.wizard-shell {
  max-width: 640px; margin: 0 auto; min-height: calc(100vh - 300px);
  display: flex; flex-direction: column; justify-content: center;
}
.wizard-shell .steps { justify-content: center; }
.wizard-shell h3 { text-align: center; font-size: 19px; }
.wizard-shell .grid-2 .btn-block, .wizard-shell .grid-3 .btn-block { padding: 18px 20px; font-size: 14px; }
.wizard-shell .grid-4 .day-pill { padding: 16px 8px; }

/* ---------------- Booking layout: main + sticky summary ---------------- */
.booking-layout { display: grid; grid-template-columns: 1fr 300px; gap: 22px; align-items: start; }
@media (max-width: 900px) { .booking-layout { grid-template-columns: 1fr; } }
.booking-summary { position: sticky; top: calc(var(--topbar-h) + 16px); background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg); padding: 20px; box-shadow: var(--shadow-1); }
.booking-main-card { min-width: 0; }
.booking-main-card > .crumbs:first-child { margin-top: -2px; }
.booking-summary .bs-title { font-size: 11px; text-transform: uppercase; letter-spacing: .06em; color: var(--text-muted); font-weight: 700; margin-bottom: 10px; }
.booking-summary .bs-row { display: flex; align-items: flex-start; gap: 9px; padding: 7px 0; border-bottom: 1px solid var(--gridline); font-size: 13px; }
.booking-summary .bs-row:last-of-type { border-bottom: none; }
.booking-summary .bs-row .icon { color: var(--text-muted); margin-top: 1px; }
.booking-summary .bs-row .bs-label { color: var(--text-muted); font-size: 11px; text-transform: uppercase; letter-spacing: .03em; display: block; }
.booking-summary .bs-row .bs-value { font-weight: 600; }
.booking-summary .bs-total { display: flex; justify-content: space-between; align-items: center; padding-top: 10px; margin-top: 6px; font-weight: 700; font-size: 15px; }

/* ---------------- Calendar / date-time picker ---------------- */
.week-strip { display: grid; grid-template-columns: repeat(auto-fit, minmax(90px, 1fr)); gap: 10px; margin-bottom: 20px; max-width: 620px; }
/* `button` is globally inline-flex; day pills need column flow for name/number. */
.day-pill { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 1px;
  border: 1px solid var(--border-strong); border-radius: var(--radius-md); padding: 9px 8px; text-align: center; background: var(--surface); }
.day-pill:hover:not(.active) { border-color: var(--accent); }
.day-pill.active { background: var(--accent); color: var(--accent-contrast); border-color: var(--accent); }
.day-pill .dname { font-size: 10.5px; text-transform: uppercase; letter-spacing: .05em; color: var(--text-muted); font-weight: 600; }
.day-pill.active .dname { color: color-mix(in srgb, var(--accent-contrast) 78%, transparent); }
.day-pill .dnum { font-size: 18px; font-weight: 700; }
.slot-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 9px; }
@media (max-width: 620px) { .slot-grid { grid-template-columns: repeat(3, 1fr); } }
.slot-btn { padding: 11px 6px; border-radius: var(--radius-sm); border: 1.5px solid var(--border-strong); background: var(--surface); font-weight: 600; font-size: 13px; }
.slot-btn:hover:not(.disabled) { border-color: var(--accent); color: var(--accent); }
.slot-btn.selected { background: var(--accent); border-color: var(--accent); color: #fff; }
.slot-btn.disabled { opacity: 0.35; text-decoration: line-through; pointer-events: none; }

/* ---------------- Timeline ---------------- */
.timeline { position: relative; padding-left: 24px; }
.timeline::before { content: ""; position: absolute; left: 5px; top: 4px; bottom: 4px; width: 2px; background: var(--gridline); }
.timeline .t-item { position: relative; padding-bottom: 16px; }
.timeline .t-item::before { content: ""; position: absolute; left: -24px; top: 2px; width: 11px; height: 11px; border-radius: 50%; background: var(--accent); border: 2px solid var(--surface); box-shadow: 0 0 0 1px var(--border-strong); }
.timeline .t-item .t-label { font-weight: 600; font-size: 13px; }
.timeline .t-item .t-time { font-size: 11px; color: var(--text-muted); }

/* ---------------- Modal / toast ---------------- */
#modalRoot:empty, #toastRoot:empty { display: none; }

/* ---------------- Side panel ---------------- */

/* ---------------- Charts ---------------- */
.bar-chart { display: flex; align-items: flex-end; gap: 10px; height: 150px; padding-top: 10px; }
.bar-chart .bar-col { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: flex-end; height: 100%; }
.bar-chart .bar { width: 65%; border-radius: 4px 4px 2px 2px; background: var(--series-1); min-height: 3px; }
.bar-chart .bar-val { font-size: 11px; font-weight: 700; margin-bottom: 4px; color: var(--text-secondary); }
.bar-chart .bar-lbl { font-size: 10.5px; color: var(--text-muted); margin-top: 6px; text-align: center; }
.hbar-row { display: flex; align-items: center; gap: 10px; margin-bottom: 9px; }
.hbar-row .hbar-lbl { width: 150px; font-size: 12.5px; color: var(--text-secondary); flex-shrink: 0; }
.hbar-row .hbar-track { flex: 1; background: var(--gridline); border-radius: 4px; height: 10px; overflow: hidden; }
.hbar-row .hbar-fill { height: 100%; border-radius: 4px; }
.hbar-row .hbar-val { width: 40px; text-align: right; font-size: 12px; font-weight: 700; flex-shrink: 0; }
.donut-legend { display: flex; flex-direction: column; gap: 8px; }
.donut-legend .leg-item { display: flex; align-items: center; gap: 8px; font-size: 12.5px; }
.donut-legend .leg-dot { width: 9px; height: 9px; border-radius: 50%; flex-shrink: 0; }
.donut-legend .leg-val { margin-left: auto; font-weight: 700; }

/* ---------------- Tabs ---------------- */
.tabs { display: flex; gap: 4px; border-bottom: 1px solid var(--border); margin-bottom: 18px; overflow-x: auto; }
.tabs button { background: none; border: none; padding: 9px 14px; font-size: 13px; font-weight: 600; color: var(--text-muted); border-bottom: 2px solid transparent; white-space: nowrap; border-radius: var(--radius-sm) var(--radius-sm) 0 0; }
.tabs button.active { color: var(--text-primary); border-bottom-color: var(--accent); }
.tabs button:hover:not(.active) { color: var(--text-primary); }

/* ---------------- Kanban (daily ops) ---------------- */
.kanban { display: grid; grid-template-columns: repeat(5, 1fr); gap: 12px; align-items: start; }
@media (max-width: 1100px) { .kanban { grid-template-columns: repeat(2, 1fr); } }
.kanban-col { background: var(--surface-alt); border-radius: var(--radius-md); padding: 10px; min-height: 120px; }
.kanban-col h4 { font-size: 11.5px; text-transform: uppercase; letter-spacing: .04em; padding: 4px 6px 10px; color: var(--text-secondary); }
.kanban-card { background: var(--surface); border-radius: var(--radius-sm); padding: 10px 11px; margin-bottom: 8px; font-size: 12.5px; border: 1px solid var(--border); box-shadow: var(--shadow-1); cursor: pointer; }
.kanban-card:hover { border-color: var(--border-strong); }
.kanban-card .kc-time { font-weight: 700; }
.kanban-card .kc-warn { display: flex; align-items: center; gap: 4px; color: var(--status-critical); font-weight: 700; font-size: 11px; margin-top: 4px; }

/* ---------------- Misc ---------------- */
.muted { color: var(--text-muted); }
.small { font-size: 12.5px; }
.text-secondary { color: var(--text-secondary); }
.divider { height: 1px; background: var(--border); margin: 14px 0; }
.flex { display: flex; }
.flex-between { display: flex; align-items: center; justify-content: space-between; }
.flex-wrap { flex-wrap: wrap; }
.gap-8 { gap: 8px; } .gap-12 { gap: 12px; } .gap-16 { gap: 16px; }
.section-title { display: flex; align-items: center; justify-content: space-between; margin-bottom: 14px; }
.pill-filter { display: inline-flex; align-items: center; gap: 6px; border: 1px solid var(--border-strong); background: var(--surface); padding: 6px 12px; border-radius: var(--radius-pill); font-size: 12px; font-weight: 600; }
.pill-filter:hover { border-color: var(--accent); color: var(--accent); }
.search-input { display: flex; align-items: center; gap: 8px; background: var(--surface); border: 1px solid var(--border-strong); border-radius: var(--radius-sm); padding: 8px 12px; }
.search-input input { border: none; outline: none; min-width: 0; flex: 1; background: transparent; text-overflow: ellipsis; }
.hero {
  background-color: var(--wash-base); background-image: var(--wash-sunlit);
  color: var(--wash-ink-soft); border: 1px solid var(--wash-line);
  border-radius: var(--radius-lg); padding: 30px 32px; margin-bottom: 24px;
  position: relative; overflow: hidden;
}
.hero h1 { color: var(--wash-ink); font-size: 26px; }
.hero p { color: var(--wash-ink-soft); max-width: 520px; font-size: 13.5px; }
.badge-today { background: var(--accent); color: var(--accent-contrast); font-size: 10px; font-weight: 700; padding: 3px 8px; border-radius: var(--radius-pill); text-transform: uppercase; letter-spacing: .03em; }
.link-btn { display: inline-flex; align-items: center; gap: 4px; background: none; border: none; color: var(--accent-ink); font-weight: 600; font-size: 12.5px; padding: 0; }
.link-btn:hover { text-decoration: underline; }
.page-header { margin-bottom: 18px; }
.page-header .ph-row { display: flex; align-items: center; justify-content: space-between; gap: 16px; flex-wrap: wrap; }
.page-header h1 { display: flex; align-items: center; gap: 4px; }
/* ============================================================================
   LIGHT SHELL, Attendee / Practitioner / Volunteer
   Interactive, inviting "product" feel: light top nav, no dark sidebar,
   rounded-pill buttons, cards that lift on hover. Admin keeps the dense
   dark-chrome control-tower look defined above; this is intentionally different.
   ========================================================================== */
.light-shell { display: flex; flex-direction: column; min-height: 100vh; }
.light-topbar { background: var(--surface); border-bottom: 1px solid var(--border); position: sticky; top: 0; z-index: 40; }
.lt-inner { max-width: 1180px; margin: 0 auto; display: flex; align-items: center; gap: 14px; padding: 11px 24px; }
.lt-navbar { border-top: 1px solid var(--border); background: var(--surface); display: flex; align-items: center; }
.lt-navbar .lt-nav { max-width: 1180px; margin: 0 auto; padding: 7px 20px; flex: 1; min-width: 0; }
/* Left/right nudge buttons for the nav row above, it scrolls
   (overflow-x: auto) with the scrollbar hidden, so on a mouse-driven
   desktop there was no visible sign more links existed off-screen, let
   alone a way to reach them without a trackpad swipe. Each arrow only
   appears once its direction actually has somewhere to go
   (App.updateLtNavArrows toggles these on .lt-navbar after every render,
   on scroll, and on resize), so a screen with everything already visible
   never shows either one. */
.lt-nav-arrow {
  display: none; align-items: center; justify-content: center;
  width: 30px; height: 30px; flex-shrink: 0; margin: 0 4px;
  border: 1px solid var(--border-strong); border-radius: 50%;
  background: var(--surface); color: var(--text-secondary); cursor: pointer;
  transition: background var(--dur-fast) var(--ease-standard), color var(--dur-fast) var(--ease-standard);
}
.lt-nav-arrow:hover { background: var(--surface-alt); color: var(--text-primary); }
.lt-navbar.can-scroll-left .lt-nav-arrow-left,
.lt-navbar.can-scroll-right .lt-nav-arrow-right { display: flex; }
.brand-light { border: none; background: none; padding: 0; }
.persona-select select { border: 1px solid var(--border-strong); border-radius: var(--radius-pill); padding: 7px 11px; font-size: 12.5px; font-weight: 600; color: var(--text-secondary); background-color: var(--surface); max-width: 150px; }
.kbd-hint kbd { margin-left: 2px; }
.brand-light { display: flex; align-items: center; gap: 9px; font-weight: 800; font-size: 16px; cursor: pointer; color: var(--text-primary); flex-shrink: 0; }
.brand-light .mark { width: 30px; height: 30px; border-radius: 10px; background: linear-gradient(135deg, var(--accent), var(--accent-gold)); color: #fff; display: flex; align-items: center; justify-content: center; font-weight: 800; font-size: 14px; }
.lt-nav { display: flex; gap: 3px; overflow-x: auto; scrollbar-width: none; }
.lt-nav::-webkit-scrollbar { display: none; }
.lt-link { display: inline-flex; align-items: center; gap: 6px; padding: 8px 14px; border-radius: var(--radius-pill); font-size: 13.5px; font-weight: 600; color: var(--text-secondary); border: none; background: transparent; white-space: nowrap; transition: background var(--dur-fast) var(--ease-standard), color var(--dur-fast) var(--ease-standard); }
.lt-link.active { background: var(--accent); color: #fff; }
.lt-link:hover:not(.active) { background: var(--surface-alt); color: var(--text-primary); }
.lt-actions { display: flex; align-items: center; gap: 8px; flex-shrink: 0; margin-left: auto; }
.lt-search { display: none; flex: 1; max-width: 360px; }
@media (min-width: 900px) { .lt-search { display: flex; } }
.lt-search .search-input { width: 100%; }
.role-switch-light { display: flex; background: var(--surface-alt); border-radius: var(--radius-pill); padding: 3px; gap: 1px; flex-shrink: 0; }
.role-switch-light button { display: flex; align-items: center; gap: 5px; border: none; background: transparent; padding: 7px 9px; border-radius: var(--radius-pill); font-size: 11.5px; font-weight: 700; color: var(--text-muted); }
.role-switch-light button.active { background: #fff; color: var(--text-primary); box-shadow: var(--shadow-1); padding: 7px 12px; }
.role-switch-light button:hover:not(.active) { color: var(--text-primary); }
/* Compact segmented control: only the active role shows its text label, others are icon-only (with a tooltip via title) */
.role-switch-light button .rsl-text { display: none; }
.role-switch-light button.active .rsl-text { display: inline; }
.light-topbar .tb-icon-btn { border-color: var(--border-strong); background: var(--surface); color: var(--text-secondary); }
.light-topbar .tb-icon-btn:hover { background: var(--surface-alt); color: var(--text-primary); }
.light-topbar .tb-icon-btn .dot { border-color: var(--surface); }
.light-topbar .tb-demo-btn { border-color: var(--border-strong); background: var(--surface); color: var(--text-secondary); }
.light-topbar .tb-demo-btn:hover { background: var(--surface-alt); color: var(--text-primary); }

/* Friendlier, rounder, springier components inside the light shell */
.light-shell .btn { border-radius: var(--radius-pill); }
.light-shell .btn-sm { border-radius: var(--radius-pill); }
.light-shell .card { border-radius: 18px; }
.light-shell .card-flat { border-radius: 16px; transition: transform var(--dur-base) var(--ease-glide), box-shadow var(--dur-base) var(--ease-glide), border-color var(--dur-base) var(--ease-glide); }
.light-shell .card-flat.clickable:hover { transform: translateY(-2px); border-color: var(--accent-gold); }
.light-shell .practitioner-card { border-radius: 20px; transition: transform var(--dur-base) var(--ease-glide), box-shadow var(--dur-base) var(--ease-glide), border-color var(--dur-base) var(--ease-glide); }
.light-shell .practitioner-card:hover { transform: translateY(-4px); box-shadow: var(--shadow-2); border-color: var(--accent-gold); }
.light-shell .kpi { border-radius: 16px; transition: transform var(--dur-base) var(--ease-glide), box-shadow var(--dur-base) var(--ease-glide); }
.light-shell .kpi:hover { transform: translateY(-2px); box-shadow: var(--shadow-2); }
.light-shell .day-pill, .light-shell .slot-btn { border-radius: 14px; transition: transform var(--dur-fast) var(--ease-standard), background var(--dur-fast) var(--ease-standard), color var(--dur-fast) var(--ease-standard), border-color var(--dur-fast) var(--ease-standard); }
.light-shell .slot-btn:hover:not(.disabled) { transform: translateY(-1px); }
.light-shell .booking-summary { border-radius: 20px; }
.light-shell .hero { border-radius: 22px; }

/* Bottom tab bar (mobile, light shell only) */
.mobile-tabbar { display: none; }
@media (max-width: 760px) {
  .light-shell .lt-navbar, .light-shell .lt-search { display: none; }
  .lt-inner { padding: 10px 14px; gap: 8px; }
  .brand-light .brand-text { display: none; }
  .lt-actions select { display: none; }
  .role-switch-light button { padding: 7px 8px; }
  .role-switch-light .rsl-text { display: none; }
  .light-topbar .tb-demo-btn .tb-demo-label { display: none; }
  .light-topbar .tb-demo-btn { padding: 0; width: 34px; justify-content: center; }
  .mobile-tabbar {
    display: flex; position: fixed; bottom: 0; left: 0; right: 0; z-index: 120;
    background: var(--surface); border-top: 1px solid var(--border); padding: 6px 4px calc(6px + env(safe-area-inset-bottom));
    box-shadow: 0 -4px 16px rgba(20,22,31,0.06);
  }
  .mobile-tabbar button { flex: 1; display: flex; flex-direction: column; align-items: center; gap: 2px; padding: 6px 2px; background: none; border: none; color: var(--text-muted); font-size: 10px; font-weight: 600; border-radius: 10px; }
  .mobile-tabbar button.active { color: var(--accent); }
  .light-shell .container { padding-bottom: 84px; }
}
@media (max-width: 700px) {
  .container { padding: 16px 14px 60px; }
}

/* "Install as app" banner, its own fixed element appended to <body> (see
   App.showInstallBanner), so it floats above whichever shell/role is on
   screen rather than being scoped to one layout. */
.install-banner {
  position: fixed; z-index: 500; left: 16px; right: 16px; bottom: 16px;
  max-width: 400px; margin: 0 auto;
  display: flex; align-items: center; gap: 12px;
  background: var(--surface); border: 1px solid var(--border); border-radius: 18px;
  padding: 14px 16px; box-shadow: 0 12px 32px rgba(20,22,31,0.16);
  opacity: 0; transform: translateY(16px) scale(0.98);
  transition: opacity var(--dur-base) var(--ease-glide), transform var(--dur-base) var(--ease-glide);
  padding-bottom: calc(14px + env(safe-area-inset-bottom));
}
.install-banner.in { opacity: 1; transform: translateY(0) scale(1); }
.install-banner-icon { width: 44px; height: 44px; border-radius: 12px; flex-shrink: 0; box-shadow: 0 2px 8px rgba(20,22,31,0.12); }
.install-banner-body { flex: 1; min-width: 0; }
.install-banner-body > strong { display: block; font-size: 14px; margin-bottom: 2px; }
.install-banner-body p { margin: 0; font-size: 12.5px; color: var(--text-muted); line-height: 1.4; }
.install-inline-icon { vertical-align: -3px; margin: 0 1px; color: var(--accent); }
.install-banner-cta { flex-shrink: 0; white-space: nowrap; }
.install-banner-close {
  position: absolute; top: -8px; right: -8px; width: 22px; height: 22px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  background: var(--surface); border: 1px solid var(--border); color: var(--text-muted);
  box-shadow: 0 2px 6px rgba(20,22,31,0.14); padding: 0; cursor: pointer;
}
.install-banner-close:hover { color: var(--text); }
@media (max-width: 480px) {
  .install-banner { left: 10px; right: 10px; bottom: 10px; padding: 12px 14px; gap: 10px; }
  .install-banner-body p { font-size: 12px; }
}
/* Clear the light-shell's own bottom tab bar so the two never overlap.
   :has() falls back gracefully (rule just doesn't apply) on the handful of
   engines that lack it, the banner still works, just without the offset. */
@media (max-width: 760px) {
  body:has(.mobile-tabbar) .install-banner { bottom: calc(64px + env(safe-area-inset-bottom)); }
}
@media (prefers-reduced-motion: reduce) {
  .install-banner { transition: opacity 0.01ms; transform: none; }
}

/* Subtle "alive" feel: content fades/slides in on every navigation */
.container, .content-area { animation: viewIn var(--dur-base) var(--ease-glide); }
@keyframes viewIn { from { opacity: 0; transform: translateY(5px); } to { opacity: 1; transform: translateY(0); } }

.hamburger-btn { display: none; }
@media (max-width: 700px) {
  .side-panel { width: 100%; }
  .sidebar { position: fixed; left: -260px; top: 0; z-index: 150; transition: left var(--dur-base) var(--ease-standard); box-shadow: var(--shadow-pop); }
  .sidebar.open { left: 0; }
  .content-area { padding: 16px 14px 90px; }
  .topbar .tb-search { display: none; }
  .hamburger-btn { display: flex !important; }
  .tb-demo-btn span.tb-demo-label { display: none; }
}
.sidebar-scrim { display: none; }
@media (max-width: 700px) {
  .sidebar-scrim.show { display: block; position: fixed; inset: 0; background: rgba(16,19,26,0.4); z-index: 140; }
}

/* ============================================================================
   PLATFORM LAYER, components introduced by the production pass.
   Dialogs, tables, palette, forms, agenda, callouts, plus the accessibility
   and responsive rules that apply across the whole product.
   ========================================================================== */

/* ---- Layering scale (documented order, no more magic numbers) ---- */
:root {
  --z-topbar: 40; --z-tabbar: 120; --z-scrim: 140; --z-sidebar: 150;
  --z-mtx: 180; --z-panel: 190; --z-modal: 200; --z-palette: 260; --z-toast: 300;
  --space-1: 4px; --space-2: 8px; --space-3: 12px; --space-4: 16px;
  --space-5: 22px; --space-6: 30px;
  --focus: 0 0 0 2px var(--surface), 0 0 0 4px var(--accent);
}

/* ---- Accessibility primitives ---- */
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.skip-link {
  position: absolute; left: 8px; top: -60px; z-index: 400; background: var(--accent);
  color: #fff; padding: 10px 16px; border-radius: var(--radius-sm); font-weight: 700;
  font-size: 13px; text-decoration: none; transition: top var(--dur-fast) var(--ease-standard);
}
.skip-link:focus { top: 8px; }
body.no-scroll { overflow: hidden; }

/* A single, consistent focus ring on everything interactive. Buttons (and
   anything with an explicit tabindex, e.g. a clickable row/card) use an
   INSET box-shadow rather than an outline: a good number of buttons across
   the product live inside a horizontally-scrolling strip, tabs, day-pills,
   quick-amount chips, filter chips, and any container with overflow-x:auto
   clips a same-element outline (which paints outside the border box) into a
   broken, fragmented ring the moment that button is focused. A box-shadow
   with no spread beyond the box can never be clipped this way, in any
   container, so this one rule stays correct everywhere rather than needing
   a per-component exception list. Text inputs/links rarely sit in a
   scrolling strip, so they keep the conventional outset outline.
   */
button:focus-visible, [tabindex]:focus-visible, .row-click:focus-visible {
  outline: none; box-shadow: inset 0 0 0 2px var(--accent); border-radius: var(--radius-sm);
}
a:focus-visible, input:focus-visible, select:focus-visible, textarea:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 2px; border-radius: var(--radius-sm);
}
.sidebar button:focus-visible, .topbar button:focus-visible {
  outline: none; box-shadow: inset 0 0 0 2px #fff;
}
/* The hero is a LIGHT surface now, so a white focus ring on it is invisible.
   It gets the same dark-on-light ring the rest of the product uses. */
.hero button:focus-visible { outline: 2px solid var(--ink-tangerine); outline-offset: 2px; }
.search-input:focus-within { outline: 2px solid var(--accent); outline-offset: 1px; }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important; animation-iteration-count: 1 !important;
    transition-duration: .001ms !important; scroll-behavior: auto !important;
  }
  .container, .content-area { animation: none !important; }
  .light-shell .card-flat.clickable:hover, .light-shell .practitioner-card:hover,
  .light-shell .kpi:hover, .light-shell .slot-btn:hover:not(.disabled) { transform: none !important; }
}

/* ---- Dialog system ----
   Every overlay fades in on open (the backdrop used to snap straight to full
   opacity while only the box inside it faded, now both move together), and
   every one has a matching CLOSE animation driven by the `.closing` class
   UI.close()/closeAll()/Palette.hide() add before they tear the DOM down
   (see core.js), the exact "abrupt / snapping" gap the box itself already
   had an entrance for but never an exit. */
.modal-overlay, .side-panel-overlay { position: fixed; inset: 0; background: rgba(16,19,26,0.55); display: flex; animation: overlayIn var(--dur-fast) var(--ease-standard); }
.modal-overlay { align-items: center; justify-content: center; padding: 20px; }
.side-panel-overlay { justify-content: flex-end; }
@keyframes overlayIn { from { opacity: 0; } to { opacity: 1; } }
.modal-overlay.closing, .side-panel-overlay.closing { animation: overlayOut var(--dur-fast) var(--ease-standard) forwards; }
@keyframes overlayOut { from { opacity: 1; } to { opacity: 0; } }
.modal-box {
  background: var(--surface); border-radius: var(--radius-lg); width: 100%; max-width: 560px;
  max-height: 88vh; display: flex; flex-direction: column; box-shadow: var(--shadow-pop); position: relative;
  animation: dlgIn var(--dur-base) var(--ease-glide);
}
.modal-box.wide { max-width: 780px; }
.modal-box.narrow { max-width: 440px; }
@keyframes dlgIn { from { opacity: 0; transform: translateY(8px) scale(.99); } to { opacity: 1; transform: none; } }
.modal-overlay.closing .modal-box { animation: dlgOut var(--dur-fast) var(--ease-standard) forwards; }
@keyframes dlgOut { from { opacity: 1; transform: none; } to { opacity: 0; transform: translateY(6px) scale(.98); } }
.side-panel {
  background: var(--surface); width: 460px; max-width: 100%; height: 100%;
  display: flex; flex-direction: column; box-shadow: var(--shadow-pop); animation: panelIn var(--dur-base) var(--ease-glide);
  border-left: 1px solid var(--border);
}
@keyframes panelIn { from { transform: translateX(24px); opacity: .6; } to { transform: none; opacity: 1; } }
.side-panel-overlay.closing .side-panel { animation: panelOut var(--dur-fast) var(--ease-standard) forwards; }
@keyframes panelOut { from { transform: none; opacity: 1; } to { transform: translateX(24px); opacity: 0; } }
.dlg-head {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 16px 20px; border-bottom: 1px solid var(--border); flex-shrink: 0;
}
.dlg-title { font-size: 15.5px; font-weight: 700; }
.dlg-body { padding: 18px 20px 20px; overflow-y: auto; flex: 1; -webkit-overflow-scrolling: touch; overscroll-behavior: contain; }
.dlg-text { font-size: 13.5px; color: var(--text-secondary); }
.dlg-actions { display: flex; gap: 10px; justify-content: flex-end; margin-top: 20px; flex-wrap: wrap; }
.modal-close {
  width: 34px; height: 34px; min-width: 34px; border-radius: var(--radius-sm); border: 1px solid transparent;
  background: transparent; color: var(--text-muted); display: flex; align-items: center; justify-content: center; flex-shrink: 0;
}
.modal-close:hover { background: var(--surface-alt); color: var(--text-primary); }
.modal-close.floating { position: absolute; top: 12px; right: 12px; z-index: 2; }
.processing { text-align: center; padding: 18px 4px 8px; }
.processing .big-check { color: var(--status-good); margin-bottom: 6px; }
.spinner {
  width: 34px; height: 34px; border-radius: 50%; margin: 0 auto 14px;
  border: 3px solid var(--surface-alt); border-top-color: var(--accent); animation: spin .8s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

/* ---- Callouts ---- */
.callout {
  display: flex; gap: 10px; align-items: flex-start; padding: 12px 14px; border-radius: var(--radius-md);
  font-size: 13px; line-height: 1.5; margin: 12px 0; border: 1px solid;
}
.callout .icon { margin-top: 1px; flex-shrink: 0; }
.callout-info { background: #EEF3FC; border-color: #CFDDF4; color: #17508F; }
.callout-warn { background: #FDF3E2; border-color: #F2DFBC; color: #8A5A06; }
.callout-danger { background: #FCEBEB; border-color: #F3CFCF; color: #A82B2B; }
.callout-ok { background: #E9F5E5; border-color: #CDE6C6; color: #16691B; }

/* ---- Forms ---- */
.form-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px; }
@media (max-width: 620px) { .form-grid { grid-template-columns: 1fr; } }
.form-section-label {
  font-size: 11px; text-transform: uppercase; letter-spacing: .07em; font-weight: 700;
  color: var(--text-muted); margin-top: 6px; padding-top: 12px; border-top: 1px solid var(--border);
}
.field label { display: block; font-size: 12.5px; font-weight: 600; margin-bottom: 5px; color: var(--text-secondary); }
.field-hint { font-size: 11.5px; color: var(--text-muted); margin-top: 4px; line-height: 1.45; }
.field-error { font-size: 11.5px; color: var(--status-critical); margin-top: 4px; font-weight: 600; display: none; }
.field-error.show { display: block; }
.field.invalid input, .field.invalid select, .field.invalid textarea {
  border-color: var(--status-critical); background-color: color-mix(in srgb, var(--status-critical) 8%, var(--surface));
}
.field.invalid input:focus, .field.invalid textarea:focus { outline-color: var(--status-critical); }
.req { color: var(--status-critical); font-weight: 700; }
.checkbox-field label { display: flex; align-items: flex-start; gap: 9px; font-weight: 500; color: var(--text-primary); font-size: 13px; }
.checkbox-field input { margin-top: 2px; width: 17px; height: 17px; accent-color: var(--accent); flex-shrink: 0; }
.checkline input { width: 17px; height: 17px; accent-color: var(--accent); }
.consent-block { background: var(--surface-alt); border-radius: var(--radius-md); padding: 12px 14px; }
.field-label-standalone { display: block; font-size: 12.5px; font-weight: 600; margin: 14px 0 6px; color: var(--text-secondary); }

/* ---- Tables ---- */
.table-block { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg); overflow: hidden; }
.table-toolbar {
  display: flex; gap: 10px; align-items: center; padding: 12px 14px; flex-wrap: wrap;
  border-bottom: 1px solid var(--border); background: var(--surface);
}
.table-search { flex: 1; min-width: 190px; max-width: 340px; }
.table-filter select {
  border-radius: var(--radius-sm); border: 1px solid var(--border-strong); padding: 8px 10px;
  background-color: var(--surface); font-size: 12.5px; font-weight: 600; color: var(--text-secondary); max-width: 190px;
}
.tt-spacer { flex: 1; }
.data-table { width: 100%; border-collapse: collapse; font-size: 13px; }
.data-table th {
  text-align: left; font-size: 11px; text-transform: uppercase; letter-spacing: .05em;
  color: var(--text-muted); font-weight: 700; padding: 10px 14px; background: var(--surface-alt);
  border-bottom: 1px solid var(--border); white-space: nowrap; position: sticky; top: 0; z-index: 1;
}
.data-table td { padding: 11px 14px; border-bottom: 1px solid var(--gridline); vertical-align: middle; }
.data-table tbody tr:last-child td { border-bottom: none; }
.data-table tbody tr:hover { background: var(--surface-alt); }
.data-table.dense td { padding: 7px 12px; }
.th-sort {
  display: inline-flex; align-items: center; gap: 4px; background: none; border: none; padding: 0;
  font: inherit; font-size: 11px; text-transform: uppercase; letter-spacing: .05em; font-weight: 700; color: var(--text-muted);
}
.th-sort:hover { color: var(--text-primary); }
.th-sort.active { color: var(--accent); }
.th-sort .icon { opacity: .6; }
.ta-right { text-align: right; } .ta-center { text-align: center; }
.col-check { width: 36px; } .col-check input { width: 16px; height: 16px; accent-color: var(--accent); }
.col-actions { white-space: nowrap; }
.cell-sub { display: block; font-size: 11.5px; color: var(--text-muted); margin-top: 1px; }
.cell-person { display: flex; align-items: center; gap: 9px; }
.row-click { cursor: pointer; }
.mono { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12.5px; }
.meter { display: inline-block; width: 46px; height: 6px; border-radius: 3px; background: var(--surface-alt); overflow: hidden; vertical-align: middle; margin-right: 6px; }
.meter-fill { display: block; height: 100%; background: var(--accent); }
.table-foot {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 11px 14px; border-top: 1px solid var(--border); flex-wrap: wrap;
}
.pager { display: flex; gap: 3px; align-items: center; flex-wrap: wrap; }
.pg-btn {
  min-width: 32px; height: 32px; padding: 0 8px; border-radius: var(--radius-sm);
  border: 1px solid var(--border-strong); background: var(--surface); color: var(--text-secondary);
  font-size: 12.5px; font-weight: 600; display: inline-flex; align-items: center; justify-content: center;
}
.pg-btn:hover:not([disabled]) { border-color: var(--accent); color: var(--accent); }
.pg-btn.active { background: var(--accent); border-color: var(--accent); color: #fff; }
.pg-btn[disabled] { opacity: .4; cursor: not-allowed; }
.pg-gap { color: var(--text-muted); padding: 0 2px; }
.bulk-bar {
  display: flex; align-items: center; gap: 10px; padding: 10px 14px; flex-wrap: wrap;
  background: var(--accent-soft); border-bottom: 1px solid var(--border);
}

/* ---- Command palette ---- */
.palette-overlay {
  position: fixed; inset: 0; background: rgba(16,19,26,0.5); z-index: var(--z-palette);
  display: flex; align-items: flex-start; justify-content: center; padding: 12vh 16px 16px;
  animation: overlayIn var(--dur-fast) var(--ease-standard);
}
.palette-overlay.closing { animation: overlayOut var(--dur-fast) var(--ease-standard) forwards; }
.palette {
  background: var(--surface); border-radius: var(--radius-lg); width: 100%; max-width: 620px;
  box-shadow: var(--shadow-pop); overflow: hidden; display: flex; flex-direction: column; max-height: 66vh;
  animation: dlgIn var(--dur-fast) var(--ease-glide);
}
.palette-overlay.closing .palette { animation: dlgOut var(--dur-fast) var(--ease-standard) forwards; }
.palette-input { display: flex; align-items: center; gap: 10px; padding: 14px 16px; border-bottom: 1px solid var(--border); }
.palette-input:focus-within { outline: 2px solid var(--accent); outline-offset: -2px; }
.palette-input input { flex: 1; border: none; outline: none; font-size: 15px; background: transparent; }
.palette-input .icon { color: var(--text-muted); }
.palette-list { overflow-y: auto; padding: 6px; }
.palette-item {
  display: flex; align-items: center; gap: 10px; width: 100%; text-align: left; padding: 9px 11px;
  border: none; background: none; border-radius: var(--radius-sm); font-size: 13.5px; color: var(--text-primary);
  transition: background-color var(--dur-fast) var(--ease-standard);
}
.palette-item.active { background: var(--accent-soft); }
.pi-icon { color: var(--text-muted); display: flex; transition: color var(--dur-fast) var(--ease-standard); }
.palette-item.active .pi-icon { color: var(--accent); }
.pi-label { flex: 1; font-weight: 600; display: flex; flex-direction: column; }
.pi-sub { font-weight: 400; font-size: 11.5px; color: var(--text-muted); }
.pi-type { font-size: 10.5px; text-transform: uppercase; letter-spacing: .05em; color: var(--text-muted); font-weight: 700; }
.palette-empty { padding: 26px 16px; text-align: center; color: var(--text-muted); font-size: 13px; }
.palette-foot {
  display: flex; gap: 16px; padding: 9px 14px; border-top: 1px solid var(--border);
  font-size: 11px; color: var(--text-muted); background: var(--surface-alt);
}
kbd {
  font-family: inherit; font-size: 10px; background: var(--surface); border: 1px solid var(--border-strong);
  border-bottom-width: 2px; border-radius: 4px; padding: 1px 5px; margin: 0 2px; color: var(--text-secondary);
}

/* ---- Toasts ---- */
#toastRoot { position: fixed; bottom: 20px; right: 20px; z-index: var(--z-toast); display: flex; flex-direction: column; gap: 8px; align-items: flex-end; }
.toast {
  display: flex; align-items: center; gap: 9px; background: var(--text-primary); color: #fff;
  padding: 11px 13px; border-radius: var(--radius-md); font-size: 13px; box-shadow: var(--shadow-pop);
  max-width: 400px; animation: toastIn var(--dur-base) var(--ease-glide);
}
@keyframes toastIn { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }
/* Auto-dismiss and the manual X used to leave differently, the timer faded
   the toast out over .3s, the X button just deleted the node. Both now play
   this on the way out (see the shared `dismiss()` in UI.toast, core.js). */
.toast.closing { animation: toastOut 0.2s var(--ease-standard) forwards; pointer-events: none; }
@keyframes toastOut { from { opacity: 1; transform: none; } to { opacity: 0; transform: translateY(6px) scale(.96); } }
.toast .toast-msg { flex: 1; }
.toast.success .icon { color: #7BE495; }
.toast.error { background: #7A1F1F; } .toast.error .icon { color: #FFC2C2; }
.toast.warning { background: #7A5406; } .toast.warning .icon { color: #FFDFA0; }
.toast.info .icon { color: #A9CDFF; }
.toast-action {
  background: rgba(255,255,255,0.16); border: none; color: #fff; font-weight: 700; font-size: 12px;
  padding: 5px 10px; border-radius: var(--radius-sm); flex-shrink: 0;
}
.toast-action:hover { background: rgba(255,255,255,0.28); }
.toast-close { background: none; border: none; color: rgba(255,255,255,0.6); display: flex; padding: 2px; flex-shrink: 0; }
.toast-close:hover { color: #fff; }

/* ---- Detail lists, panels, results ---- */
.detail-list { margin: 0; display: flex; flex-direction: column; gap: 9px; }
.detail-list > div { display: flex; justify-content: space-between; gap: 14px; align-items: baseline; }
.detail-list dt { font-size: 12.5px; color: var(--text-muted); flex-shrink: 0; }
.detail-list dd { margin: 0; font-size: 13.5px; font-weight: 600; text-align: right; word-break: break-word; }
.detail-list.two-col { display: grid; grid-template-columns: repeat(2, 1fr); gap: 12px 20px; }
@media (max-width: 620px) { .detail-list.two-col { grid-template-columns: 1fr; } }
.panel-head { padding-bottom: 12px; border-bottom: 1px solid var(--border); margin-bottom: 14px; }
.panel-section { padding-bottom: 16px; margin-bottom: 16px; border-bottom: 1px solid var(--border); }
.panel-section:last-child { border-bottom: none; margin-bottom: 0; }
.panel-section h4 { font-size: 11px; text-transform: uppercase; letter-spacing: .06em; color: var(--text-muted); margin-bottom: 9px; }
.mini-kpis { display: flex; gap: 10px; }
.mini-kpis > div { flex: 1; background: var(--surface-alt); border-radius: var(--radius-md); padding: 10px; text-align: center; }
.mk-val { display: block; font-size: 19px; font-weight: 700; }
.mk-lbl { display: block; font-size: 10.5px; color: var(--text-muted); text-transform: uppercase; letter-spacing: .04em; margin-top: 2px; }
.search-group { margin-bottom: 24px; }
.search-group h3 { display: flex; align-items: center; gap: 8px; margin-bottom: 10px; }
.count-pill { background: var(--surface-alt); color: var(--text-muted); font-size: 11px; font-weight: 700; padding: 2px 8px; border-radius: var(--radius-pill); }
.result-row {
  display: flex; align-items: center; gap: 11px; width: 100%; text-align: left; padding: 11px 14px;
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-md);
  margin-bottom: 7px; font-size: 13.5px; color: var(--text-primary); transition: border-color var(--dur-fast) var(--ease-standard), transform var(--dur-fast) var(--ease-standard);
}
.result-row:hover { border-color: var(--accent); transform: translateX(2px); }
.result-row.compact { padding: 9px 11px; }
.rr-body { flex: 1; display: flex; flex-direction: column; min-width: 0; }
.rr-body strong { font-weight: 650; }
.rr-icon { color: var(--text-muted); display: flex; width: 34px; height: 34px; align-items: center; justify-content: center; background: var(--surface-alt); border-radius: var(--radius-sm); flex-shrink: 0; }
.result-row .icon:last-child { color: var(--text-muted); }

/* ---- KPI strip: flexbox, not a fixed 6-track grid, each row's tiles grow
   to fill that row's own width, so a short list (or a ragged last row when
   wrapping) never leaves a dead gap the way a fixed grid template would. ---- */
.kpi-strip { display: flex; flex-wrap: wrap; gap: 10px; margin-bottom: 18px; }
.kpi-strip > .kpi { flex: 1 1 150px; }
@media (max-width: 620px) { .kpi-strip > .kpi { flex-basis: 130px; } }

/* ---- Wallet balance card, a genuine vertical premium financial card.
   Solid tonal gradient (derived from the live --accent so it stays correct
   under any published event theme) rather than a scenic photo, so the
   surface reads as a real card product, not a postcard with numbers on it. ---- */
.wallet-balance-card {
  position: relative; width: 100%; max-width: 272px; aspect-ratio: 5 / 8;
  border-radius: 24px; overflow: hidden; isolation: isolate; margin-bottom: 20px;
  padding: 22px 22px 24px; display: flex; flex-direction: column;
  background:
    radial-gradient(140% 90% at 8% -10%, color-mix(in srgb, var(--accent) 55%, #171922) 0%, transparent 55%),
    linear-gradient(165deg, #23262F 0%, #16181F 46%, #0C0D12 100%);
  box-shadow: 0 24px 48px -18px rgba(10,10,14,.55), 0 2px 8px rgba(0,0,0,.18), inset 0 1px 0 rgba(255,255,255,.06);
  color: #fff;
  transition: transform var(--dur-slow) var(--ease-glide), box-shadow var(--dur-slow) var(--ease-glide);
}
/* A physical artifact reacts to attention, a slight lift plus a diagonal
   sheen sweep on hover/focus, like light catching a laminated card. Purely
   transform/opacity, respects reduced motion. */
.wallet-balance-card:hover, .wallet-balance-card:focus-within { transform: translateY(-3px); box-shadow: 0 32px 58px -18px rgba(10,10,14,.6), 0 4px 10px rgba(0,0,0,.2), inset 0 1px 0 rgba(255,255,255,.08); }
.wallet-balance-card::after {
  content: ""; position: absolute; inset: 0; z-index: 3; pointer-events: none;
  background: linear-gradient(115deg, transparent 0%, transparent 42%, rgba(255,255,255,.14) 50%, transparent 58%, transparent 100%);
  transform: translateX(-130%); transition: transform .7s var(--ease-glide);
}
.wallet-balance-card:hover::after, .wallet-balance-card:focus-within::after { transform: translateX(0%); }
@media (prefers-reduced-motion: reduce) { .wallet-balance-card, .wallet-balance-card::after { transition: none; } .wallet-balance-card:hover { transform: none; } }

.wallet-balance-card .wbc-top { display: flex; align-items: flex-start; justify-content: space-between; gap: 8px; }
.wallet-balance-card .wbc-brand { display: flex; align-items: center; gap: 8px; }
.wallet-balance-card .wbc-brand-mark { width: 26px; height: 26px; border-radius: 8px; object-fit: contain; box-shadow: 0 2px 6px rgba(0,0,0,.35); flex-shrink: 0; }
.wallet-balance-card .wbc-brand-word { font-family: var(--font-display); font-size: 16.5px; font-weight: 700; letter-spacing: -0.01em; }
.wallet-balance-card .wbc-pending {
  font-size: 10px; font-weight: 700; padding: 3px 8px; border-radius: 20px; flex-shrink: 0;
  background: rgba(255,255,255,.14); border: 1px solid rgba(255,255,255,.16); backdrop-filter: blur(2px);
}
.wallet-balance-card .wbc-eyebrow {
  display: block; margin-top: 16px; font-size: 10.5px; font-weight: 800; letter-spacing: .1em; text-transform: uppercase;
  color: rgba(255,255,255,.6);
}
/* EMV-style chip, a small, restrained realism cue rather than decoration. */
.wallet-balance-card .wbc-chip {
  width: 38px; height: 29px; border-radius: 7px; margin-top: 16px;
  background:
    repeating-linear-gradient(90deg, rgba(0,0,0,.22) 0 1.5px, transparent 1.5px 8px),
    linear-gradient(150deg, #F4E4B0 0%, #D9B848 45%, #9C7A1F 100%);
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.35), 0 1px 3px rgba(0,0,0,.35);
}
.wallet-balance-card .wbc-mid { margin-top: auto; }
.wallet-balance-card .wbc-balance-label { font-size: 11px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; color: rgba(255,255,255,.62); margin-bottom: 4px; }
.wallet-balance-card .wbc-balance { font-size: clamp(26px, 8vw, 32px); font-weight: 800; line-height: 1.05; letter-spacing: -.01em; }
.wallet-balance-card .wbc-bottom { margin-top: 18px; padding-top: 16px; border-top: 1px solid rgba(255,255,255,.14); display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.wallet-balance-card .wbc-name { font-size: 12.5px; font-weight: 700; letter-spacing: .02em; }
.wallet-balance-card .wbc-id {
  font-size: 12px; opacity: .78; letter-spacing: .06em; background: none; border: none; padding: 4px 6px; margin-left: -6px;
  color: inherit; font-family: inherit; font-weight: 600; cursor: pointer; display: inline-flex; align-items: center; gap: 5px;
  border-radius: 6px; transition: opacity var(--dur-fast) var(--ease-standard), background var(--dur-fast) var(--ease-standard);
}
.wallet-balance-card .wbc-id:hover, .wallet-balance-card .wbc-id:focus-visible { opacity: 1; background: rgba(255,255,255,.12); }
.wallet-balance-card .wbc-id .icon { opacity: .8; }
@media (max-width: 480px) { .wallet-balance-card { max-width: 240px; } }

/* ---- Wallet hero row: the card artifact plus a companion panel, so the
   fixed-width card never leaves a large dead area beside it on wide screens. ---- */
/* The card's height is fixed by its aspect-ratio; the row stretches its
   sibling to match that same height so the two sit on one visual baseline
   top AND bottom, rather than the panel trailing off wherever its own
   content happens to end, the "up too high" / uneven-height mismatch
   flagged in review. The card itself opts out of the stretch (align-self:
   start) so it keeps its own aspect ratio instead of being deformed. */
.wallet-hero-row { display: grid; grid-template-columns: minmax(220px, 272px) 1fr; gap: 20px; align-items: stretch; margin-bottom: 20px; }
.wallet-hero-row .wallet-balance-card { margin-bottom: 0; align-self: start; }
@media (max-width: 900px) { .wallet-hero-row .wallet-balance-card { justify-self: center; } }
/* Content flows top-down at a fixed rhythm rather than `space-between`, 
   space-between was designed for two short sections filling a stretched
   panel, and left a dead gap once the panel's third section (recent
   activity) is what actually fills the height with real content. */
.wallet-side-panel { display: flex; flex-direction: column; gap: 16px; }
.wallet-side-panel .grid { flex: none; }
.wallet-side-panel .wallet-recent { flex: 1; display: flex; flex-direction: column; min-height: 0; }
.wallet-side-panel .wallet-recent .wtx-list { flex: 1; }
@media (max-width: 900px) { .wallet-hero-row { grid-template-columns: 1fr; align-items: start; } }

/* ---- Wallet actions: icon-first controls, not a row of full-width text
   buttons that read as "a collection of buttons" rather than a wallet. One
   component at every breakpoint, mobile gets larger touch targets via the
   media query below, not a shrunk-down copy of the desktop control. ---- */
.wallet-actions { display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px; }
/* Views with fewer than four actions (e.g. the volunteer allowance's single
   "Scan to pay") get a track per action instead of stretching one button
   across four empty-looking columns. */
.wallet-actions:has(.wa-btn:only-child) { grid-template-columns: 1fr; max-width: 220px; }
.wallet-actions:has(.wa-btn:nth-child(2):last-child) { grid-template-columns: repeat(2, 1fr); }
.wallet-actions:has(.wa-btn:nth-child(3):last-child) { grid-template-columns: repeat(3, 1fr); }
.wa-btn {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 7px;
  padding: 14px 4px; border-radius: var(--radius-md); border: 1px solid var(--border);
  background: var(--surface); color: var(--text-primary); cursor: pointer; font-family: inherit;
  font-size: 12px; font-weight: 700;
  transition: transform var(--dur-fast) var(--ease-standard), border-color var(--dur-fast) var(--ease-standard),
    box-shadow var(--dur-fast) var(--ease-standard);
}
.wa-btn .wa-ico {
  display: flex; align-items: center; justify-content: center; width: 38px; height: 38px; border-radius: 50%;
  background: var(--accent-soft); color: var(--accent-ink);
  transition: background var(--dur-fast) var(--ease-standard), color var(--dur-fast) var(--ease-standard), transform var(--dur-fast) var(--ease-standard);
}
.wa-btn .wa-label { line-height: 1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%; }
.wa-btn:hover:not(:disabled) { border-color: var(--accent); transform: translateY(-2px); box-shadow: var(--shadow-1); }
.wa-btn:hover:not(:disabled) .wa-ico { background: var(--accent); color: var(--accent-contrast); transform: scale(1.06); }
.wa-btn:active:not(:disabled) { transform: translateY(0) scale(0.96); transition-duration: 0.08s; }
.wa-btn:focus-visible { outline: none; box-shadow: inset 0 0 0 2px var(--accent); }
.wa-btn:disabled { opacity: 0.42; cursor: not-allowed; }
@media (max-width: 640px) {
  .wa-btn { padding: 15px 2px; border-radius: var(--radius-lg); }
  .wa-btn .wa-ico { width: 44px; height: 44px; }
  .wa-btn .wa-label { font-size: 11.5px; }
}

/* ---- Recent activity, living inside the wallet's own card as a compact
   ledger rather than a disconnected stack of little cards below it. Spacing
   above it comes from the panel's own flex `gap`, not a margin here, since
   this now nests inside .wallet-side-panel. ---- */
.wr-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 4px; }
.wr-head h3 { margin: 0; }
.wtx-list { display: flex; flex-direction: column; }
.wtx-row {
  display: flex; align-items: center; gap: 12px; padding: 12px 6px; margin: 0 -6px;
  border-bottom: 1px solid var(--border); border-radius: var(--radius-sm);
  transition: background var(--dur-fast) var(--ease-standard);
}
.wtx-row:last-child { border-bottom: none; }
.wtx-row:hover { background: var(--surface-mist, var(--surface-alt)); }
.wtx-ico { width: 34px; height: 34px; border-radius: 50%; display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
.wtx-ico.tone-good { background: color-mix(in srgb, var(--status-good) 18%, transparent); color: var(--status-good); }
.wtx-ico.tone-slate { background: var(--surface-alt); color: var(--text-secondary); }
.wtx-info { min-width: 0; flex: 1; }
.wtx-label { font-size: 13.5px; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.wtx-meta { font-size: 11.5px; color: var(--text-muted); margin-top: 2px; }
.wtx-right { text-align: right; flex-shrink: 0; }
.wtx-amount { font-weight: 700; font-size: 13.5px; font-variant-numeric: tabular-nums; }
.wtx-amount.credit { color: var(--status-good); }
@media (max-width: 640px) {
  .wtx-row { padding: 13px 4px; }
  .wtx-label { font-size: 13px; }
}

/* ---- Compact stat bar: supporting context, not six competing "heroes".
   One clear focal element per screen earns a card; the rest is a quiet,
   scannable line, per the anti-generic "no giant KPIs with no context"
   and "no endless equal-sized cards" rules. ---- */
.stat-bar {
  display: flex; flex-wrap: wrap; align-items: center; gap: 4px 18px;
  padding: 12px 16px; margin-bottom: 16px; background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius-md); font-size: 12.5px;
}
.stat-bar .stat-item { display: inline-flex; align-items: baseline; gap: 5px; color: var(--text-secondary); white-space: nowrap; }
.stat-bar .stat-item strong { font-size: 14px; color: var(--text-primary); font-variant-numeric: tabular-nums; }
.stat-bar .stat-item.warn strong { color: var(--status-warning); }
.stat-bar .stat-item .icon { opacity: .7; }

/* ---- Practitioner agenda / week grid ---- */
.next-card { border-left: 3px solid var(--accent); }
.eyebrow { font-size: 10.5px; text-transform: uppercase; letter-spacing: .07em; font-weight: 700; color: var(--accent); }
.timeline-list { display: flex; flex-direction: column; gap: 7px; }
.tl-row {
  display: flex; align-items: center; gap: 14px; background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius-md); padding: 11px 14px; cursor: pointer; transition: border-color var(--dur-fast) var(--ease-standard), transform var(--dur-fast) var(--ease-standard);
}
.tl-row:hover { border-color: var(--accent); transform: translateX(2px); }
.tl-row.cancelled, .tl-row.no_show { opacity: .62; }
.tl-time { font-weight: 700; font-size: 13px; width: 68px; flex-shrink: 0; }
.tl-body { flex: 1; min-width: 0; }
.tl-status { flex-shrink: 0; }
.seg-control { display: inline-flex; background: var(--surface-alt); border-radius: var(--radius-sm); padding: 3px; gap: 2px; }
.seg-control button { border: none; background: none; padding: 6px 13px; border-radius: 5px; font-size: 12.5px; font-weight: 600; color: var(--text-secondary); }
.seg-control button.active { background: var(--surface); color: var(--text-primary); box-shadow: var(--shadow-1); }
.week-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 12px; }
@media (max-width: 900px) { .week-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 560px) { .week-grid { grid-template-columns: 1fr; } }
.wk-col { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-md); padding: 10px; }
.wk-head { display: flex; flex-direction: column; padding-bottom: 8px; border-bottom: 1px solid var(--border); margin-bottom: 8px; }
.wk-item {
  display: flex; flex-direction: column; width: 100%; text-align: left; gap: 1px; padding: 7px 9px;
  border: 1px solid var(--border); border-left: 3px solid var(--series-1); border-radius: var(--radius-sm);
  background: var(--surface); margin-bottom: 6px; font-size: 12px;
}
.wk-item:hover { border-color: var(--accent); }
.wk-item.completed { border-left-color: var(--status-good); }
.wk-item.cancelled, .wk-item.no_show { border-left-color: var(--status-critical); opacity: .6; }
.wk-time { font-weight: 700; }
.wk-name { color: var(--text-primary); }
.wk-svc { color: var(--text-muted); font-size: 11px; }
.wk-empty { color: var(--text-muted); font-size: 12px; padding: 10px 0; text-align: center; }
.wk-block { font-size: 11px; background: var(--surface-alt); border-radius: 5px; padding: 5px 8px; color: var(--text-muted); margin-top: 4px; }
.agenda { display: flex; flex-direction: column; gap: 4px; }
.ag-row { display: flex; gap: 12px; align-items: stretch; }
.ag-time { width: 74px; flex-shrink: 0; font-size: 12px; color: var(--text-muted); padding-top: 12px; font-weight: 600; }
.ag-card { flex: 1; background: var(--surface); border: 1px solid var(--border); border-left: 3px solid var(--border-strong); border-radius: var(--radius-md); padding: 10px 13px; }
.ag-card.ghost { background: transparent; border-style: dashed; border-left-style: dashed; display: flex; align-items: center; }
.ag-row.booked { cursor: pointer; }
.ag-row.booked:hover .ag-card { border-color: var(--accent); }
.ag-row.unavailable .ag-card { opacity: .55; }
.day-pill .dcount { font-size: 10px; color: var(--text-muted); margin-top: 1px; }
.day-pill.active .dcount { color: color-mix(in srgb, var(--accent-contrast) 78%, transparent); }
.block-list { display: flex; flex-direction: column; gap: 8px; }
.block-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 10px 12px; background: var(--surface-alt); border-radius: var(--radius-md); }
.offering-card .meta-row, .meta-row {
  display: flex; gap: 14px; flex-wrap: wrap; font-size: 12px; color: var(--text-muted); margin-top: 8px;
}
.meta-row span { display: inline-flex; align-items: center; gap: 4px; }
.notif-row { display: flex; gap: 11px; align-items: flex-start; }
.nr-icon { width: 30px; height: 30px; border-radius: var(--radius-sm); background: var(--surface-alt); color: var(--text-muted); display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
.chip-row { display: flex; gap: 5px; flex-wrap: wrap; margin-top: 8px; }
.warn-text { color: var(--status-warning); font-weight: 600; }
.danger-text { color: var(--status-critical); }
.btn.danger-text:hover { background: #FCEBEB; }
.rule-card.inactive { opacity: .68; }
.switch { display: inline-flex; align-items: center; gap: 7px; cursor: pointer; }
.switch input { position: absolute; opacity: 0; width: 0; height: 0; }
.switch-track {
  width: 36px; height: 20px; border-radius: 999px; background: var(--border-strong); position: relative; transition: background var(--dur-fast) var(--ease-standard); flex-shrink: 0;
}
.switch-track::after {
  content: ""; position: absolute; top: 2px; left: 2px; width: 16px; height: 16px; border-radius: 50%;
  background: #fff; transition: transform var(--dur-fast) var(--ease-standard); box-shadow: var(--shadow-1);
}
.switch input:checked + .switch-track { background: var(--status-good); }
.switch input:checked + .switch-track::after { transform: translateX(16px); }
.switch input:focus-visible + .switch-track { outline: 2px solid var(--accent); outline-offset: 2px; }
.msg-preview { background: var(--surface-alt); border-radius: var(--radius-md); padding: 16px; }
.msg-subject { font-weight: 700; margin-bottom: 10px; padding-bottom: 10px; border-bottom: 1px solid var(--border); }
.msg-body { white-space: pre-wrap; font-family: inherit; font-size: 13px; line-height: 1.6; margin: 0; }
.empty-state.restricted .es-icon, .empty-state.error-boundary .es-icon { color: var(--status-warning); }
.slot-btn { position: relative; }
.slot-x { display: block; font-size: 9px; text-transform: uppercase; letter-spacing: .04em; margin-top: 1px; }
.slot-grid.compact { grid-template-columns: repeat(auto-fill, minmax(84px, 1fr)); }
.sticky-cta { margin-top: 22px; }
.btn-lg { padding: 12px 22px; font-size: 14.5px; }

/* ---- Touch targets: never below 44px on touch devices ---- */
@media (hover: none) and (pointer: coarse) {
  .btn-sm { min-height: 40px; padding: 9px 14px; }
  .pg-btn, .modal-close, .tb-icon-btn { min-height: 44px; min-width: 44px; }
  .link-btn, .crumbs button { min-height: 32px; }
  .role-switch-light button { min-height: 40px; }
}

/* ---- Tables become cards on small screens ---- */
@media (max-width: 760px) {
  .data-table thead { display: none; }
  .data-table, .data-table tbody, .data-table tr, .data-table td { display: block; width: 100%; }
  .data-table tr {
    border: 1px solid var(--border); border-radius: var(--radius-md); margin-bottom: 10px;
    padding: 6px 4px; background: var(--surface);
  }
  .data-table td {
    display: flex; justify-content: space-between; gap: 14px; align-items: center;
    border-bottom: 1px solid var(--gridline); padding: 9px 12px; text-align: right;
  }
  .data-table td:last-child { border-bottom: none; }
  .data-table td::before {
    content: attr(data-label); font-size: 11px; text-transform: uppercase; letter-spacing: .04em;
    color: var(--text-muted); font-weight: 700; text-align: left; flex-shrink: 0;
  }
  .data-table td.hide-sm { display: none; }
  .data-table td.col-check::before, .data-table td.col-actions::before { content: ""; }
  .data-table .cell-person { justify-content: flex-end; }
  .table-toolbar { padding: 10px; }
  .table-search { max-width: none; }
  .table-filter select { max-width: none; flex: 1; }
  /* Toasts must never sit on top of the mobile tab bar */
  #toastRoot { left: 12px; right: 12px; bottom: calc(84px + env(safe-area-inset-bottom)); align-items: stretch; }
  .toast { max-width: none; }
  .side-panel { width: 100%; }
  .modal-overlay { padding: 0; align-items: flex-end; }
  .modal-box { max-width: none; border-radius: 18px 18px 0 0; max-height: 92vh; }
  .palette-overlay { padding: 8vh 10px 10px; }
  .detail-list > div { flex-direction: column; gap: 2px; }
  .detail-list dd { text-align: left; }
  .ag-time { width: 58px; }
  .tl-time { width: 58px; }
}

/* Admin content area should use the full width on large screens for dense tables */
@media (min-width: 1400px) { .content-area { max-width: 1440px; } }
.more-list { display: flex; flex-direction: column; }
.mono { white-space: nowrap; }
@media (max-width: 760px) { .mono { white-space: normal; } }

/* ---- Admin dashboard alerts + drill-through KPIs ---- */
.alert-strip { display: grid; grid-template-columns: repeat(auto-fit, minmax(290px, 1fr)); gap: 10px; margin-bottom: 18px; }
.alert-card {
  display: flex; align-items: center; gap: 10px; padding: 11px 13px; border-radius: var(--radius-md);
  border: 1px solid; font-size: 13px; background: var(--surface);
}
.alert-card.warn { border-color: #F2DFBC; background: #FDF8EF; color: #8A5A06; }
.alert-card.info { border-color: #CFDDF4; background: #F4F8FE; color: #17508F; }
.alert-card .ac-text { flex: 1; }
.alert-card .btn { flex-shrink: 0; }
/* `button` is globally inline-flex, which would lay the KPI's label/value/sub
   out in a row, force block flow for the clickable variant. */
.kpi-link { display: block; text-align: left; width: 100%; position: relative; cursor: pointer; font: inherit; }
.kpi-link .kpi-go { position: absolute; top: 12px; right: 12px; color: var(--text-muted); opacity: 0; transition: opacity var(--dur-fast) var(--ease-standard), transform var(--dur-fast) var(--ease-standard); }
.kpi-link:hover { border-color: var(--accent); }
.kpi-link:hover .kpi-go { opacity: 1; transform: translateX(2px); }

/* ---- Master calendar grid ---- */
.cal-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 12px; }
.cal-col { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-md); padding: 11px; }
.cal-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; padding-bottom: 9px; border-bottom: 1px solid var(--border); margin-bottom: 9px; }
.cal-item {
  display: flex; flex-direction: column; gap: 2px; width: 100%; text-align: left; padding: 8px 10px;
  border: 1px solid var(--border); border-left: 3px solid var(--border-strong); border-radius: var(--radius-sm);
  background: var(--surface); margin-bottom: 7px; font-size: 12px;
}
.cal-item:hover { border-color: var(--accent); background: var(--surface-alt); }
.ci-time { font-weight: 700; font-size: 12px; }
.ci-name { color: var(--text-primary); }
.ci-svc { color: var(--text-muted); font-size: 11px; }
.ci-foot { display: flex; gap: 5px; flex-wrap: wrap; margin-top: 4px; }
.cal-free { display: flex; align-items: center; gap: 6px; font-size: 12px; color: var(--status-good); padding: 8px 0; }

/* ---- Daily operations kanban ---- */
.kanban { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 12px; align-items: start; }
@media (max-width: 1200px) { .kanban { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (max-width: 820px) { .kanban { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 560px) { .kanban { grid-template-columns: 1fr; } }
.kanban-col { background: var(--surface-alt); border-radius: var(--radius-md); padding: 11px; border: 1px solid var(--border); }
.kanban-col.attention { background: #FDF6F6; border-color: #F0D8D8; }
.kb-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.kb-head h4 { display: flex; align-items: center; gap: 6px; font-size: 12.5px; margin: 0; }
.kb-hint { font-size: 11px; color: var(--text-muted); margin: 2px 0 10px; }
.kanban-card {
  display: flex; flex-direction: column; gap: 2px; width: 100%; text-align: left; background: var(--surface);
  border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 10px; margin-bottom: 8px; font-size: 12px;
}
.kanban-card:hover { border-color: var(--accent); box-shadow: var(--shadow-1); }
/* .kc-open is the card's primary click target (opens the booking). It is a
   real <button> so the secondary .kc-action button below never has to nest
   inside another interactive element, both remain independently reachable
   by keyboard and assistive tech. */
.kc-open {
  display: flex; flex-direction: column; align-items: flex-start; text-align: left; gap: 2px;
  width: 100%; border: none; background: none; padding: 0; margin: 0; font: inherit; color: inherit; cursor: pointer;
}
.kc-time { font-weight: 700; }
.kc-name { font-weight: 600; }
.kc-meta { color: var(--text-muted); font-size: 11px; }
.kc-chips { display: flex; gap: 4px; flex-wrap: wrap; margin-top: 5px; }
.kc-action {
  display: inline-block; margin-top: 7px; font-size: 11.5px; font-weight: 700; color: var(--accent);
  background: none; border: 1px solid var(--accent); border-radius: var(--radius-pill); padding: 3px 10px; align-self: flex-start;
}
.kc-action:hover { background: var(--accent); color: #fff; }
.kb-empty { font-size: 12px; color: var(--text-muted); text-align: center; padding: 14px 0; }

/* ---- Smart match results ---- */
.match-card { margin-bottom: 14px; border-left: 3px solid var(--accent); }

/* ---- Chart accessibility: charts carry a text summary for screen readers ---- */
.bar-chart, .donut-legend, .hbar-row { font-variant-numeric: tabular-nums; }

/* ============================================================================
   SECTION IDENTITY, each area of the product carries a tone.
   Tones are UI chrome only. Status colours (good/warning/serious/critical) are
   never used here, so a section tint can never be read as a booking state.
   ========================================================================== */
.tone-terracotta { --tone-bg: #FDECDC; --tone-fg: #A34317; --tone-line: #E2672B; }
.tone-gold       { --tone-bg: #FDF3DC; --tone-fg: #8A5A06; --tone-line: #EDA100; }
.tone-aqua       { --tone-bg: #DCF2EA; --tone-fg: #0A6345; --tone-line: #1BAF7A; }
.tone-blue       { --tone-bg: #E4EDFA; --tone-fg: #17508F; --tone-line: #2A78D6; }
.tone-violet     { --tone-bg: #E9E6F7; --tone-fg: #3F3092; --tone-line: #4A3AA7; }
.tone-green      { --tone-bg: #E0F0DA; --tone-fg: #16691B; --tone-line: #008300; }
.tone-magenta    { --tone-bg: #FBE7EE; --tone-fg: #96305A; --tone-line: #E87BA4; }
.tone-slate      { --tone-bg: #ECEEF3; --tone-fg: #454C5E; --tone-line: #6B7183; }

/* ---- Page header ---- */
.page-header { margin-bottom: 22px; }
.page-header .ph-row { display: flex; align-items: flex-start; justify-content: space-between; gap: 18px; flex-wrap: wrap; }
.ph-lead { display: flex; align-items: flex-start; gap: 13px; min-width: 0; }
.ph-icon {
  width: 42px; height: 42px; border-radius: 13px; flex-shrink: 0;
  background: var(--tone-bg, var(--accent-soft)); color: var(--tone-fg, var(--accent));
  display: flex; align-items: center; justify-content: center;
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--tone-line, var(--accent)) 22%, transparent);
}
.ph-text { min-width: 0; }
.page-header h1 { font-size: 23px; letter-spacing: -0.02em; margin: 1px 0 0; }
.ph-sub { font-size: 13px; color: var(--text-secondary); margin: 3px 0 0; max-width: 68ch; line-height: 1.5; }
.ph-actions { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }
/* A hairline in the section tone anchors the header to its area */
.page-header::after {
  content: ""; display: block; height: 2px; border-radius: 2px; margin-top: 16px;
  background: linear-gradient(90deg, var(--tone-line, var(--accent)) 0%, color-mix(in srgb, var(--tone-line, var(--accent)) 18%, transparent) 42%, transparent 100%);
}
@media (max-width: 620px) {
  .ph-icon { width: 36px; height: 36px; border-radius: 11px; }
  .page-header h1 { font-size: 20px; }
  .ph-actions { width: 100%; }
  .ph-actions .btn { flex: 1; justify-content: center; }
}

/* ---- KPI tiles ---- */
.kpi { position: relative; overflow: hidden; border: 1px solid var(--border); padding-top: 17px; transition: border-color var(--dur-fast) var(--ease-standard), box-shadow var(--dur-fast) var(--ease-standard); }
.kpi::before { content: ""; position: absolute; top: 0; left: 0; right: 0; height: 3px; background: var(--tone-line, var(--border)); }
.kpi .kpi-ico {
  width: 20px; height: 20px; border-radius: 6px; display: inline-flex; align-items: center; justify-content: center;
  background: var(--tone-bg, var(--surface-alt)); color: var(--tone-fg, var(--text-muted)); flex-shrink: 0;
}
.kpi .kpi-value { font-variant-numeric: tabular-nums; }
.kpi.accent { box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--tone-line, var(--accent)) 30%, transparent); }
.kpi.accent::before { height: 4px; }

/* ---- Empty states with warmth ---- */
.empty-state { text-align: center; padding: 46px 24px; color: var(--text-secondary); animation: viewIn var(--dur-base) var(--ease-glide); }
.es-art {
  width: 84px; height: 84px; margin: 0 auto 16px; border-radius: 50%;
  background: var(--tone-bg, var(--surface-alt)); display: flex; align-items: center; justify-content: center;
  position: relative;
}
.es-art::before {
  content: ""; position: absolute; inset: -8px; border-radius: 50%;
  border: 1px dashed color-mix(in srgb, var(--tone-line, var(--accent)) 35%, transparent);
}
.es-icon { color: var(--tone-fg, var(--accent)); display: flex; }
.empty-state h3 { font-size: 16px; margin-bottom: 6px; }
.empty-state p { max-width: 42ch; margin: 0 auto 16px; font-size: 13.5px; line-height: 1.6; }

/* ---- Sidebar picks up the section tone when active ---- */
.side-link.active { background: color-mix(in srgb, var(--tone-line, var(--accent)) 16%, transparent); border-left-color: var(--tone-line, var(--accent)); }
.side-link.active .icon { color: color-mix(in srgb, var(--tone-line, var(--accent)) 55%, #fff); }

/* ---- Light-shell nav takes the tone of the destination ---- */
.lt-link.active { background: var(--accent); }
.lt-link .icon { opacity: .8; }
.lt-link.active .icon { opacity: 1; }

/* ---- Numbers align in tables ---- */
.data-table td, .data-table th { font-variant-numeric: tabular-nums; }
.data-table td:not(.ta-right):not(.ta-center) { font-variant-numeric: normal; }
.ta-right, .hbar-val, .bar-val, .leg-val, .mk-val { font-variant-numeric: tabular-nums; }

/* ---- Section headings inside pages ---- */
.section-title { display: flex; align-items: center; justify-content: space-between; gap: 14px; margin: 26px 0 12px; flex-wrap: wrap; }
.section-title:first-child { margin-top: 0; }
.section-title h2, .section-title h3 { margin: 0; }
.card > h3:first-child, .card > .section-title:first-child { margin-top: 0; }

/* ============================================================================
   ATTENDEE WARMTH, hero, category tiles, confirmation ticket
   ========================================================================== */
.hero {
  position: relative; overflow: hidden; display: flex; align-items: center; justify-content: space-between;
  gap: 24px; padding: 38px 36px;
}
.hero-body { position: relative; z-index: 1; max-width: 640px; }
/* On the sunlit wash the eyebrow no longer needs a scrim to survive, it needs
   only to read as a label. White at 62% lifts it a shade above the wash without
   introducing a second colour, and the tangerine ink carries the brand. */
.hero-eyebrow {
  display: inline-block; font-size: 11px; font-weight: 700; letter-spacing: .09em; text-transform: uppercase;
  color: var(--ink-tangerine); background: rgba(255,255,255,0.62); border: 1px solid rgba(244,154,98,0.28);
  padding: 5px 11px; border-radius: var(--radius-pill); margin-bottom: 14px;
}
.hero h1 { font-size: 32px; line-height: 1.15; margin-bottom: 8px; }
.hero p { font-size: 15px; max-width: 52ch; }
.hero-search { margin: 18px 0 16px; max-width: 480px; }
.hero-search .search-input {
  background: #FFFFFF; border-color: var(--wash-line); padding: 6px 6px 6px 14px; border-radius: var(--radius-pill);
  box-shadow: 0 2px 10px rgba(160,104,52,0.07);
}
.hero-search .search-input input { color: var(--wash-ink); font-size: 14px; }
.hero-search .search-input input::placeholder { color: #8A7663; }
.hero-search .search-input .icon { color: var(--ink-tangerine); }
/* The filled accent button, not a white one: on a light wash a white fill has
   nothing to sit against, and the search action is the hero's second CTA. */
.hero-search .btn-primary { background: var(--accent); color: var(--accent-contrast); border-radius: var(--radius-pill); }
.hero-search .btn-primary:hover { background: var(--accent-strong); color: #FFF6EC; }
.hero-actions { display: flex; gap: 10px; flex-wrap: wrap; }
.btn-hero-ghost {
  display: inline-flex; align-items: center; gap: 7px; border: 1px solid color-mix(in srgb, var(--accent) 34%, transparent);
  background: rgba(255,255,255,0.74); color: var(--ink-tangerine); padding: 12px 20px; border-radius: var(--radius-pill);
  font-size: 14px; font-weight: 600;
}
.btn-hero-ghost:hover { background: #FFFFFF; border-color: color-mix(in srgb, var(--accent) 55%, transparent); }
.hero-stats { display: flex; gap: 20px; flex-wrap: wrap; margin-top: 20px; font-size: 12.5px; color: var(--wash-ink-soft); }
.hero-stats span { display: inline-flex; align-items: center; gap: 6px; }
/* Concentric rings, a calm nod to sound/breath rather than clip-art. Drawn in
   the wash's own warm tones at low alpha so they read as light rather than as
   an applied graphic; the old white strokes disappeared on a light ground. */
.hero-art { position: absolute; right: -60px; top: 50%; transform: translateY(-50%); width: 340px; height: 340px; z-index: 0; }
.hero-art span { position: absolute; inset: 0; border-radius: 50%; border: 1.5px solid rgba(244,154,98,0.20); }
.hero-art span:nth-child(2) { inset: 42px; border-color: rgba(246,177,154,0.26); }
.hero-art span:nth-child(3) { inset: 84px; background: rgba(255,255,255,0.34); border-color: transparent; }
@media (max-width: 860px) {
  .hero { padding: 28px 22px; }
  .hero h1 { font-size: 25px; }
  .hero-art { display: none; }
  .hero-actions .btn, .hero-actions .btn-hero-ghost { flex: 1; justify-content: center; }
}

/* Next-session strip */
.next-up {
  display: flex; align-items: center; gap: 14px; flex-wrap: wrap; padding: 14px 18px; margin-bottom: 26px;
  background: var(--tone-bg); border: 1px solid color-mix(in srgb, var(--tone-line) 26%, transparent);
  border-radius: var(--radius-lg);
}
.nu-icon {
  width: 40px; height: 40px; border-radius: 12px; background: var(--surface); color: var(--tone-fg);
  display: flex; align-items: center; justify-content: center; flex-shrink: 0;
}
.nu-body { flex: 1; min-width: 200px; display: flex; flex-direction: column; gap: 1px; }
.nu-label { font-size: 10.5px; font-weight: 700; letter-spacing: .07em; text-transform: uppercase; color: var(--tone-fg); }

/* Category tiles, flexbox, not a fixed grid track count, so a ragged last
   row (e.g. 5 items over 2 columns) still stretches to fill instead of
   leaving an odd tile stranded at half width. */
.cat-grid { display: flex; flex-wrap: wrap; gap: 12px; }
.cat-grid > .cat-card { flex: 1 1 180px; }
.cat-card {
  display: flex; flex-direction: column; align-items: flex-start; gap: 5px; text-align: left; padding: 16px;
  background: var(--surface); border: 1px solid var(--border); border-radius: 16px;
  transition: transform var(--dur-base) var(--ease-glide), border-color var(--dur-base) var(--ease-glide), box-shadow var(--dur-base) var(--ease-glide);
}
.cat-card:hover { transform: translateY(-3px); border-color: var(--tone-line); box-shadow: var(--shadow-2); }
.cat-ico {
  width: 38px; height: 38px; border-radius: 12px; background: var(--tone-bg); color: var(--tone-fg);
  display: flex; align-items: center; justify-content: center; margin-bottom: 4px;
}
.cat-card strong { font-size: 13.5px; line-height: 1.35; }

/* QR entry cards */
.qr-card { display: flex; flex-direction: column; align-items: flex-start; gap: 5px; text-align: left; transition: transform var(--dur-base) var(--ease-glide), border-color var(--dur-base) var(--ease-glide); }
.qr-card:hover { transform: translateY(-2px); border-color: var(--accent); }
.qr-ico { width: 34px; height: 34px; border-radius: 10px; background: var(--tone-bg); color: var(--tone-fg); display: flex; align-items: center; justify-content: center; margin-bottom: 4px; }

/* ---- Confirmation ticket ---- */
.confirm-hero { text-align: center; padding: 30px 20px 26px; }
.ch-badge {
  width: 82px; height: 82px; margin: 0 auto 16px; border-radius: 50%;
  background: #E0F0DA; color: #16691B; display: flex; align-items: center; justify-content: center;
  animation: pop .34s cubic-bezier(.2,.8,.3,1.2);
}
@keyframes pop { from { transform: scale(.7); opacity: 0; } to { transform: scale(1); opacity: 1; } }
.confirm-hero h1 { font-size: 27px; margin-bottom: 6px; }
.confirm-hero p { color: var(--text-secondary); max-width: 46ch; margin: 0 auto 16px; }
.ch-ref {
  display: inline-flex; align-items: center; gap: 10px; background: var(--surface-alt);
  border: 1px dashed var(--border-strong); border-radius: var(--radius-pill); padding: 8px 18px;
}
.ch-ref span { font-size: 11px; text-transform: uppercase; letter-spacing: .06em; color: var(--text-muted); font-weight: 700; }
.ch-ref strong { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 14px; }
.ticket {
  display: flex; background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg);
  overflow: hidden; box-shadow: var(--shadow-1); margin-bottom: 18px;
}
.ticket-main { flex: 1; padding: 22px 24px; min-width: 0; }
.ticket-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 16px; margin-top: 20px; }
.ticket-grid > div { display: flex; flex-direction: column; gap: 2px; }
.ticket-grid span { font-size: 10.5px; text-transform: uppercase; letter-spacing: .06em; color: var(--text-muted); font-weight: 700; }
.ticket-grid strong { font-size: 14px; }
.ticket-grid em { font-style: normal; font-size: 12px; color: var(--text-muted); }
.ticket-stub {
  width: 168px; flex-shrink: 0; border-left: 2px dashed var(--border-strong); padding: 22px 18px;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 10px;
  background: var(--surface-alt); text-align: center;
}
.qr { display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px; width: 84px; height: 84px; }
.qr span { background: transparent; border-radius: 1px; }
.qr span.on { background: var(--text-primary); }
@media (max-width: 700px) {
  .ticket { flex-direction: column; }
  .ticket-stub { width: 100%; border-left: none; border-top: 2px dashed var(--border-strong); flex-direction: row; justify-content: flex-start; gap: 16px; text-align: left; }
}
.ticket-head { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; }
.ticket-head h2 { margin: 0 0 2px; }
.qr { display: grid; gap: 1.5px; width: 92px; height: 92px; }
/* Kanban columns take a tone so the board reads from a distance */
.kanban-col { background: var(--surface); border-top: 3px solid var(--tone-line, var(--border)); }
.kanban-col.attention { background: var(--tone-bg); }
.kb-ico { color: var(--tone-fg, var(--text-muted)); display: inline-flex; vertical-align: -2px; }
.kb-head h4 { color: var(--text-primary); }

/* Filter chips */
.filter-row { display: flex; gap: 7px; flex-wrap: wrap; margin-bottom: 20px; }
.pill-filter {
  border: 1px solid var(--border-strong); background: var(--surface); color: var(--text-secondary);
  padding: 7px 13px; border-radius: var(--radius-pill); font-size: 12.5px; font-weight: 600;
  transition: background var(--dur-fast) var(--ease-standard), border-color var(--dur-fast) var(--ease-standard), color var(--dur-fast) var(--ease-standard);
}
.pill-filter:hover { border-color: var(--tone-line); color: var(--tone-fg); }
.pill-filter.on { background: var(--tone-bg); border-color: var(--tone-line); color: var(--tone-fg); }

/* The global `button { align-items:center }` centres children in every
   column-flow button component. These are content cards, align them left. */
.kanban-card, .cal-item, .wk-item, .cat-card, .qr-card, .palette-item .pi-label {
  align-items: flex-start; text-align: left;
}
.kanban-card .kc-chips { align-self: stretch; }

/* ============================================================================
   LAYOUT SYSTEM, scrollbars, rhythm, symmetry, responsive behaviour.
   This block is authoritative and intentionally last in the sheet.
   ========================================================================== */

/* ---- 1. Scrollbars are hidden everywhere; scrolling itself is untouched ---- */
* { scrollbar-width: none; -ms-overflow-style: none; }
*::-webkit-scrollbar { width: 0; height: 0; display: none; }
/* Getting this pair right took two wrong turns, so the reasoning is spelled
   out in full:
   1. `overflow-x: clip` alone (overflow-y left at its default `visible`) is
      unsafe, per spec, pairing a non-visible overflow-x with a visible
      overflow-y forces the *used* overflow-y to `auto`, and with BOTH html
      and body doing this independently, two separate phantom scroll
      containers appear, neither of which is the element the browser
      actually scrolls (`document.scrollingElement`), so `position: sticky`
      on the mobile top bar bound to the wrong one and scrolled away with
      the page.
   2. `overflow: clip` on both axes "fixed" sticky but broke scrolling
      outright, `clip`, unlike `hidden`/`auto`, is never scrollable at all
      (not even programmatically), so the whole page froze.
   The actual fix: exactly one element gets to be the real scroll container.
   `html` is `document.scrollingElement` in standards mode, so it alone
   carries the explicit, non-mixed pair `overflow-x: clip` (cuts horizontal
   bleed, no scrollbar) + `overflow-y: auto` (real vertical scrolling).
   `body` stays fully `visible` on both axes so it never competes to become
   a second scroll container, sticky descendants then have exactly one
   scrolling ancestor to bind to, and it's the right one. */
html { overflow-x: clip; overflow-y: auto; }
body { overflow: visible; }
@supports not (overflow-x: clip) { html { overflow-x: hidden; } }

/* ---- 2. Spacing rhythm, one vertical scale, applied consistently ---- */
:root {
  --gap: 16px;          /* grid + card gutter */
  --stack: 22px;        /* space between major blocks */
  --pad-card: 20px;     /* card interior */
  --pad-card-sm: 16px;
}
.grid { gap: var(--gap); align-items: stretch; }
.card { padding: var(--pad-card); }
.card-flat { padding: var(--pad-card-sm); }
.card + .card, .card + .grid, .grid + .card, .grid + .grid,
.table-block + .card, .card + .table-block, .kpi-strip + .card { margin-top: var(--stack); }
.content-area > *:last-child, .container > *:last-child { margin-bottom: 0; }

/* ---- 3. Symmetry, cards in a grid share a height and align their footers ---- */
.grid > .card, .grid > .card-flat, .grid > .practitioner-card, .grid > .cat-card, .grid > .qr-card {
  display: flex; flex-direction: column; min-height: 0;
}
.grid > .card-flat > p, .grid > .card > p { flex: 0 0 auto; }
/* Push any trailing action row to the bottom so rows of cards line up */
.grid > .card-flat > .flex:last-child, .grid > .card > .flex:last-child,
.grid > .card-flat > .btn:last-child, .offering-card .flex:last-child { margin-top: auto; }
.practitioner-card .pc-body { flex: 1; }
.practitioner-card .pc-body .flex:last-child { margin-top: auto; padding-top: 8px; }
/* Equal-height KPI tiles regardless of whether they carry a sub-line */
.kpi-strip > .kpi { display: flex; flex-direction: column; }
.kpi-strip > .kpi .kpi-sub { margin-top: auto; padding-top: 4px; }

/* ---- 4. Consistent inner rhythm ---- */
.card > h3:first-child, .card > h2:first-child { margin-top: 0; }
.card > .divider:first-of-type { margin-top: 10px; }
.detail-list + .btn, .detail-list + .flex { margin-top: 14px; }
h2 + .grid, h3 + .grid, .section-title + .grid, .section-title + .table-block { margin-top: 0; }

/* ---- 5. Light-shell top bar: never overflows, degrades in clear steps ---- */
.lt-inner { flex-wrap: nowrap; min-width: 0; }
.lt-actions { min-width: 0; }
.persona-select select { max-width: 130px; text-overflow: ellipsis; }
@media (max-width: 1180px) {
  .lt-inner { padding-left: 16px; padding-right: 16px; }
  .lt-navbar .lt-nav { padding-left: 12px; padding-right: 12px; }
}
@media (max-width: 1024px) {
  .kbd-hint kbd { display: none; }
  .lt-search { max-width: 240px; }
}
@media (max-width: 900px) {
  .lt-search { display: none; }
  .light-topbar .tb-demo-btn .tb-demo-label { display: none; }
  .light-topbar .tb-demo-btn { padding: 0; width: 38px; justify-content: center; }
  .role-switch-light .rsl-text { display: none; }
  .role-switch-light button { padding: 8px 10px; }
  .persona-select { display: none; }
}

/* ---- 6. Mobile: width-driven, not pointer-driven ---- */
@media (max-width: 760px) {
  :root { --gap: 12px; --stack: 18px; --pad-card: 16px; --pad-card-sm: 14px; }
  /* Touch targets. Keyed to viewport width so it holds in every browser. */
  .btn, .btn-sm, .pg-btn, .pill-filter, .tabs button, .day-pill,
  .lt-link, .seg-control button, .role-switch-light button, .tb-icon-btn, .tb-demo-btn {
    min-height: 44px;
  }
  .btn-sm { padding: 10px 14px; font-size: 13px; }
  .brand-light { min-height: 44px; }
  .mobile-tabbar button { min-height: 52px; }
  .checkline input, .col-check input, .checkbox-field input { width: 20px; height: 20px; }
  .crumbs button, .link-btn { min-height: 36px; }
  .qty-stepper button { width: 40px; height: 40px; font-size: 17px; }
  .qty-stepper span { min-width: 32px; font-size: 14px; }
  /* Full-width primary actions read better than half-width ones on a phone */
  .ph-actions { width: 100%; }
  .ph-actions > .btn, .ph-actions > .seg-control, .ph-actions > div { flex: 1; }
  .flex.gap-12 > .btn, .flex.gap-8 > .btn { flex: 1 1 auto; justify-content: center; }
  /* Any inline flex action/status row (page-header right slots, status rows,
     toolbar clusters) must wrap rather than force the page wider than the
     viewport, this has no effect at all when the row already fits on one
     line, so it is safe everywhere this utility combo is used. */
  .flex.gap-8, .flex.gap-12, .flex.gap-16 { flex-wrap: wrap; }
  .ph-row { row-gap: 10px; }
  /* Two fields side by side (corner style + theme name, logo + app icon,
     etc.) reads as cramped on a phone even once each half technically fits, 
     and a couple of them (file inputs especially) carry native browser
     chrome that can't shrink to a fractional column width at all. Stack
     instead of squeezing, per the "design mobile intentionally" rule. */
  .field-row { flex-direction: column; gap: 0; }
  .field-row .field { width: 100%; min-width: 0; }
  /* Nothing may exceed the viewport. Horizontal clipping is handled once,
     globally, by `html` above, it does not need repeating on every nested
     wrapper. Redundantly restricting overflow here (on .container /
     .content-area, which are never meant to scroll independently) would
     re-create the exact two-scroll-container bug the `html`/`body` split
     above exists to avoid, just one level deeper, breaking sticky elements
     nested inside them (e.g. .booking-summary) the same way. */
  html, body { max-width: 100%; }
  .container, .content-area { max-width: 100%; }
  .card, .card-flat, .table-block, .kpi { max-width: 100%; }
  .week-strip { max-width: none; grid-template-columns: repeat(4, 1fr); gap: 8px; }
  .day-pill { padding: 8px 4px; }
  .day-pill .dnum { font-size: 16px; }
  .booking-layout { display: block; }
  .booking-summary { position: static; margin-top: 18px; }
  .steps { gap: 4px; }
  .agenda .ag-card { padding: 9px 11px; }
  .detail-list dd { font-size: 13px; }
  .ticket-grid { grid-template-columns: repeat(2, 1fr); }
  .hero-stats { gap: 10px 16px; font-size: 12px; }
  .search-input { padding: 9px 12px; }
  .search-input input { font-size: 16px; } /* prevents iOS zoom-on-focus */
  input, select, textarea { font-size: 16px; }
  .field label { font-size: 13px; }
}
@media (max-width: 420px) {
  .cat-grid > .cat-card { flex-basis: 140px; }
  .ticket-grid { grid-template-columns: 1fr; }
  .mini-kpis { flex-direction: column; }
  .filter-row { flex-wrap: nowrap; overflow-x: auto; padding-bottom: 4px; }
  .pill-filter { white-space: nowrap; flex-shrink: 0; }
}

/* ---- 7. Tablet: two columns rather than a stretched three ---- */
@media (max-width: 980px) and (min-width: 761px) {
  .booking-layout { grid-template-columns: 1fr; }
  .booking-summary { position: static; }
}
@media (max-width: 760px) {
  /* The admin sidebar becomes a touch drawer, its rows need real targets */
  .sidebar { width: 280px; }
  .side-link { min-height: 46px; padding: 11px 12px; font-size: 14px; }
  .role-pill { min-height: 44px; }
  .sidebar .brand { min-height: 56px; }
  .side-group-label { margin-top: 18px; }
}
/* The whole cell is the checkbox target, not just the 20px box */
.check-target { display: flex; align-items: center; justify-content: center; min-width: 40px; min-height: 40px; margin: -8px; cursor: pointer; }
.check-target input { width: 18px; height: 18px; accent-color: var(--accent); }
@media (max-width: 760px) { .check-target { justify-content: flex-end; min-height: 44px; } }
.switch { min-height: 40px; }
@media (max-width: 760px) { .switch { min-height: 44px; } .switch-track { width: 42px; height: 24px; } .switch-track::after { width: 20px; height: 20px; } .switch input:checked + .switch-track::after { transform: translateX(18px); } }

/* ---- 8. Admin drawer: fully off-canvas, aligned to the 760px breakpoint ---- */
@media (max-width: 760px) {
  .shell { display: block; }
  .sidebar {
    position: fixed; left: -300px; top: 0; width: 280px; height: 100vh; z-index: var(--z-sidebar);
    transition: left var(--dur-base) var(--ease-standard); box-shadow: var(--shadow-pop);
  }
  .sidebar.open { left: 0; }
  .sidebar-scrim { display: none; position: fixed; inset: 0; background: rgba(16,19,26,0.5); z-index: var(--z-scrim); }
  .sidebar-scrim.show { display: block; }
  .main-col { width: 100%; min-width: 0; }
  .topbar { padding: 0 12px; gap: 8px; }
  .topbar .tb-search { display: none; }
  .hamburger-btn { display: flex !important; }
  .tb-demo-btn .tb-demo-label { display: none; }
  .tb-demo-btn { padding: 0; width: 40px; justify-content: center; }
  .content-area { padding: 18px 14px 90px; max-width: 100%; }
}

/* ---- 9. Light top bar at phone widths: nothing is allowed to clip ---- */
@media (max-width: 560px) {
  .lt-inner { gap: 6px; padding: 9px 12px; }
  /* The role switcher is a demo affordance, it moves into the More sheet
     rather than squeezing the bell and brand off the edge. */
  .role-switch-light { display: none; }
  .light-topbar .tb-demo-btn { width: 40px; }
  .brand-light .mark { width: 32px; height: 32px; }
}
.more-section { margin-top: 18px; padding-top: 16px; border-top: 1px solid var(--border); }
.more-section h4 { font-size: 11px; text-transform: uppercase; letter-spacing: .06em; color: var(--text-muted); margin-bottom: 10px; }
.more-roles { display: grid; grid-template-columns: repeat(2, 1fr); gap: 8px; }
.role-tile {
  display: flex; align-items: center; gap: 9px; padding: 13px; border-radius: var(--radius-md);
  border: 1px solid var(--border); background: var(--surface); font-size: 13.5px; font-weight: 600; min-height: 48px;
}
.role-tile.on { border-color: var(--accent); background: var(--accent-soft); color: var(--accent-ink); }
@media (max-width: 760px) {
  /* Bulk selection is a desktop workflow, the empty checkbox row just added
     noise to every stacked card on a phone. */
  .data-table td.col-check, .data-table th.col-check { display: none; }
  /* Row actions read better as a full-width footer on a card */
  .data-table td.col-actions { justify-content: flex-end; gap: 8px; padding-top: 12px; }
  .data-table td.col-actions .btn { flex: 1; justify-content: center; }
}

/* ============================================================================
   APP SHELL SCROLLING
   Admin is a console: the sidebar and top bar are fixed furniture, and only the
   content pane scrolls. On phones the shell collapses to normal page scrolling
   with a sticky top bar, because a nested scroller fights the browser there.
   ========================================================================== */
@media (min-width: 761px) {
  html, body { height: 100%; }
  .shell { height: 100vh; height: 100dvh; overflow: hidden; align-items: stretch; }
  .sidebar { position: static; height: 100%; overflow-y: auto; overscroll-behavior: contain; }
  .main-col { height: 100%; overflow: hidden; }
  .topbar { position: static; flex-shrink: 0; }
  .content-scroll { flex: 1; overflow-y: auto; overscroll-behavior: contain; }
  .content-area { padding-bottom: 48px; }
}
/* Phones: one page scroller, top bar pinned */
@media (max-width: 760px) {
  .shell { height: auto; overflow: visible; }
  .content-scroll { overflow: visible; }
  .topbar { position: sticky; top: 0; z-index: var(--z-topbar); }
  .main-col { height: auto; overflow: visible; }
}
/* The light shell keeps natural page scrolling with a pinned header */
.light-topbar { position: sticky; top: 0; z-index: var(--z-topbar); }

/* ============================================================================
   FIXED-CHROME CLEARANCE
   The mobile tab bar is position:fixed, so the scrolling content must reserve
   space for it. An earlier `.container` shorthand was overwriting the reserve,
   letting the last card slide underneath. Declared last so it wins.
   ========================================================================== */
@media (max-width: 760px) {
  .light-shell .container { padding-bottom: calc(92px + env(safe-area-inset-bottom)); }
  .light-shell .content-area { padding-bottom: calc(92px + env(safe-area-inset-bottom)); }
  /* Admin uses a drawer rather than a tab bar, so it only needs breathing room */
  .shell .content-area { padding-bottom: calc(32px + env(safe-area-inset-bottom)); }
}

/* ============================================================================
   AUTHENTICATION
   ========================================================================== */
.auth-shell { display: grid; grid-template-columns: 1.05fr 1fr; min-height: 100vh; min-height: 100dvh; }
/* The sign-in brand panel carries the same sunlit wash as the hero, one
   surface language for every large light-filled moment in the product, rather
   than a separate deep gradient here and a light one three screens later. The
   radial centres are shifted for a tall panel so the warmth pools low-right,
   behind the rings, and the copy column stays close to the cream base. */
.auth-brand {
  position: relative; overflow: hidden; color: var(--wash-ink-soft); padding: 48px 52px;
  background-color: var(--wash-base);
  background-image:
    radial-gradient(96% 62% at 96% 104%, rgba(246,177,154,0.48) 0%, rgba(246,177,154,0.17) 42%, rgba(246,177,154,0) 72%),
    radial-gradient(78% 52% at 108% 74%, color-mix(in srgb, var(--accent) 28%, transparent) 0%, color-mix(in srgb, var(--accent) 0%, transparent) 66%),
    radial-gradient(90% 58% at 12% -12%, rgba(248,215,122,0.44) 0%, rgba(248,215,122,0.16) 40%, rgba(248,215,122,0) 70%),
    linear-gradient(168deg, rgba(255,232,213,0.00) 0%, rgba(255,232,213,0.28) 46%, rgba(255,232,213,0.62) 84%, rgba(255,232,213,0.34) 100%),
    linear-gradient(160deg, #FFFCF5 0%, #FFF8E8 56%, #FFF4E4 100%);
  border-right: 1px solid var(--wash-line);
  display: flex; align-items: center;
}
.auth-brand-inner { position: relative; z-index: 1; max-width: 460px; }
.auth-logo { color: var(--wash-ink); font-size: 17px; margin-bottom: 34px; cursor: default; }
.auth-logo .mark { background: #FFFFFF; box-shadow: 0 2px 8px rgba(160,104,52,0.12); }
.auth-brand h1 { color: var(--wash-ink); font-size: 34px; line-height: 1.15; margin-bottom: 12px; letter-spacing: -0.02em; }
.auth-brand p { color: var(--wash-ink-soft); font-size: 15px; max-width: 42ch; }
.auth-points { list-style: none; padding: 0; margin: 28px 0 0; display: flex; flex-direction: column; gap: 13px; }
.auth-points li { display: flex; align-items: center; gap: 11px; font-size: 14px; color: var(--wash-ink-soft); }
.auth-points strong { color: var(--wash-ink); }
.auth-points .icon { flex-shrink: 0; color: var(--ink-tangerine); }
.auth-foot { margin-top: 34px; font-size: 12.5px; color: var(--wash-ink-soft); border-top: 1px solid color-mix(in srgb, var(--accent) 22%, transparent); padding-top: 16px; }
.auth-rings { position: absolute; right: -120px; bottom: -140px; width: 460px; height: 460px; }
.auth-rings span { position: absolute; inset: 0; border-radius: 50%; border: 1.5px solid rgba(244,154,98,0.20); }
.auth-rings span:nth-child(2) { inset: 60px; border-color: rgba(246,177,154,0.26); }
.auth-rings span:nth-child(3) { inset: 120px; background: rgba(255,255,255,0.32); border-color: transparent; }

.auth-panel { display: flex; align-items: center; justify-content: center; padding: 40px 32px; background: var(--page-bg); overflow-y: auto; }
.auth-card { width: 100%; max-width: 420px; }
.auth-title { font-size: 25px; margin-bottom: 6px; }
.auth-sub { color: var(--text-secondary); font-size: 13.5px; margin-bottom: 20px; }
.auth-alt { text-align: center; font-size: 13px; color: var(--text-secondary); margin-top: 16px; }
.auth-done { text-align: center; }
.auth-done .ch-badge { background: var(--accent-soft); color: var(--accent-ink); }

.pass-wrap { position: relative; }
.pass-wrap input { width: 100%; padding-right: 44px; }
.pass-toggle {
  position: absolute; right: 4px; top: 50%; transform: translateY(-50%); width: 36px; height: 36px;
  border: none; background: none; color: var(--text-muted); display: flex; align-items: center; justify-content: center; border-radius: var(--radius-sm);
}
.pass-toggle:hover { color: var(--text-primary); background: var(--surface-alt); }
.pw-meter { display: grid; grid-template-columns: repeat(4, 1fr); gap: 4px; margin-top: 7px; }
.pw-meter span { height: 4px; border-radius: 2px; background: var(--surface-alt); }
.pw-meter.s1 span:nth-child(-n+1) { background: var(--status-critical); }
.pw-meter.s2 span:nth-child(-n+2) { background: var(--status-warning); }
.pw-meter.s3 span:nth-child(-n+3) { background: var(--accent); }
.pw-meter.s4 span { background: var(--status-good); }

/* Explicit password requirements checklist, each rule its own line with a
   live-updating indicator, so the person always knows exactly which
   condition is unmet instead of guessing from a single strength bar. */
.pw-requirements { list-style: none; margin: 9px 0 0; padding: 0; display: flex; flex-direction: column; gap: 5px; }
.pw-requirements li {
  display: flex; align-items: center; gap: 7px; font-size: 12.5px; color: var(--text-secondary);
  transition: color var(--dur-fast) var(--ease-standard);
}
.pw-requirements li .pwr-ico {
  display: flex; align-items: center; justify-content: center; width: 15px; height: 15px; border-radius: 50%;
  border: 1.5px solid var(--border-strong); color: transparent; flex-shrink: 0;
  transition: background var(--dur-fast) var(--ease-standard), border-color var(--dur-fast) var(--ease-standard), color var(--dur-fast) var(--ease-standard);
}
.pw-requirements li.met { color: var(--status-good); }
.pw-requirements li.met .pwr-ico { background: var(--status-good); border-color: var(--status-good); color: #fff; }
/* Live match/mismatch feedback under "Confirm password", appears only once
   the person has typed something there, so an empty field never shows red. */
.pw-match-hint { display: none; align-items: center; gap: 6px; font-size: 12.5px; margin-top: 7px; font-weight: 600; }
.pw-match-hint.show { display: flex; }
.pw-match-hint.ok { color: var(--status-good); }
.pw-match-hint.bad { color: var(--status-critical); }

.demo-accounts { margin-top: 24px; border-top: 1px solid var(--border); padding-top: 16px; }
.demo-accounts summary { cursor: pointer; font-size: 13px; font-weight: 600; display: flex; align-items: center; gap: 7px; color: var(--text-secondary); list-style: none; }
.demo-accounts summary::-webkit-details-marker { display: none; }
.demo-accounts summary:hover { color: var(--text-primary); }
.demo-grid { display: flex; flex-direction: column; gap: 7px; margin-top: 12px; }
.demo-card {
  display: flex; align-items: center; gap: 11px; text-align: left; width: 100%; padding: 11px 13px;
  border: 1px solid var(--border); border-radius: var(--radius-md); background: var(--surface); min-height: 56px;
}
.demo-card:hover { border-color: var(--accent); background: var(--accent-soft); }
.dc-ico { width: 34px; height: 34px; border-radius: 10px; background: var(--surface-alt); color: var(--text-secondary); display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
.demo-card:hover .dc-ico { background: #fff; color: var(--accent-ink); }
.dc-body { flex: 1; display: flex; flex-direction: column; min-width: 0; }
.dc-cred { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 10.5px; color: var(--text-muted); margin-top: 3px; }

/* Account controls in the shells */
.account-btn {
  display: flex; align-items: center; gap: 10px; width: 100%; padding: 9px 10px; margin-bottom: 12px;
  border-radius: var(--radius-md); border: 1px solid var(--chrome-border); background: rgba(255,255,255,0.05); color: #fff; text-align: left;
}
.account-btn:hover { background: rgba(255,255,255,0.1); }
.ab-text { flex: 1; display: flex; flex-direction: column; min-width: 0; }
.ab-text strong { font-size: 13px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ab-text span { font-size: 11px; color: var(--chrome-text); }
.account-btn .icon:last-child { color: var(--chrome-text); }
.rp-note { background: rgba(255,255,255,0.12); border-radius: var(--radius-pill); padding: 1px 6px; font-size: 9px; letter-spacing: .04em; margin-left: 4px; }
.avatar-btn { border: none; background: none; padding: 0; border-radius: 50%; flex-shrink: 0; }
.avatar-btn .avatar { border-radius: 50%; }
.avatar-btn:hover { opacity: .85; }
.account-head { display: flex; align-items: center; gap: 13px; }

@media (max-width: 900px) {
  .auth-shell { grid-template-columns: 1fr; }
  .auth-brand { padding: 30px 24px; align-items: flex-start; }
  .auth-brand h1 { font-size: 25px; }
  .auth-points, .auth-foot { display: none; }
  .auth-logo { margin-bottom: 18px; }
  .auth-rings { display: none; }
  .auth-panel { padding: 26px 18px 48px; }
}

/* ============================================================================
   PAYMENT SYSTEM, method picker, staged gateway modal, receipts
   ========================================================================== */
.pay-layout { display: grid; grid-template-columns: 1.3fr 1fr; gap: 18px; align-items: start; }
.pay-alt-col { display: flex; flex-direction: column; gap: 14px; }
@media (max-width: 900px) { .pay-layout { grid-template-columns: 1fr; } }

/* Marketplace cart quantity stepper, a compact −/qty/+ control used both on
   product cards and inside the cart drawer, so the two always look like the
   same control rather than two different bespoke widgets. */
.qty-stepper { display: inline-flex; align-items: center; gap: 0; border: 1.5px solid var(--border); border-radius: var(--radius-md); overflow: hidden; flex-shrink: 0; }
.qty-stepper button { width: 28px; height: 28px; display: flex; align-items: center; justify-content: center; background: var(--surface-alt, #F4F5F8); border: none; font-size: 15px; font-weight: 600; cursor: pointer; color: var(--text); line-height: 1; }
.qty-stepper button:hover:not(:disabled) { background: var(--border); }
.qty-stepper button:disabled { opacity: .4; cursor: not-allowed; }
.qty-stepper span { min-width: 26px; text-align: center; font-weight: 700; font-size: 13px; }

/* Product tile image on the Marketplace / vendor Store grids, cropped to a
   fixed-ratio banner above the card content so a mix of vendor-uploaded
   photos and unillustrated items still lines up in a tidy grid. */
.prod-tile-img { width: 100%; aspect-ratio: 16/9; border-radius: var(--radius-md); background-size: cover; background-position: center; margin-bottom: 10px; background-color: var(--surface-alt, #F4F5F8); }

/* Transaction/commission breakdown strip, "Sale → commission → you receive".
   Used on every earnings screen (practitioner and vendor) and inside receipt
   modals so the platform's cut is always shown as an explicit step, not a
   number quietly missing from the total. Wraps to a vertical stack on narrow
   screens rather than truncating or shrinking the figures. */
.breakdown-flow { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; }
.bf-step { display: flex; flex-direction: column; gap: 2px; padding: 10px 14px; border-radius: var(--radius-md); background: var(--surface-alt, #F4F5F8); min-width: 128px; }
.bf-step.bf-final { background: var(--status-good-bg, #E9F5E5); }
.bf-label { font-size: 11.5px; text-transform: uppercase; letter-spacing: .02em; color: var(--text-muted); }
.bf-neg { color: var(--status-bad-fg, #A82B2B); }
.bf-arrow { color: var(--text-muted); flex-shrink: 0; }
@media (max-width: 620px) {
  .breakdown-flow { flex-direction: column; align-items: stretch; gap: 8px; }
  .bf-arrow { transform: rotate(90deg); align-self: center; }
  .bf-step { min-width: 0; }
}

.pm-list { display: flex; flex-direction: column; gap: 8px; margin-top: 10px; }
.pm-card {
  display: flex; align-items: center; gap: 11px; text-align: left; width: 100%; padding: 11px 13px;
  border: 1.5px solid var(--border); border-radius: var(--radius-md); background: var(--surface); min-height: 56px;
  transition: border-color var(--dur-fast) var(--ease-standard), background var(--dur-fast) var(--ease-standard);
}
.pm-card:hover { border-color: var(--accent); }
.pm-card.selected { border-color: var(--accent); background: var(--accent-soft); }
.pm-icon {
  width: 34px; height: 34px; border-radius: 10px; background: var(--surface-alt); color: var(--text-secondary);
  display: flex; align-items: center; justify-content: center; flex-shrink: 0;
}
.pm-card.selected .pm-icon { background: #fff; color: var(--accent-ink); }
.pm-info { flex: 1; display: flex; flex-direction: column; min-width: 0; gap: 1px; }
.pm-info strong { font-size: 13.5px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.pm-radio {
  width: 18px; height: 18px; border-radius: 50%; border: 1.5px solid var(--border-strong); flex-shrink: 0; position: relative;
}
.pm-card.selected .pm-radio { border-color: var(--accent); }
.pm-card.selected .pm-radio::after {
  content: ""; position: absolute; inset: 3px; border-radius: 50%; background: var(--accent);
}
.pm-add-btn {
  display: flex; align-items: center; justify-content: center; gap: 7px; width: 100%; padding: 11px 13px;
  border: 1.5px dashed var(--border-strong); border-radius: var(--radius-md); background: transparent;
  color: var(--text-secondary); font-size: 13px; font-weight: 600;
}
.pm-add-btn:hover { border-color: var(--accent); color: var(--accent-ink); background: var(--accent-soft); }

/* Payment-methods management list (profile screen), same row anatomy, no radio */
.pm-manage-list { display: flex; flex-direction: column; gap: 8px; }
.pm-row {
  display: flex; align-items: center; gap: 11px; padding: 11px 13px;
  border: 1px solid var(--border); border-radius: var(--radius-md); background: var(--surface); flex-wrap: wrap;
}
.pm-row .pm-info strong { white-space: normal; }

.paybill-fallback { margin: 4px 0 2px; }
.paybill-fallback summary { cursor: pointer; font-size: 13px; font-weight: 600; color: var(--text-secondary); list-style: none; }
.paybill-fallback summary::-webkit-details-marker { display: none; }
.paybill-fallback summary:hover { color: var(--text-primary); }
.paybill-fallback[open] summary { margin-bottom: 4px; }

/* Staged gateway modal, reuses .processing's spinner/success styling and adds a decline state */
.processing .big-x { color: var(--status-critical); margin-bottom: 6px; }
.processing.declined h3 { color: var(--status-critical); }
.dlg-actions-col { display: flex; flex-direction: column; gap: 9px; }
.dlg-actions-col .btn { justify-content: center; }

/* Receipt */
.receipt { font-size: 13.5px; }
.receipt-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; }
.receipt .detail-list dt { font-size: 12px; }
.receipt .detail-list dd { font-size: 13px; }

/* ============================================================================
   MOBILE TRANSACTION FLOW
   A dedicated, full-screen, mobile-first replacement for the desktop
   modal-based Top Up / Withdraw / Scan-to-Pay dialogs, never a shrunk copy
   of them. Own step engine, own custom numeric keypad (no system keyboard
   for amounts, so there is nothing to trigger iOS/Android auto-zoom or fight
   the mobile tab bar for space), own sticky header/footer chrome. Desktop is
   completely untouched: this only ever mounts when App.js decides the
   viewport is a phone.
   ========================================================================== */
.mtx-overlay {
  position: fixed; inset: 0; z-index: var(--z-mtx); background: var(--page-bg);
  display: flex; flex-direction: column; overflow: hidden;
  animation: mtxIn var(--dur-slow) var(--ease-glide);
}
@keyframes mtxIn { from { transform: translateY(100%); } to { transform: translateY(0); } }
@keyframes mtxOut { from { transform: translateY(0); opacity: 1; } to { transform: translateY(14px); opacity: 0; } }
.mtx-overlay.leaving { animation: mtxOut var(--dur-base) var(--ease-standard) forwards; }
@media (min-width: 761px) { .mtx-overlay { display: none; } }

.mtx-header {
  flex-shrink: 0; display: flex; align-items: center; gap: 4px; padding: calc(10px + env(safe-area-inset-top)) 6px 10px;
  background: var(--surface); border-bottom: 1px solid var(--border);
}
.mtx-head-btn {
  width: 40px; height: 40px; min-width: 40px; border-radius: 50%; border: none; background: none;
  color: var(--text-secondary); display: flex; align-items: center; justify-content: center; flex-shrink: 0;
}
.mtx-head-btn:active { background: var(--surface-alt); }
.mtx-head-title { flex: 1; text-align: center; font-size: 14.5px; font-weight: 700; padding: 0 4px; }
.mtx-head-spacer { width: 40px; flex-shrink: 0; }
.mtx-dots { display: flex; justify-content: center; gap: 5px; padding: 9px 0 0; background: var(--surface); flex-shrink: 0; }
.mtx-dots span { width: 6px; height: 6px; border-radius: 50%; background: var(--border-strong); transition: background-color var(--dur-base) var(--ease-standard), width var(--dur-base) var(--ease-standard); }
.mtx-dots span.done { background: var(--accent-gold); }
.mtx-dots span.on { background: var(--accent); width: 16px; border-radius: 3px; }

.mtx-body {
  flex: 1; overflow-y: auto; overscroll-behavior: contain; -webkit-overflow-scrolling: touch; padding: 20px 20px calc(16px + env(safe-area-inset-bottom));
  animation: mtxBodyIn var(--dur-base) var(--ease-glide);
}
@keyframes mtxBodyIn { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
.mtx-body.centered { display: flex; flex-direction: column; }
/* The transaction sheet is a focused, single-purpose full-screen takeover, 
   a premium wallet app doesn't interrupt amount entry or PIN confirmation
   with an unrelated background toast (e.g. "Signed in as…"), and at the
   larger keypad sizes used here a bottom-corner toast has no safe height
   left to sit in without clipping the last key row anyway. The flow's own
   completion toast is unaffected: MobileTxn.finish() always calls close()
   (which clears mtx-open synchronously) before it calls UI.toast(). */
body.mtx-open #toastRoot { display: none; }
.mtx-footer {
  flex-shrink: 0; padding: 12px 20px calc(14px + env(safe-area-inset-bottom)); background: var(--surface);
  border-top: 1px solid var(--border); box-shadow: 0 -6px 20px rgba(20,22,31,0.05);
}
.mtx-footer .btn-block { min-height: 1px; }
.mtx-cta {
  width: 100%; min-height: 52px; border-radius: var(--radius-md); font-size: 15.5px; font-weight: 700;
  display: flex; align-items: center; justify-content: center; gap: 8px;
  background: var(--accent); color: #fff; border: none; transition: transform var(--dur-fast) var(--ease-standard), background-color var(--dur-fast) var(--ease-standard), opacity var(--dur-fast) var(--ease-standard);
}
.mtx-cta:active:not([disabled]) { transform: scale(0.985); background: var(--accent-strong); }
.mtx-cta[disabled] { opacity: 0.35; pointer-events: none; }
.mtx-cta.danger { background: var(--status-critical); }
.mtx-foot-link { display: block; width: 100%; text-align: center; margin-top: 10px; font-size: 12.5px; font-weight: 600; color: var(--text-secondary); background: none; border: none; }
.mtx-foot-link:active { color: var(--accent); }

/* ---- Amount entry ----------------------------------------------------
   The eyebrow/value/help/quick-amounts cluster sits as a natural-height
   block pinned near the top (.mtx-amount-top), and the keypad grows to
   fill whatever room is left (.mtx-keypad is a flex child with flex:1),
   then vertically self-centers its rows via align-content. That gives
   the keys real size on a typical phone instead of a small, cramped
   cluster floating in the middle of a mostly-empty screen, while a
   minmax() cap on the rows keeps them from ballooning absurdly large on
   short amount screens or bigger devices. */
.mtx-amount-wrap { flex: 1; min-height: 0; display: flex; flex-direction: column; align-items: center; text-align: center; padding: 4px 0 10px; }
.mtx-amount-top { flex-shrink: 0; display: flex; flex-direction: column; align-items: center; gap: 4px; width: 100%; }
.mtx-amount-eyebrow { font-size: 12.5px; font-weight: 600; color: var(--text-muted); margin-bottom: 6px; }
.mtx-amount-row { display: flex; align-items: baseline; gap: 6px; justify-content: center; max-width: 100%; }
.mtx-amount-currency { font-size: 20px; font-weight: 700; color: var(--text-muted); font-family: var(--font-display); }
.mtx-amount-value {
  font-family: var(--font-display); font-weight: 700; font-size: clamp(34px, 12vw, 48px); line-height: 1; color: var(--text-primary);
  font-variant-numeric: tabular-nums; white-space: nowrap; letter-spacing: -0.01em;
  max-width: 100%; overflow-x: auto; scrollbar-width: none;
}
.mtx-amount-value::-webkit-scrollbar { display: none; }
.mtx-amount-value .ph { color: var(--border-strong); }
.mtx-amount-caret { display: inline-block; width: 2px; height: 0.85em; background: var(--accent); margin-left: 2px; vertical-align: -0.05em; animation: mtxCaret 1s step-end infinite; }
@keyframes mtxCaret { 50% { opacity: 0; } }
.mtx-amount-wrap.shake .mtx-amount-value, .mtx-amount-wrap.shake .mtx-amount-currency { color: var(--status-critical); }
.mtx-amount-wrap.shake { animation: mtxShake 0.36s var(--ease-standard); }
@keyframes mtxShake { 20% { transform: translateX(-6px); } 40% { transform: translateX(5px); } 60% { transform: translateX(-3px); } 80% { transform: translateX(2px); } 100% { transform: translateX(0); } }
.mtx-amount-help { font-size: 12.5px; color: var(--text-muted); min-height: 17px; margin-top: 8px; }
.mtx-amount-help.error { color: var(--status-critical); font-weight: 600; }

.mtx-quick-amounts { flex-shrink: 0; display: flex; gap: 8px; flex-wrap: wrap; justify-content: center; margin-top: 10px; }
.mtx-chip {
  padding: 8px 14px; border-radius: var(--radius-pill); border: 1px solid var(--border-strong); background: var(--surface);
  font-size: 13px; font-weight: 700; color: var(--text-secondary); min-height: 36px;
}
.mtx-chip:active { background: var(--accent-soft); border-color: var(--accent); color: var(--accent-ink); transform: scale(0.96); }

.mtx-keypad {
  flex: 1; min-height: 0; width: 100%; max-width: 380px; margin: 14px auto 0; align-self: center;
  display: grid; grid-template-columns: repeat(3, 1fr); grid-template-rows: repeat(4, minmax(58px, 96px));
  align-content: center; justify-content: center; gap: 14px 16px;
}
.mtx-key {
  border-radius: var(--radius-lg); border: none; background: var(--surface-alt);
  font-size: 27px; font-weight: 600; color: var(--text-primary); display: flex; align-items: center; justify-content: center;
  transition: transform var(--dur-fast) var(--ease-standard), background-color var(--dur-fast) var(--ease-standard);
  font-family: var(--font-ui);
}
.mtx-key:active { transform: scale(0.93); background: var(--accent-soft); }
.mtx-key.blank { background: none; pointer-events: none; }
.mtx-key.mtx-key-back { color: var(--text-secondary); display: flex; align-items: center; justify-content: center; }
.mtx-key.mtx-key-back svg { width: 24px; height: 24px; }
.mtx-key.mtx-key-back:active { color: var(--status-critical); }
@media (max-height: 700px) {
  .mtx-keypad { grid-template-rows: repeat(4, minmax(48px, 76px)); gap: 10px 14px; margin-top: 8px; }
  .mtx-key { font-size: 24px; }
}
/* The PIN pad is a shorter, lower-stakes entry (4 digits vs. an open-ended
   amount) so it stays visually secondary, narrower and its rows capped
   lower, while still sharing the same key styling for consistency. */
.mtx-keypad.mtx-keypad--pin { max-width: 300px; grid-template-rows: repeat(4, minmax(56px, 74px)); margin-top: 18px; }

/* ---- Destination / method picker step ---- */
.mtx-section-label { font-size: 11.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .06em; color: var(--text-muted); margin: 18px 0 8px; }
.mtx-section-label:first-child { margin-top: 0; }
.mtx-dest-card {
  display: flex; align-items: center; gap: 12px; padding: 14px; border-radius: var(--radius-md);
  border: 1px solid var(--border); background: var(--surface); margin-bottom: 4px;
}
.mtx-dest-icon {
  width: 42px; height: 42px; border-radius: var(--radius-md); background: var(--accent-soft); color: var(--accent-ink);
  display: flex; align-items: center; justify-content: center; flex-shrink: 0;
}
.mtx-dest-body { flex: 1; min-width: 0; }
.mtx-dest-body strong { display: block; font-size: 14.5px; }
.mtx-dest-body .small { display: block; margin-top: 1px; }
.mtx-method-list { display: flex; flex-direction: column; gap: 9px; margin-top: 4px; }
.mtx-method-list .pm-card { min-height: 58px; padding: 13px 14px; }

/* ---- Details (optional note) step ---- */
.mtx-note-field textarea {
  width: 100%; min-height: 90px; padding: 12px 13px; border-radius: var(--radius-md); border: 1px solid var(--border-strong);
  background: var(--surface); color: var(--text-primary); font-size: 16px; font-family: inherit; resize: vertical;
}
.mtx-note-field textarea:focus { outline: 2px solid var(--accent); outline-offset: 1px; border-color: var(--accent); }
.mtx-note-count { text-align: right; font-size: 11.5px; color: var(--text-muted); margin-top: 4px; }

/* ---- Review step ---- */
.mtx-review-card { border: 1px solid var(--border); border-radius: var(--radius-md); background: var(--surface); overflow: hidden; }
.mtx-review-row { display: flex; align-items: flex-start; justify-content: space-between; gap: 14px; padding: 13px 15px; border-bottom: 1px solid var(--border); font-size: 13.5px; }
.mtx-review-row:last-child { border-bottom: none; }
.mtx-review-row .k { color: var(--text-muted); flex-shrink: 0; }
.mtx-review-row .v { text-align: right; font-weight: 600; color: var(--text-primary); }
.mtx-review-row.total { background: var(--surface-alt); }
.mtx-review-row.total .k, .mtx-review-row.total .v { font-weight: 800; font-size: 15px; color: var(--text-primary); }

/* ---- PIN / authenticate step ---- */
.mtx-pin-wrap { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center; gap: 6px; padding: 10px 0 18px; }
.mtx-pin-icon { width: 52px; height: 52px; border-radius: 50%; background: var(--accent-soft); color: var(--accent-ink); display: flex; align-items: center; justify-content: center; margin-bottom: 6px; }
.mtx-pin-dots { display: flex; gap: 14px; margin-top: 14px; }
.mtx-pin-dots span { width: 15px; height: 15px; border-radius: 50%; border: 2px solid var(--border-strong); transition: background-color var(--dur-fast) var(--ease-standard), border-color var(--dur-fast) var(--ease-standard); }
.mtx-pin-dots span.filled { background: var(--accent); border-color: var(--accent); }
.mtx-pin-wrap.shake .mtx-pin-dots span { border-color: var(--status-critical); }
.mtx-pin-wrap.shake .mtx-pin-dots span.filled { background: var(--status-critical); }
.mtx-pin-wrap.shake { animation: mtxShake 0.36s var(--ease-standard); }
.mtx-pin-error { font-size: 12.5px; color: var(--status-critical); font-weight: 600; min-height: 17px; margin-top: 10px; }
.mtx-pin-hint { font-size: 11.5px; color: var(--text-muted); margin-top: 4px; }

/* ---- Processing step ---- */
.mtx-processing-wrap { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center; gap: 4px; padding: 20px 12px; }
.mtx-spinner-lg { width: 52px; height: 52px; border-radius: 50%; border: 4px solid var(--surface-alt); border-top-color: var(--accent); animation: spin 0.85s linear infinite; margin-bottom: 18px; }
.mtx-processing-title { font-size: 16px; font-weight: 700; margin-bottom: 4px; transition: opacity var(--dur-base) var(--ease-standard); }
.mtx-processing-sub { font-size: 13px; color: var(--text-muted); max-width: 32ch; transition: opacity var(--dur-base) var(--ease-standard); }
.mtx-processing-fade { opacity: 0; }

/* ---- Success / failure step ---- */
.mtx-result-wrap { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center; padding: 10px 4px 20px; }
.mtx-result-icon {
  width: 76px; height: 76px; border-radius: 50%; display: flex; align-items: center; justify-content: center; margin-bottom: 16px;
  animation: mtxPop 0.4s var(--ease-glide);
}
@keyframes mtxPop { from { transform: scale(0.6); opacity: 0; } to { transform: scale(1); opacity: 1; } }
.mtx-result-icon.good { background: #E9F5E5; color: var(--status-good); }
.mtx-result-icon.bad { background: #FCEBEB; color: var(--status-critical); }
.mtx-result-title { font-size: 19px; font-weight: 800; margin-bottom: 6px; font-family: var(--font-display); }
.mtx-result-amount { font-size: 15px; font-weight: 700; color: var(--text-primary); margin-bottom: 4px; font-variant-numeric: tabular-nums; }
.mtx-result-sub { font-size: 13px; color: var(--text-muted); max-width: 34ch; line-height: 1.5; }
.mtx-result-ref { font-size: 12px; color: var(--text-muted); margin-top: 10px; }
.mtx-result-card { width: 100%; margin-top: 20px; border: 1px solid var(--border); border-radius: var(--radius-md); background: var(--surface); text-align: left; }
.mtx-fail-actions { display: flex; flex-direction: column; gap: 9px; width: 100%; margin-top: 4px; }

/* Loading skeleton for an amount-step balance line that hasn't resolved yet
   (kept for state-completeness; the demo data is always synchronous today) */
.mtx-skel { display: inline-block; height: 12px; width: 90px; border-radius: 4px; background: var(--surface-alt); vertical-align: middle; }

/* ============================================================================
   Lyfe, PREMIUM LAYER
   Loaded after styles.css. Three jobs, in this order of importance:

     1. LAYOUT DISCIPLINE, sibling cards share width, height, padding, radius
        and baseline. Nothing is sized by how much text happens to be in it.
     2. CARD HIERARCHY, a card's treatment states its rank (primary, metric,
        insight, action, status, command), so a page reads as a composition
        instead of a pile of white rectangles.
     3. COLOUR WITH DISCIPLINE, colour is bound to meaning. It extends the
        existing .tone-* families rather than introducing a second, competing
        palette, so one hue means one thing everywhere it appears.
   ========================================================================== */

/* ---- 1. Tone families, extended -------------------------------------------
   styles.css already binds each tone to --tone-bg / --tone-fg / --tone-line.
   The premium layer adds two more stops per family so the same hue can carry
   a gradient wash and a deep ink without anyone inventing a one-off colour:
     --tone-grad  soft directional wash for primary cards
     --tone-deep  saturated ink for dark surfaces, chart fills, emphasis
     --tone-glow  shadow colour, so elevation is tinted by the card's own hue
   Semantics (unchanged from the existing system, now written down):
     terracotta = brand / activity   green   = money, revenue, success
     blue       = volume, bookings   violet  = people, growth
     aqua       = capacity, places   gold    = caution, demand, waiting
     magenta    = messaging          slate   = system, neutral, config
*/
/* Tone families, retuned to Lyfe Sunlit Editorial.
   Per family: --tone-bg soft tint · --tone-line brand hue (fills, rules,
   chart marks) · --tone-fg / --tone-deep INK (verified for text on light
   grounds) · --tone-grad wash · --tone-glow tinted elevation. */
.tone-terracotta { --tone-bg: #FFF1DC; --tone-fg: #9C4B1C; --tone-line: #F47A3D; --tone-deep: #9C4B1C; --tone-glow: rgba(244,122,61,0.22); --tone-grad: linear-gradient(135deg, #FFF7EC 0%, #FFE6CE 100%); }
.tone-gold       { --tone-bg: #FFF6DC; --tone-fg: #7A6220; --tone-line: #F8C94C; --tone-deep: #7A6220; --tone-glow: rgba(248,201,76,0.26); --tone-grad: linear-gradient(135deg, #FFFAE9 0%, #FDEFC6 100%); }
.tone-aqua       { --tone-bg: #E2F4F1; --tone-fg: #1F6F68; --tone-line: #39AFA3; --tone-deep: #1F6F68; --tone-glow: rgba(57,175,163,0.22); --tone-grad: linear-gradient(135deg, #EDF8F6 0%, #D5EEE9 100%); }
.tone-blue       { --tone-bg: #E4EFF5; --tone-fg: #3B6980; --tone-line: #4B87A6; --tone-deep: #3B6980; --tone-glow: rgba(75,135,166,0.22); --tone-grad: linear-gradient(135deg, #EFF6FA 0%, #DDEAF2 100%); }
.tone-violet     { --tone-bg: #F1E8EE; --tone-fg: #6B4A62; --tone-line: #76516C; --tone-deep: #6B4A62; --tone-glow: rgba(118,81,108,0.22); --tone-grad: linear-gradient(135deg, #F7F0F4 0%, #EDDFE8 100%); }
.tone-green      { --tone-bg: #E4F0E5; --tone-fg: #426B4C; --tone-line: #568B62; --tone-deep: #426B4C; --tone-glow: rgba(86,139,98,0.22); --tone-grad: linear-gradient(135deg, #EEF6EE 0%, #DCEBDD 100%); }
.tone-magenta    { --tone-bg: #FBE9EC; --tone-fg: #A8483C; --tone-line: #EF6A5B; --tone-deep: #A8483C; --tone-glow: rgba(239,106,91,0.24); --tone-grad: linear-gradient(135deg, #FEF2F1 0%, #FBDFDC 100%); }
.tone-slate      { --tone-bg: #F1EDE5; --tone-fg: #6F655C; --tone-line: #9A8C7F; --tone-deep: #6F655C; --tone-glow: rgba(154,140,127,0.20); --tone-grad: linear-gradient(135deg, #F7F4EE 0%, #EBE5DA 100%); }

/* Role aliases. Written as intent at the call site, tone-money rather than
   tone-green, so a later palette change never silently changes a meaning. */
.tone-money    { --tone-bg: #E3F0E9; --tone-fg: #276A51; --tone-line: #276A51; --tone-deep: #276A51; --tone-glow: rgba(39,106,81,0.22); --tone-grad: linear-gradient(135deg, #EDF6F1 0%, #DBEBE1 100%); }
.tone-volume   { --tone-bg: #E4EFF5; --tone-fg: #3B6980; --tone-line: #4B87A6; --tone-deep: #3B6980; --tone-glow: rgba(75,135,166,0.22); --tone-grad: linear-gradient(135deg, #EFF6FA 0%, #DDEAF2 100%); }
.tone-growth   { --tone-bg: #F1E8EE; --tone-fg: #6B4A62; --tone-line: #76516C; --tone-deep: #6B4A62; --tone-glow: rgba(118,81,108,0.22); --tone-grad: linear-gradient(135deg, #F7F0F4 0%, #EDDFE8 100%); }
.tone-caution  { --tone-bg: #FFF6DC; --tone-fg: #815C1D; --tone-line: #D99A32; --tone-deep: #815C1D; --tone-glow: rgba(217,154,50,0.24); --tone-grad: linear-gradient(135deg, #FFFAE9 0%, #FDEFC6 100%); }
.tone-failure  { --tone-bg: #FBE7E4; --tone-fg: #A94036; --tone-line: #D9594C; --tone-deep: #A94036; --tone-glow: rgba(217,89,76,0.22); --tone-grad: linear-gradient(135deg, #FEF1EF 0%, #FADEDA 100%); }
.tone-capacity { --tone-bg: #E2F4F1; --tone-fg: #1D6660; --tone-line: #247D76; --tone-deep: #1D6660; --tone-glow: rgba(36,125,118,0.22); --tone-grad: linear-gradient(135deg, #EDF8F6 0%, #D5EEE9 100%); }

/* ---- 2. Layout symmetry ---------------------------------------------------
   The rule the whole product now obeys: sibling cards are sized by the GRID,
   never by their contents. A card with three lines and a card with twelve
   occupy the same track and the same height; the longer one scrolls or
   truncates internally rather than dragging the row out of shape.
*/

/* Every grid stretches its children to a common height, and every direct card
   child fills the track it was given. This single pair of rules is what stops
   "one card is taller because it has more rows" across the entire product. */
.grid, .kpi-strip, .alert-strip, .card-row {
  align-items: stretch;
}
/* min-width:0 lets a long unbroken value shrink instead of blowing the track.
   Height is deliberately NOT set to 100% here: `align-items: stretch` above
   already makes every card fill its row, and it lets the row grow to the
   tallest card. Forcing height:100% instead resolves each card against a row
   sized by its siblings, so a card with more content silently clips. Cards
   that must not drag a row taller opt in with .card-scroll instead. */
.grid > *, .kpi-strip > *, .alert-strip > *, .card-row > * {
  min-width: 0;
  /* Grid children carry NO margin, the gap owns all spacing between them.
     This is load-bearing, not tidiness. styles.css stacks flow content with
     `.card + .card { margin-top: var(--stack) }`, and inside a grid the second
     card is still an adjacent sibling, so it inherited a 22px top margin. With
     `align-items: stretch` the row sizes to the tallest card and the margin is
     then subtracted from the margined one, so it started 22px lower AND ended
     22px shorter than the card beside it. That mismatch appeared in every grid
     holding two or more cards, right across the product. */
  margin: 0;
}
.grid > .card, .card-row > .card { display: flex; flex-direction: column; }

/* The margin reset above is written as `.grid > *` (0,1,0), which loses to the
   flow-stacking rule `.card + .card` (0,2,0) it needs to cancel. These restate
   every stacking pair scoped to a grid container, at (0,3,0), so the gap is the
   only thing separating grid children. */
.grid > .card + .card,
.grid > .card + .grid,
.grid > .grid + .card,
.grid > .grid + .grid,
.grid > .table-block + .card,
.grid > .card + .table-block,
.grid > .kpi-strip + .card,
.kpi-strip > .card + .card,
.alert-strip > .card + .card,
.card-row > .card + .card { margin-top: 0; }

/* Quantity queries: the strip takes its column count from how many tiles it
   actually holds, so the last row is never a stretched orphan. Pure CSS, 
   no data attributes to keep in sync, no post-render JS pass. */
.kpi-strip {
  display: grid;
  gap: 12px;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  margin-bottom: 18px;
}
/* Gated to >=621px for the same specificity reason as the bands below: these
   `:has()` selectors outrank the plain `.kpi-strip` rule that turns the strip
   into a scrollable rail on phones. */
@media (min-width: 621px) {
  .kpi-strip:has(> :nth-child(2):last-child) { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .kpi-strip:has(> :nth-child(3):last-child) { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .kpi-strip:has(> :nth-child(5):last-child) { grid-template-columns: repeat(5, minmax(0, 1fr)); }
  .kpi-strip:has(> :nth-child(6):last-child) { grid-template-columns: repeat(6, minmax(0, 1fr)); }
  .kpi-strip:has(> :nth-child(7)) { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}

/* Step down in factors, so a row never leaves one tile stranded on its own.
   6→3→2, 5→3 (3+2 reads as intentional), 4→2.

   Each band carries `min-width: 621px` so the breakpoints are mutually
   exclusive. Without it these `:has()` selectors, which take the specificity
   of their argument, still match below 620px and outrank the plainer
   `.kpi-strip` rule in the phone block, leaving stale explicit tracks behind
   the rail and collapsing the first tiles to zero width. */
@media (min-width: 621px) and (max-width: 1280px) {
  .kpi-strip:has(> :nth-child(5):last-child),
  .kpi-strip:has(> :nth-child(6):last-child) { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
@media (min-width: 621px) and (max-width: 900px) {
  .kpi-strip, .kpi-strip:has(> :nth-child(3):last-child),
  .kpi-strip:has(> :nth-child(5):last-child),
  .kpi-strip:has(> :nth-child(6):last-child),
  .kpi-strip:has(> :nth-child(7)) { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
/* On a phone the strip becomes a swipeable rail rather than a tall stack, 
   six full-width tiles would push the real content below the fold. */
@media (max-width: 620px) {
  /* Deliberately still `display: grid`, laid out as a single auto-flow column
     track. styles.css sets `.kpi-strip > .kpi { flex: 1 1 150px }`, which is a
     more specific selector than anything that could be written here, so
     switching the rail to flex would hand layout back to that rule and the
     tiles would wrap into ragged rows again. In grid context it is inert. */
  .kpi-strip {
    display: grid;
    grid-template-columns: none;
    grid-auto-flow: column;
    grid-auto-columns: 63%;
    gap: 10px; overflow-x: auto; scroll-snap-type: x mandatory;
    margin-left: -16px; margin-right: -16px; padding: 2px 16px 8px;
    scrollbar-width: none; -webkit-overflow-scrolling: touch;
  }
  .kpi-strip::-webkit-scrollbar { display: none; }
  .kpi-strip > * { scroll-snap-align: start; }
  /* Two tiles fit side by side; a rail would be pointless scrolling. */
  .kpi-strip:has(> :nth-child(2):last-child) {
    grid-auto-flow: row; grid-template-columns: repeat(2, minmax(0, 1fr));
    overflow: visible; margin: 0 0 18px; padding: 0;
  }
  /* Opt out of the swipeable rail: every tile visible at once, as a plain
     2-column grid, for screens where scanning the full set at a glance
     matters more than saving vertical space (vendor/admin earnings and
     revenue summaries). Highest specificity in this block so it wins over
     the plain `.kpi-strip` and `:has()` rules above regardless of tile count. */
  .kpi-strip.kpi-strip-full {
    display: grid; grid-auto-flow: row; grid-template-columns: repeat(2, minmax(0, 1fr));
    overflow: visible; margin: 0 0 18px; padding: 0;
  }
}

/* Alert strip: same discipline, and a lone alert must not stretch full-bleed
   into a banner, it keeps the width it would have had beside a sibling. */
.alert-strip { display: grid; gap: 12px; grid-template-columns: repeat(2, minmax(0, 1fr)); }
.alert-strip:has(> :nth-child(3)) { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.alert-strip:has(> :nth-child(4)) { grid-template-columns: repeat(4, minmax(0, 1fr)); }
@media (max-width: 1180px) { .alert-strip:has(> :nth-child(4)) { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 1000px) { .alert-strip, .alert-strip:has(> :nth-child(3)) { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 700px)  { .alert-strip, .alert-strip:has(> :nth-child(3)), .alert-strip:has(> :nth-child(4)) { grid-template-columns: 1fr; } }

/* Standardised card metrics. One padding, one radius, one border, one gap, 
   applied at the container level so individual cards stop drifting. */
.card { --card-pad: 18px; }
@media (max-width: 620px) { .card { --card-pad: 15px; } }

/* A content region that must not drag its card taller than its row-mates.
   Used for lists whose length is data-driven (today's sessions, activity
   feeds): the card keeps the row's height and the overflow scrolls. */
.card-scroll { flex: 1; min-height: 0; overflow-y: auto; overscroll-behavior: contain; margin: 0 calc(var(--card-pad) * -1) calc(var(--card-pad) * -1); padding: 0 var(--card-pad) var(--card-pad); }
.card-scroll::-webkit-scrollbar { width: 6px; }
.card-scroll::-webkit-scrollbar-thumb { background: var(--border-strong); border-radius: 3px; }
.card-scroll::-webkit-scrollbar-track { background: transparent; }
/* Once cards stack there is no sibling left to match heights with, so the
   height cap stops earning its keep and a nested scroll region inside an
   already-scrolling page just becomes a touch trap. Release it. !important is
   needed because the cap is set per-card as an inline max-height. */
@media (max-width: 620px) {
  .card-scroll { max-height: none !important; overflow: visible; margin: 0; padding: 0; }
}

/* Pushes an action to the card's bottom edge so sibling cards align their
   footers even when their bodies differ in length. */
.card-foot { margin-top: auto; padding-top: 12px; }

/* ---- 3. Card hierarchy ----------------------------------------------------
   Rank is expressed through surface, not size alone, so the grid stays regular
   while the eye still gets a clear order to read in.
*/

/* PRIMARY, the one or two figures a page exists to communicate. Tinted wash,
   tone-coloured elevation, oversized figure. Deliberately breaks the visual
   rhythm; that is the point, so it is used sparingly. */
.card-primary {
  position: relative; overflow: hidden;
  background: var(--tone-grad, linear-gradient(135deg, #FFF3E9 0%, #FDE2CC 100%));
  border: 1px solid color-mix(in srgb, var(--tone-line, var(--accent)) 26%, transparent);
  border-radius: 18px; padding: var(--card-pad);
  box-shadow: 0 6px 22px -8px var(--tone-glow, rgba(226,103,43,0.2));
  display: flex; flex-direction: column;
}
/* Soft radial highlight, reads as light falling on the surface rather than
   as a decorative blob, and sits under the content at low opacity. */
.card-primary::after {
  content: ""; position: absolute; top: -40%; right: -12%; width: 62%; aspect-ratio: 1;
  background: radial-gradient(circle, color-mix(in srgb, var(--tone-line, var(--accent)) 18%, transparent) 0%, transparent 70%);
  pointer-events: none;
}
.card-primary > * { position: relative; z-index: 1; }
.cp-label { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em; color: var(--tone-deep, var(--text-secondary)); display: flex; align-items: center; gap: 7px; }
.cp-value { font-family: var(--font-display); font-size: clamp(26px, 2.4vw, 34px); font-weight: 700; letter-spacing: -0.02em; line-height: 1.1; margin-top: 8px; color: var(--text-primary); font-variant-numeric: tabular-nums; }
.cp-sub { font-size: 12.5px; color: var(--tone-deep, var(--text-secondary)); margin-top: 4px; font-weight: 600; }
.cp-ico { width: 26px; height: 26px; border-radius: 8px; background: color-mix(in srgb, var(--tone-line, var(--accent)) 20%, #fff); color: var(--tone-deep, var(--accent)); display: inline-flex; align-items: center; justify-content: center; }

/* COMMAND, the dark signature surface. One per screen at most, reserved for
   the page's operational centre of gravity. High contrast, tone-lit. */
.card-command {
  position: relative; overflow: hidden; border-radius: 18px; padding: var(--card-pad);
  background: linear-gradient(140deg, #241C17 0%, #17120F 58%, #1E1813 100%);
  border: 1px solid rgba(255,244,230,0.10);
  box-shadow: 0 14px 38px -14px rgba(20,16,12,0.55);
  color: #F2EAE0; display: flex; flex-direction: column;
}
.card-command::after {
  content: ""; position: absolute; top: -55%; right: -18%; width: 70%; aspect-ratio: 1;
  background: radial-gradient(circle, color-mix(in srgb, var(--tone-line, var(--accent)) 32%, transparent) 0%, transparent 68%);
  pointer-events: none;
}
.card-command > * { position: relative; z-index: 1; }
.card-command h3, .card-command .cc-value { color: #FFF; }
.card-command .cc-label { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em; color: #B2A99E; display: flex; align-items: center; gap: 7px; }
.card-command .cc-value { font-family: var(--font-display); font-size: clamp(28px, 2.8vw, 38px); font-weight: 700; letter-spacing: -0.02em; line-height: 1.08; margin-top: 8px; font-variant-numeric: tabular-nums; }
.card-command .cc-sub { font-size: 12.5px; color: #C9C0B5; margin-top: 5px; }
/* A command card whose value is a phrase rather than a figure. The display
   scale is tuned for numerals; a sentence set at 38px shouts and wraps badly. */
.card-command .cc-value.is-text { font-size: clamp(17px, 1.5vw, 20px); line-height: 1.3; letter-spacing: -0.01em; margin-top: 4px; }
.card-command .cc-title-row { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
.card-command .cc-divider { height: 1px; background: rgba(255,244,230,0.10); margin: 14px 0; }
.card-command .cc-stat-row { display: grid; grid-template-columns: repeat(3, minmax(0,1fr)); gap: 12px; }
.card-command .cc-stat { min-width: 0; }
.card-command .cc-stat b { display: block; font-size: 17px; font-weight: 700; color: #FFF; font-variant-numeric: tabular-nums; letter-spacing: -0.01em; }
.card-command .cc-stat span { display: block; font-size: 10.5px; color: #9A8F82; text-transform: uppercase; letter-spacing: 0.05em; font-weight: 700; margin-top: 2px; }

/* METRIC, the workhorse secondary tile. Colour arrives as a tinted icon chip
   and a hairline top rule, never as a full wash, so a row of six stays calm. */
.kpi {
  background: var(--surface); border-radius: 16px; padding: 15px 16px 14px;
  border: 1px solid var(--border); position: relative; overflow: hidden;
  display: flex; flex-direction: column; gap: 0;
  transition: transform var(--dur-base) var(--ease-glide), box-shadow var(--dur-base) var(--ease-glide), border-color var(--dur-base) var(--ease-glide);
}
.kpi::before { height: 3px; background: var(--tone-line, var(--border)); opacity: 0.9; }
/* min-height reserves the same two-line box for every label, so a tile whose
   label wraps ("Payment completion") keeps its figure on the same baseline as
   its single-line neighbours instead of dropping half a line below the row. */
.kpi .kpi-label {
  font-size: 10.5px; letter-spacing: 0.055em; color: var(--text-muted); gap: 7px;
  min-height: 30px; align-items: center; line-height: 1.25;
}
.kpi .kpi-ico { width: 22px; height: 22px; border-radius: 7px; background: var(--tone-bg, var(--surface-alt)); color: var(--tone-deep, var(--tone-fg, var(--text-muted))); }
.kpi .kpi-value { font-family: var(--font-display); font-size: 23px; margin-top: 9px; letter-spacing: -0.02em; }
.kpi-strip .kpi .kpi-value { font-size: 22px; }
.kpi .kpi-sub { font-size: 11.5px; margin-top: 3px; color: var(--text-muted); }
.kpi:hover { transform: translateY(-2px); box-shadow: 0 8px 20px -8px var(--tone-glow, rgba(20,22,31,0.18)); border-color: color-mix(in srgb, var(--tone-line, var(--accent)) 34%, var(--border)); }
.kpi-link { text-align: left; width: 100%; cursor: pointer; font: inherit; }
.kpi-go { position: absolute; right: 13px; bottom: 12px; color: var(--tone-line, var(--text-muted)); opacity: 0; transform: translateX(-4px); transition: opacity var(--dur-base) var(--ease-glide), transform var(--dur-base) var(--ease-glide); }
.kpi-link:hover .kpi-go, .kpi-link:focus-visible .kpi-go { opacity: 1; transform: none; }
/* The figure sits on its own line; the trend chip aligns to its baseline. */
.kpi-value-row { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; margin-top: 9px; }
.kpi-value-row .kpi-value { margin-top: 0; }

/* INSIGHT, an observation or recommendation, not a number. Tinted body and a
   colour-bearing left edge mark it as interpretation rather than raw data. */
.card-insight {
  background: var(--tone-bg, var(--surface-alt));
  border: 1px solid color-mix(in srgb, var(--tone-line, var(--accent)) 22%, transparent);
  border-left: 3px solid var(--tone-line, var(--accent));
  border-radius: 14px; padding: 14px 16px; display: flex; gap: 12px; align-items: flex-start;
}
.card-insight .ci-ico { width: 30px; height: 30px; border-radius: 9px; background: color-mix(in srgb, var(--tone-line, var(--accent)) 18%, #fff); color: var(--tone-deep, var(--accent)); display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
.card-insight .ci-body { flex: 1; min-width: 0; }
.card-insight .ci-title { font-weight: 700; font-size: 13.5px; color: var(--tone-deep, var(--text-primary)); }
.card-insight .ci-text { font-size: 12.5px; color: var(--text-secondary); margin-top: 2px; line-height: 1.5; }

/* ACTION, built around one task. Dashed edge signals "unfilled / do this". */
/* align-items:flex-start is load-bearing: styles.css sets `align-items:center`
   on every button, which centres these flex-column children and undoes the
   left-aligned reading order the card is designed around. */
.card-action {
  background: var(--surface); border: 1px dashed var(--border-strong); border-radius: 14px;
  padding: 15px 16px; display: flex; flex-direction: column; align-items: flex-start; gap: 8px; text-align: left; width: 100%;
  transition: border-color var(--dur-base) var(--ease-glide), background var(--dur-base) var(--ease-glide), transform var(--dur-base) var(--ease-glide);
}
.card-action:hover { border-color: var(--tone-line, var(--accent)); border-style: solid; background: var(--tone-bg, var(--accent-soft)); transform: translateY(-2px); }
.card-action .ca-ico { width: 32px; height: 32px; border-radius: 10px; background: var(--tone-bg, var(--surface-alt)); color: var(--tone-deep, var(--accent)); display: flex; align-items: center; justify-content: center; }
.card-action .ca-title { font-weight: 700; font-size: 13.5px; }
.card-action .ca-sub { font-size: 11.5px; color: var(--text-muted); line-height: 1.45; }

/* STATUS, system/operational health. The dot is the information; the row is
   scannable at a glance without reading a single word. */
.status-list { display: flex; flex-direction: column; }
.status-row { display: flex; align-items: center; gap: 11px; padding: 10px 0; border-bottom: 1px solid var(--border); }
.status-row:last-child { border-bottom: none; }
.status-row:first-child { padding-top: 0; }
.status-dot { width: 9px; height: 9px; border-radius: 50%; flex-shrink: 0; background: var(--status-good); position: relative; }
.status-dot.good { background: var(--status-good); }
.status-dot.warn { background: var(--status-warning); }
.status-dot.bad  { background: var(--status-critical); }
/* A slow halo on the live indicator only, motion here means "this is current". */
.status-dot.live::after { content: ""; position: absolute; inset: -4px; border-radius: 50%; border: 1px solid currentColor; opacity: 0.45; animation: statusPulse 2.4s var(--ease-standard) infinite; }
@keyframes statusPulse { 0% { transform: scale(0.75); opacity: 0.5; } 70% { transform: scale(1.35); opacity: 0; } 100% { opacity: 0; } }
.status-row .sr-name { flex: 1; min-width: 0; font-size: 13px; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.status-row .sr-val { font-size: 12px; font-weight: 700; color: var(--text-secondary); font-variant-numeric: tabular-nums; flex-shrink: 0; }

/* ---- 3b. Identity: gradient avatars ---------------------------------------
   Six families, assigned deterministically per identity (see avatarFamily in
   views.js). Each is a two-stop diagonal gradient chosen so all six sit at
   similar lightness and saturation, a directory of people reads as varied
   without any one avatar looking louder or off-brand than the rest.

   Both stops of every family clear 4.5:1 against white initials, verified per
   stop, not eyeballed. The first pass used brighter, more saturated colours and
   every one of the six failed (2.99–4.10:1); small white text simply cannot sit
   on a vivid mid-tone. Each stop is scaled toward black until it passes, which
   preserves the hue and the relationship between the two stops, so the families
   still read as the gradients they are named for, and the deeper, jewel-toned
   result looks more expensive than the bright version did, not less.
*/
.avatar {
  position: relative; overflow: hidden;
  background-image: var(--av-grad);
  box-shadow: 0 2px 6px -1px var(--av-shadow, rgba(20,22,31,0.28)), 0 1px 2px rgba(20,22,31,0.10);
  letter-spacing: 0.01em;
}
/* Inner highlight, a soft light source at the top-left and a subtle inner
   rim, so the avatar reads as a lit surface rather than a flat colour chip. */
.avatar::before {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background:
    radial-gradient(120% 100% at 22% 12%, rgba(255,255,255,0.34) 0%, rgba(255,255,255,0.10) 38%, transparent 68%);
  box-shadow: inset 0 0 0 1px rgba(255,255,255,0.18), inset 0 -1px 2px rgba(0,0,0,0.10);
  border-radius: inherit;
}
.avatar .av-initials { position: relative; z-index: 1; color: #FFFFFF; text-shadow: 0 1px 2px rgba(20,16,12,0.22); }

/* --av-tint is the same family at wash strength, for plates sitting behind a
   large avatar (practitioner cards) so band and avatar always agree. */
.av-g1 { --av-grad: linear-gradient(135deg, #4A73C3 0%, #7A5BEC 100%); --av-shadow: rgba(74,115,195,0.40);  --av-tint: linear-gradient(135deg, #EDF2FD 0%, #EFEAFE 100%); }  /* blue → violet    */
.av-g2 { --av-grad: linear-gradient(135deg, #9157D6 0%, #CB3990 100%); --av-shadow: rgba(145,87,214,0.40);  --av-tint: linear-gradient(135deg, #F4EDFC 0%, #FBEAF4 100%); }  /* purple → magenta */
.av-g3 { --av-grad: linear-gradient(135deg, #0E8376 0%, #127F9B 100%); --av-shadow: rgba(14,131,118,0.38);  --av-tint: linear-gradient(135deg, #E6F5F2 0%, #E6F3F8 100%); }  /* teal → cyan      */
.av-g4 { --av-grad: linear-gradient(135deg, #B85A25 0%, #C34D5B 100%); --av-shadow: rgba(184,90,37,0.40);   --av-tint: linear-gradient(135deg, #FBEFE7 0%, #FBECEE 100%); }  /* orange → coral   */
/* Emerald → teal, taken 7% darker than the other five. Both original stops
   cleared 4.5:1 on their own, but the BLEND between them peaked at 4.44:1, 
   checking only the declared stops misses the midpoint, which is the largest
   area of a 135° two-stop gradient and the part most initials actually sit on. */
.av-g5 { --av-grad: linear-gradient(135deg, #0E7D48 0%, #0C7A6F 100%); --av-shadow: rgba(14,125,72,0.36);   --av-tint: linear-gradient(135deg, #E6F4EC 0%, #E6F3F1 100%); }  /* emerald → teal   */
.av-g6 { --av-grad: linear-gradient(135deg, #5A5BD6 0%, #2C73D9 100%); --av-shadow: rgba(90,91,214,0.40);   --av-tint: linear-gradient(135deg, #EEEEFB 0%, #E9F1FC 100%); }  /* indigo → blue    */

/* Presence dot. Sits outside the clipped surface, so the avatar itself can keep
   overflow:hidden for the highlight. */
.avatar.has-status { overflow: visible; }
.avatar.has-status::after {
  content: ""; position: absolute; right: -1px; bottom: -1px;
  width: 32%; height: 32%; min-width: 8px; min-height: 8px; max-width: 13px; max-height: 13px;
  border-radius: 50%; border: 2px solid var(--surface); background: var(--status-good);
}
.avatar.has-status[data-status="warn"]::after { background: var(--status-warning); }
.avatar.has-status[data-status="bad"]::after  { background: var(--status-critical); }
/* Clip the highlight to the avatar shape now that overflow is visible. */
.avatar.has-status::before { overflow: hidden; }

/* Identity block, the shared header for every Profile Card. */
.identity { display: flex; align-items: center; gap: 13px; padding: 2px 0 14px; border-bottom: 1px solid var(--border); margin-bottom: 14px; }
.identity .id-body { min-width: 0; flex: 1; }
/* The small camera badge that opens the profile photo editor, sits over
   the bottom-right of a person's own avatar, never shown on anyone else's. */
.identity-avatar-wrap { position: relative; flex-shrink: 0; display: inline-flex; }
.avatar-edit-badge {
  position: absolute; right: -3px; bottom: -3px; width: 22px; height: 22px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center; background: var(--accent); color: #fff;
  border: 2px solid var(--surface); padding: 0; cursor: pointer;
  transition: transform var(--dur-fast) var(--ease-standard), background var(--dur-fast) var(--ease-standard);
}
.avatar-edit-badge:hover { background: var(--accent-gold); transform: scale(1.08); }
.avatar-edit-badge:active { transform: scale(0.94); }
.avatar-edit-badge:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* Profile photo editor dialog */
.photo-editor { display: flex; flex-direction: column; align-items: center; text-align: center; gap: 12px; }
.photo-editor-preview {
  width: 108px; height: 108px; border-radius: 50%; overflow: hidden; flex-shrink: 0;
  background: var(--surface-alt); border: 1px solid var(--border);
  display: flex; align-items: center; justify-content: center;
  font-size: 34px; font-weight: 700; color: var(--text-muted);
}
.photo-editor-preview img { width: 100%; height: 100%; object-fit: cover; display: block; }
.photo-editor-actions { display: flex; gap: 8px; flex-wrap: wrap; justify-content: center; }
.photo-editor-hint { margin: 0; }
.identity .id-name { display: block; font-size: 15.5px; font-weight: 700; letter-spacing: -0.01em; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.identity .id-sub { display: block; font-size: 12.5px; color: var(--text-secondary); margin-top: 2px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.identity .id-chips { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 7px; }

/* A person inside a table cell: avatar plus name on one baseline. Keeps rows the
   same height whether or not a given row carries an avatar. */
.cell-identity { display: flex; align-items: center; gap: 10px; min-width: 0; }
.cell-identity strong { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Small caption above a sub-region inside a card, one level below .section-title,
   used where a card holds two labelled halves. */
.mini-label { font-size: 10.5px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em; color: var(--text-muted); margin-bottom: 9px; }

/* ---- 4. Data visualisation ------------------------------------------------
   Charts are built from the same tokens as the cards that hold them, so they
   read as part of the interface rather than an embedded widget.
*/

/* Trend chip, direction is carried by shape and text as well as colour, so it
   survives greyscale and colour-blindness. */
.trend { display: inline-flex; align-items: center; gap: 3px; font-size: 11.5px; font-weight: 700; padding: 2px 7px 2px 5px; border-radius: var(--radius-pill); font-variant-numeric: tabular-nums; white-space: nowrap; }
.trend.up   { background: #E0F0DA; color: #16691B; }
.trend.down { background: #FBE3E3; color: #A82B2B; }
/* --text-muted on --surface-alt measures 4.31:1, just under the 4.5:1 floor for
   text this size; --text-secondary clears it at 5.3:1. */
.trend.flat { background: var(--surface-alt); color: var(--text-secondary); }

/* Sparkline, sized by its container, never a fixed pixel box. The wrapper
   carries the tone colour once via `color`, so both the stroke/fill below
   AND the gradient's own stop-color="currentColor" (baked in by views.js)
   pick it up without repeating the var(--tone-line, ...) fallback chain in
   five separate places. */
/* A real instrument, not a strip: an axis-max/min reading pinned above and
   below the plot, a taller plot band, a gridline backdrop, and a full
   crosshair, the observability-dashboard vocabulary (Dynatrace, Grafana)
   rather than a bare inline sparkline. */
.spark-wrap { display: flex; align-items: stretch; gap: 8px; width: 100%; }
.spark-axis { flex: 0 0 auto; display: flex; flex-direction: column; justify-content: space-between; align-items: flex-end; padding: 6px 0 4px; }
.spark-axis span { font-size: 10px; font-weight: 700; letter-spacing: 0.02em; color: color-mix(in srgb, var(--tone-line, var(--accent)) 45%, var(--text-muted) 55%); font-variant-numeric: tabular-nums; opacity: 0.85; white-space: nowrap; }
.spark-plot { flex: 1; min-width: 0; cursor: crosshair; }
.sparkline { display: block; width: 100%; height: 92px; overflow: visible; color: var(--tone-line, var(--accent)); }
.sparkline .spark-grid { stroke: color-mix(in srgb, currentColor 16%, transparent); stroke-width: 1; stroke-dasharray: 3 4; vector-effect: non-scaling-stroke; }
.sparkline .spark-line { fill: none; stroke: currentColor; stroke-width: 2.5; stroke-linecap: round; stroke-linejoin: round; filter: drop-shadow(0 3px 6px color-mix(in srgb, currentColor 50%, transparent)); }
.sparkline .spark-dot { fill: currentColor; stroke: var(--surface); stroke-width: 1.6; vector-effect: non-scaling-stroke; }
.sparkline .spark-dot-halo { fill: color-mix(in srgb, currentColor 22%, transparent); animation: sparkHalo 2.4s var(--ease-standard) infinite; }
@keyframes sparkHalo { 0%, 100% { transform: scale(0.85); opacity: 0.7; } 50% { transform: scale(1.25); opacity: 0.15; } }
/* .spark-area's fill is set inline (fill="url(#...)", a per-instance gradient
   baked in by views.js), nothing here overrides it, since a CSS `fill` rule
   always wins over an SVG presentation attribute and would flatten the
   gradient straight back to a solid tint. */
.sparkline .spark-guide-v, .sparkline .spark-guide-h { stroke: color-mix(in srgb, currentColor 70%, var(--text-primary) 30%); stroke-width: 1; stroke-dasharray: 3 3; opacity: 0; transition: opacity var(--dur-fast) var(--ease-standard); pointer-events: none; }
.sparkline .spark-hover-dot { fill: var(--surface); stroke: currentColor; stroke-width: 2.4; opacity: 0; transition: opacity var(--dur-fast) var(--ease-standard); pointer-events: none; filter: drop-shadow(0 0 5px color-mix(in srgb, currentColor 65%, transparent)); }
/* Draw-in: the line traces once on first paint. Purely additive, the chart is
   already correct and readable at frame zero if the animation never runs. */
.sparkline .spark-line { stroke-dasharray: 1400; stroke-dashoffset: 0; animation: sparkDraw 1s var(--ease-glide); }
@keyframes sparkDraw { from { stroke-dashoffset: 1400; } to { stroke-dashoffset: 0; } }

/* ---- Chart reading tooltip -------------------------------------------------
   One shared floating element (see ChartTip in core.js) raised by every chart
   primitive on hover/focus, sparkline points, bars, hbars, donut segments, 
   so "how is this period performing" is always one hover away, everywhere in
   the product, from one piece of code. */
.chart-tip {
  position: fixed; z-index: 340; top: 0; left: 0;
  background: var(--chrome-bg-raised, #211A15); color: #F8F3EC;
  border: 1px solid rgba(255,255,255,0.12);
  border-radius: 10px; padding: 8px 12px; font-size: 12.5px; line-height: 1.35;
  box-shadow: 0 10px 28px -10px rgba(0,0,0,0.45);
  pointer-events: none; opacity: 0; transform: translateY(3px);
  transition: opacity var(--dur-fast) var(--ease-standard), transform var(--dur-fast) var(--ease-standard);
  white-space: nowrap; max-width: 240px;
}
.chart-tip.on { opacity: 1; transform: translateY(0); }
.chart-tip b { display: block; font-size: 13.5px; font-weight: 700; font-variant-numeric: tabular-nums; }
.chart-tip span { display: block; font-size: 11px; color: #C9BFB2; margin-top: 1px; font-weight: 600; }

/* ---- Bars, hbars, donut segments: hover/focus lift -------------------------
   A richer surface (gradient fill, baked in by views.js) plus a visible
   "this one" state, brightness lift + soft glow, so hovering (or tabbing
   to, for keyboard users) a single bar reads as an intentional highlight, not
   just a flat colour swap. */
.bar-chart .bar { cursor: crosshair; transition: filter var(--dur-fast) var(--ease-standard), box-shadow var(--dur-fast) var(--ease-standard); }
.bar-chart .bar:hover, .bar-chart .bar:focus-visible { filter: brightness(1.12) saturate(1.08); box-shadow: 0 0 0 3px var(--bar-glow, transparent); outline: none; }
.hbar-fill { cursor: crosshair; transition: filter var(--dur-fast) var(--ease-standard); }
.hbar-fill:hover, .hbar-fill:focus-visible { filter: brightness(1.12) saturate(1.08); outline: none; }
.donut-seg { cursor: crosshair; transition: filter var(--dur-fast) var(--ease-standard); }
.donut-seg:hover, .donut-seg:focus-visible { filter: brightness(1.15) saturate(1.1); outline: none; }

/* Donut, rebuilt in SVG: crisp edges, a real gap between arcs, and a centre
   that can hold a labelled figure. */
.donut-wrap { display: flex; align-items: center; gap: 20px; flex-wrap: wrap; }
.donut-svg { flex-shrink: 0; transform: rotate(-90deg); filter: drop-shadow(0 10px 22px rgba(0,0,0,0.16)); }
.donut-svg circle { fill: none; stroke-linecap: butt; transition: stroke-width var(--dur-base) var(--ease-glide), filter var(--dur-fast) var(--ease-standard); }
.donut-seg { animation: donutDraw 0.85s var(--ease-glide) backwards; }
@keyframes donutDraw { from { stroke-dasharray: 0 999; } }
.donut-center { display: flex; flex-direction: column; align-items: center; justify-content: center; }
.donut-center b { font-family: var(--font-display); font-size: 22px; font-weight: 700; letter-spacing: -0.02em; font-variant-numeric: tabular-nums; }
.donut-center span { font-size: 9.5px; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.06em; font-weight: 700; margin-top: 1px; }
.donut-legend { display: flex; flex-direction: column; gap: 7px; flex: 1; min-width: 130px; }
.donut-legend .leg-item { display: flex; align-items: center; gap: 8px; font-size: 12.5px; font-weight: 600; color: var(--text-secondary); }
.donut-legend .leg-dot { width: 9px; height: 9px; border-radius: 3px; flex-shrink: 0; }
.donut-legend .leg-val { margin-left: auto; font-weight: 700; color: var(--text-primary); font-variant-numeric: tabular-nums; }
.donut-legend .leg-pct { font-size: 11px; color: var(--text-muted); font-variant-numeric: tabular-nums; min-width: 34px; text-align: right; }

/* Progress gauge, one bar component for quotas, capacity, goals, splits.
   Named "gauge", not "meter": styles.css already owns .meter as a fixed 46x6px
   inline mini-bar used inside tables. Reusing that name inherited its width and
   height and silently collapsed every bar drawn here. */
.gauge { display: flex; flex-direction: column; gap: 6px; }
.gauge-head { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; font-size: 12.5px; }
.gauge-head .gh-name { font-weight: 600; color: var(--text-secondary); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.gauge-head .gh-val { font-weight: 700; font-variant-numeric: tabular-nums; flex-shrink: 0; }
.gauge-track { height: 8px; border-radius: var(--radius-pill); background: var(--surface-alt); overflow: hidden; }
.gauge-fill { height: 100%; border-radius: inherit; background: var(--tone-line, var(--accent)); transform-origin: left center; animation: meterGrow 0.7s var(--ease-glide); }
@keyframes meterGrow { from { transform: scaleX(0); } }
.gauge-fill.grad { background: linear-gradient(90deg, var(--tone-line, var(--accent)), color-mix(in srgb, var(--tone-line, var(--accent)) 55%, var(--accent-gold))); }

/* Bars. The column owns the vertical space and the bar is a percentage of the
   slot inside it, so the plot grows with its card instead of sitting as a
   small fixed-height graphic marooned in a tall panel. */
.bar-chart { height: auto; align-items: stretch; position: relative; }
.bar-chart .bar-col { justify-content: flex-end; position: relative; z-index: 1; }
/* max-width keeps a four-bar chart in a wide card from rendering as heavy
   slabs; the bars stay in proportion to the type around them. */
.bar-chart .bar-slot { flex: 1; width: 56%; max-width: 56px; min-height: 24px; display: flex; align-items: flex-end; position: relative; }
.bar-chart .bar {
  width: 100%; border-radius: 7px 7px 3px 3px; animation: barGrow 0.7s var(--ease-glide); transform-origin: bottom center;
  position: relative; z-index: 1; box-shadow: inset 0 1px 0 rgba(255,255,255,0.35);
}
@keyframes barGrow { from { transform: scaleY(0); } }
/* A faint reference grid behind each bar's own slot, 25/50/75/100% of the
   plot's own range, the exact box the bar's height percentage is drawn
   against, the same "how does this compare to its own range" read the
   gridlines behind a sparkline give, applied to bars: a bar chart that reads
   as an instrument against a scale, not values floating on bare canvas. */
.bar-chart .bar-slot::before {
  content: ""; position: absolute; inset: 0; pointer-events: none; z-index: 0;
  background-image: repeating-linear-gradient(to top,
    color-mix(in srgb, var(--text-muted) 18%, transparent) 0,
    color-mix(in srgb, var(--text-muted) 18%, transparent) 1px,
    transparent 1px, transparent 25%);
}
.hbar-fill { border-radius: var(--radius-pill); animation: meterGrow 0.7s var(--ease-glide); transform-origin: left center; }
/* Ranked-list rows share one height and one set of column edges. The value
   column is sized for the longest value it will actually hold (a formatted
   currency figure) so it never wraps and drags a single row taller than the
   rest, and the label column truncates rather than reflowing the row. */
.hbar-row { align-items: center; min-height: 30px; margin-bottom: 10px; }
.hbar-row:last-child { margin-bottom: 0; }
.hbar-row .hbar-lbl { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; line-height: 1.35; }
.hbar-row .hbar-val { width: auto; min-width: 44px; white-space: nowrap; }

/* Chart region inside a card. Children stretch to the full width of the card
   so a chart is never a small centred graphic in a wide panel; the frame also
   absorbs the leftover height when a sibling card is taller. */
.chart-frame { flex: 1; min-height: 150px; display: flex; flex-direction: column; justify-content: center; padding-top: 4px; }
/* :not(.sr-only) matters, the charts emit a visually-hidden text summary as a
   sibling, and forcing width:100% onto it overrides the 1px clip that hides it,
   pushing an invisible 1440px paragraph off-screen and creating a horizontal
   scrollbar on the whole page. */
.chart-frame > *:not(.sr-only) { width: 100%; }
.chart-frame > .bar-chart { height: 100%; }
/* The donut is intrinsically sized, so it centres rather than stretching. */
.chart-frame > .donut-wrap { justify-content: center; }

/* Lets one card claim two tracks so a hero can dominate a row without leaving
   the grid, the row stays on the same columns and gutters as everything else. */
.span-2 { grid-column: span 2; }
@media (max-width: 1100px) { .span-2 { grid-column: span 1; } }

/* Distributes a card's flexible content over the height it was given, instead
   of bunching it at the top and leaving a dead band above the footer. Used
   where a card is stretched by a taller sibling in the same row. */
.stack-fill { flex: 1; min-height: 0; display: flex; flex-direction: column; justify-content: space-evenly; gap: 12px; padding: 4px 0; }

/* ---- 5. Interaction -------------------------------------------------------
   Motion states cause and continuity. Nothing here delays feedback, and the
   global prefers-reduced-motion rule in styles.css neutralises all of it.
*/
.card { transition: box-shadow var(--dur-base) var(--ease-glide), border-color var(--dur-base) var(--ease-glide), transform var(--dur-base) var(--ease-glide); }
.card.clickable:hover, .card.hoverable:hover { transform: translateY(-2px); box-shadow: 0 10px 26px -12px rgba(20,22,31,0.22); border-color: color-mix(in srgb, var(--tone-line, var(--accent)) 30%, var(--border)); }
.card-primary:hover { box-shadow: 0 12px 30px -10px var(--tone-glow, rgba(226,103,43,0.28)); }

/* Section heading: consistent optical baseline and a tone-tinted rule, so the
   eye can find the start of a section without a heavier divider. */
.section-title { display: flex; align-items: center; gap: 10px; margin-bottom: 13px; }
.section-title h3 { margin: 0; font-size: 14px; letter-spacing: -0.01em; }
.section-title .st-spacer { flex: 1; }

/* Card headers reserve one shared height whether or not they carry an action.
   Without this a card whose header holds a button ("Change", "Edit") is taller
   than the plain-heading card beside it, so its divider, and every row under
   it, sits a few pixels lower than its neighbour's for the whole card. The
   height is set to the natural height of a small button so nothing is pushed
   around when one is present. */
.card > h3:first-child,
.card > h2:first-child,
.card > .flex-between:first-child,
.card > .section-title:first-child {
  min-height: 30px;
  display: flex; align-items: center;
}
.card > .flex-between:first-child > h3,
.card > .flex-between:first-child > h2 { margin: 0; }

/* Skeletons for genuinely pending content. */
/* The shimmer's highlight stop was a hardcoded light grey (#E9EAF1), fine on
   the light theme, but a bright flash sweeping across every loading
   placeholder in dark mode. color-mix against --surface-alt keeps the same
   "light catching a surface" effect in both themes without a literal color. */
.skel { background: linear-gradient(90deg, var(--surface-alt) 25%, color-mix(in srgb, var(--surface-alt) 55%, var(--text-primary) 12%) 50%, var(--surface-alt) 75%); background-size: 200% 100%; animation: skelSlide 1.3s linear infinite; border-radius: var(--radius-sm); }
@keyframes skelSlide { to { background-position: -200% 0; } }

/* ---- 6. Responsive composition -------------------------------------------
   Cards recompose rather than shrink; nothing critical is hidden by width.
*/
@media (max-width: 980px) {
  .card-command .cc-stat-row { grid-template-columns: repeat(3, minmax(0,1fr)); }
}
@media (max-width: 620px) {
  .card-primary, .card-command { border-radius: 16px; }
  .cp-value { font-size: 26px; }
  .card-command .cc-value { font-size: 28px; }
  .card-command .cc-stat-row { gap: 8px; }
  .card-command .cc-stat b { font-size: 15px; }
  .donut-wrap { gap: 14px; justify-content: center; }
  .donut-legend { min-width: 100%; }
}

/* ============================================================================
   ENTRY EXPERIENCE, welcome, registration, first-run onboarding
   The first ninety seconds of Lyfe. Warm and festival-first rather than
   enterprise; every step is skippable; nothing here blocks the way in.
   ========================================================================== */

/* ---- Welcome ------------------------------------------------------------ */
.welcome-shell {
  min-height: 100vh; min-height: 100dvh; position: relative; overflow: hidden;
  display: flex; align-items: center; justify-content: center;
  padding: 32px 20px calc(32px + env(safe-area-inset-bottom));
  /* The front door now carries the sunlit wash, in the same Cream → Peach →
     Soft Tangerine → Warm Gold family as the hero and the sign-in panel. The
     previous deep gradient had to be darkened to #9D481E at its lightest just
     to keep white captions legible, a gradient shaped by a contrast problem
     rather than by art direction. Inverting to dark ink on a light ground
     removes the constraint entirely: the wash can now stay pale and the text
     clears 4.5:1 without a single scrim.

     Vertically centred content on a full-bleed screen means warmth has to sit
     in the corners, so the gold pools top-right and the coral bottom-left and
     the middle band, where the title, stats and buttons live, stays cream. */
  background-color: var(--wash-base);
  background-image:
    radial-gradient(72% 52% at 92% -8%, rgba(248,215,122,0.52) 0%, rgba(248,215,122,0.18) 40%, rgba(248,215,122,0) 70%),
    radial-gradient(64% 48% at -6% 104%, rgba(246,177,154,0.44) 0%, rgba(246,177,154,0.15) 44%, rgba(246,177,154,0) 74%),
    radial-gradient(46% 34% at 106% 88%, rgba(244,154, 98,0.24) 0%, rgba(244,154, 98,0) 68%),
    linear-gradient(172deg, rgba(255,232,213,0.00) 0%, rgba(255,232,213,0.22) 40%, rgba(255,232,213,0.50) 100%),
    linear-gradient(150deg, #FFFDF7 0%, #FFF8E8 58%, #FFF3E1 100%);
  color: var(--wash-ink-soft);
}
.welcome-art { position: absolute; inset: 0; pointer-events: none; }
/* Soft light sources, layered over the wash to give the flat gradient a sense
   of depth. Blur is expensive at large radii, so these are plain radial
   gradients, same effect, no filter cost on a first-paint screen. Alphas are
   halved from the dark-gradient version: on a light ground the same values
   read as blotches rather than as light. */
.wa-orb { position: absolute; border-radius: 50%; pointer-events: none; }
.wa-orb-1 { width: 60vmin; height: 60vmin; top: -18vmin; right: -12vmin; background: radial-gradient(circle, rgba(248,215,122,0.30) 0%, transparent 68%); }
.wa-orb-2 { width: 46vmin; height: 46vmin; bottom: -14vmin; left: -10vmin; background: radial-gradient(circle, rgba(246,177,154,0.26) 0%, transparent 70%); }
.wa-orb-3 { width: 34vmin; height: 34vmin; top: 42%; left: 58%; background: radial-gradient(circle, rgba(255,252,244,0.55) 0%, transparent 72%); }

.welcome-inner {
  position: relative; z-index: 1; width: 100%; max-width: 420px; text-align: center;
  animation: welcomeIn 0.5s var(--ease-glide);
}
@keyframes welcomeIn { from { opacity: 0; transform: translateY(10px); } }
.welcome-logo { display: flex; align-items: center; justify-content: center; gap: 10px; margin-bottom: 26px; }
.welcome-logo img { width: 34px; height: 34px; object-fit: contain; }
.welcome-logo span { font-family: var(--font-display); font-size: 20px; font-weight: 700; letter-spacing: -0.01em; }
.welcome-logo span { color: var(--wash-ink); }
.welcome-title { font-family: var(--font-display); font-size: clamp(30px, 7vw, 40px); font-weight: 700; line-height: 1.12; letter-spacing: -0.02em; margin: 0 0 10px; color: var(--wash-ink); }
.welcome-sub { font-size: 15.5px; line-height: 1.5; color: var(--wash-ink-soft); margin: 0 auto 26px; max-width: 21em; }


.welcome-actions { display: flex; flex-direction: column; gap: 10px; }
.welcome-shell .btn-lg { min-height: 52px; font-size: 15.5px; }
.welcome-signin { background: rgba(255,255,255,0.82); border-color: rgba(196,120,64,0.32); color: var(--ink-tangerine); }
.welcome-signin:hover { background: #FFFFFF; border-color: rgba(196,120,64,0.52); }

.welcome-explore {
  display: flex; flex-direction: column; align-items: center; gap: 2px; width: 100%;
  margin-top: 18px; padding: 12px; border: none; background: none; color: var(--wash-ink);
  font-size: 14px; font-weight: 700; border-radius: var(--radius-md);
  transition: background var(--dur-fast) var(--ease-standard);
}
.welcome-explore:hover { background: rgba(255,255,255,0.6); }
.welcome-explore .we-sub { font-size: 12px; font-weight: 500; color: var(--wash-ink-soft); }
.welcome-foot { margin-top: 24px; font-size: 11.5px; color: var(--wash-ink-soft); }

/* Explains why the auth screen appeared, when it interrupted something. */
.intent-banner {
  display: flex; align-items: center; gap: 9px; padding: 10px 12px; margin-bottom: 16px;
  background: var(--accent-soft); border: 1px solid color-mix(in srgb, var(--accent) 26%, transparent);
  border-radius: var(--radius-md); font-size: 12.5px; font-weight: 600; color: var(--accent-ink);
}

/* ---- International phone field ----------------------------------------- */
.phone-input { display: flex; gap: 8px; }
.phone-input select { flex: 0 0 116px; min-width: 0; }
.phone-input input { flex: 1; min-width: 0; }

/* ---- First-run onboarding ---------------------------------------------- */
.onboard-shell {
  min-height: 100vh; min-height: 100dvh; position: relative; overflow-y: auto;
  display: flex; align-items: center; justify-content: center;
  padding: 24px 18px calc(24px + env(safe-area-inset-bottom));
  background: var(--page-bg);
}
.onboard-art { position: absolute; inset: 0; pointer-events: none; overflow: hidden; opacity: 0.5; }
.onboard-card {
  position: relative; z-index: 1; width: 100%; max-width: 560px;
  background: var(--surface); border: 1px solid var(--border); border-radius: 22px;
  box-shadow: 0 20px 60px -28px rgba(20,22,31,0.32); padding: 20px 22px 24px;
  animation: welcomeIn 0.35s var(--ease-glide);
}
.onboard-head { display: flex; align-items: center; gap: 12px; margin-bottom: 20px; }
.onboard-dots { display: flex; gap: 6px; flex: 1; }
.onboard-dots span { height: 4px; flex: 1; border-radius: 2px; background: var(--border-strong); transition: background-color var(--dur-base) var(--ease-standard); }
.onboard-dots span.done { background: var(--accent-gold); }
.onboard-dots span.on { background: var(--accent); }
.onboard-skip { flex-shrink: 0; font-size: 12.5px; }
.onboard-body { display: flex; flex-direction: column; }
.onboard-body.centered { align-items: center; text-align: center; }
.onboard-badge {
  width: 66px; height: 66px; border-radius: 22px; display: flex; align-items: center; justify-content: center;
  background: linear-gradient(135deg, var(--accent) 0%, var(--accent-gold) 100%); color: #fff;
  box-shadow: 0 10px 26px -10px rgba(226,103,43,0.6); margin-bottom: 18px;
}
.onboard-badge.success { background: linear-gradient(135deg, #0F864D 0%, #12A05C 100%); box-shadow: 0 10px 26px -10px rgba(15,134,77,0.55); }
.onboard-title { font-family: var(--font-display); font-size: clamp(21px, 3.4vw, 26px); font-weight: 700; letter-spacing: -0.02em; line-height: 1.2; margin: 0 0 8px; }
.onboard-sub { font-size: 14px; line-height: 1.55; color: var(--text-secondary); margin: 0 0 20px; max-width: 34em; }
.onboard-actions { display: flex; flex-direction: column; gap: 8px; width: 100%; margin-top: 18px; }

/* Multi-select interest cards. Selection is shown by tint, border, colour AND
   a check mark, so it never depends on colour alone. */
.choice-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(148px, 1fr)); gap: 10px; }
.choice-card {
  position: relative; display: flex; flex-direction: column; align-items: flex-start; gap: 8px;
  padding: 14px; min-height: 92px; border-radius: 15px; text-align: left;
  border: 1.5px solid var(--border); background: var(--surface); color: var(--text-primary);
  transition: border-color var(--dur-fast) var(--ease-standard), background var(--dur-fast) var(--ease-standard), transform var(--dur-fast) var(--ease-standard);
}
.choice-card:hover { border-color: var(--tone-line, var(--accent)); transform: translateY(-2px); }
.choice-card .cc-ico { width: 34px; height: 34px; border-radius: 11px; display: flex; align-items: center; justify-content: center; background: var(--tone-bg, var(--surface-alt)); color: var(--tone-deep, var(--accent)); }
.choice-card .cc-label { font-size: 13.5px; font-weight: 700; line-height: 1.3; }
.choice-card .cc-check {
  position: absolute; top: 10px; right: 10px; width: 20px; height: 20px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center; background: var(--tone-line, var(--accent)); color: #fff;
  opacity: 0; transform: scale(0.6); transition: opacity var(--dur-fast) var(--ease-glide), transform var(--dur-fast) var(--ease-glide);
}
.choice-card.selected { border-color: var(--tone-line, var(--accent)); background: var(--tone-bg, var(--accent-soft)); }
.choice-card.selected .cc-check { opacity: 1; transform: none; }
.choice-card.selected .cc-ico { background: color-mix(in srgb, var(--tone-line, var(--accent)) 20%, #fff); }

/* Single-select participation paths. */
.path-list { display: flex; flex-direction: column; gap: 10px; }
.path-card {
  display: flex; align-items: center; gap: 13px; width: 100%; text-align: left;
  padding: 14px; min-height: 68px; border-radius: 15px;
  border: 1.5px solid var(--border); background: var(--surface); color: var(--text-primary);
  transition: border-color var(--dur-fast) var(--ease-standard), background var(--dur-fast) var(--ease-standard), transform var(--dur-fast) var(--ease-standard);
}
.path-card:hover { border-color: var(--tone-line, var(--accent)); transform: translateY(-1px); }
.path-card .pc-ico { width: 40px; height: 40px; border-radius: 13px; display: flex; align-items: center; justify-content: center; background: var(--tone-bg, var(--surface-alt)); color: var(--tone-deep, var(--accent)); flex-shrink: 0; }
.path-card .pc-body { flex: 1; min-width: 0; }
.path-card .pc-body strong { display: block; font-size: 14px; font-weight: 700; }
.path-card .pc-body span { display: block; font-size: 12.5px; color: var(--text-secondary); margin-top: 2px; line-height: 1.4; }
.path-card .pc-check {
  width: 22px; height: 22px; border-radius: 50%; flex-shrink: 0;
  display: flex; align-items: center; justify-content: center;
  background: var(--tone-line, var(--accent)); color: #fff;
  opacity: 0; transform: scale(0.6); transition: opacity var(--dur-fast) var(--ease-glide), transform var(--dur-fast) var(--ease-glide);
}
.path-card.selected { border-color: var(--tone-line, var(--accent)); background: var(--tone-bg, var(--accent-soft)); }
.path-card.selected .pc-check { opacity: 1; transform: none; }

/* Quick-start tiles on the final screen. */
.quick-start { display: grid; grid-template-columns: repeat(3, minmax(0,1fr)); gap: 10px; width: 100%; }
.qs-card {
  display: flex; flex-direction: column; align-items: flex-start; gap: 4px; text-align: left;
  padding: 14px; border-radius: 15px; border: 1px solid var(--border); background: var(--surface);
  min-height: 116px; transition: border-color var(--dur-fast) var(--ease-standard), transform var(--dur-fast) var(--ease-standard), background var(--dur-fast) var(--ease-standard);
}
.qs-card:hover { border-color: var(--tone-line, var(--accent)); background: var(--tone-bg, var(--accent-soft)); transform: translateY(-2px); }
.qs-card .qs-ico { width: 34px; height: 34px; border-radius: 11px; display: flex; align-items: center; justify-content: center; background: var(--tone-bg, var(--surface-alt)); color: var(--tone-deep, var(--accent)); margin-bottom: 4px; }
.qs-card strong { font-size: 13px; font-weight: 700; line-height: 1.25; }
.qs-card span { font-size: 11.5px; color: var(--text-muted); line-height: 1.35; }

@media (max-width: 560px) {
  .onboard-card { padding: 18px 16px 20px; border-radius: 18px; }
  .quick-start { grid-template-columns: 1fr; }
  .qs-card { min-height: 0; flex-direction: row; align-items: center; gap: 12px; }
  .qs-card .qs-ico { margin-bottom: 0; flex-shrink: 0; }
  .qs-card strong { flex: 0 0 auto; }
  .qs-card span { display: none; }
  .choice-grid { grid-template-columns: repeat(2, minmax(0,1fr)); }
}

/* ============================================================================
   SESSION CARDS, the Session family
   A discipline carries one icon and one tone everywhere it appears, so a
   reader starts recognising a session type by its colour before reading it.
   Every card is the same height with the price rail and actions on a shared
   baseline, whatever the length of the title above.
   ========================================================================== */
.session-grid { align-items: stretch; }
.session-card {
  display: flex; flex-direction: column; height: 100%;
  background: var(--surface); border: 1px solid var(--border); border-radius: 18px;
  padding: 16px 16px 14px; position: relative; overflow: hidden;
  transition: transform var(--dur-base) var(--ease-glide), box-shadow var(--dur-base) var(--ease-glide), border-color var(--dur-base) var(--ease-glide);
}
/* A tone-coloured hairline along the top edge, enough to identify the
   discipline at a glance without tinting the whole card. */
.session-card::before {
  content: ""; position: absolute; top: 0; left: 0; right: 0; height: 3px;
  background: linear-gradient(90deg, var(--tone-line, var(--accent)), color-mix(in srgb, var(--tone-line, var(--accent)) 30%, transparent));
}
.session-card:hover { transform: translateY(-3px); box-shadow: 0 12px 28px -14px var(--tone-glow, rgba(20,22,31,0.24)); border-color: color-mix(in srgb, var(--tone-line, var(--accent)) 34%, var(--border)); }

.sc-top { display: flex; align-items: center; gap: 9px; margin-bottom: 11px; }
.sc-ico { width: 32px; height: 32px; border-radius: 11px; display: flex; align-items: center; justify-content: center; background: var(--tone-bg, var(--surface-alt)); color: var(--tone-deep, var(--accent)); flex-shrink: 0; }
.sc-cat { flex: 1; min-width: 0; font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.05em; color: var(--tone-deep, var(--text-muted)); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sc-dur { flex-shrink: 0; font-size: 11.5px; font-weight: 700; color: var(--text-muted); font-variant-numeric: tabular-nums; }

/* Two lines maximum, so a long session name can never push the price rail of
   one card out of line with its neighbours. */
.sc-name {
  font-family: var(--font-display); font-size: 15.5px; font-weight: 700; line-height: 1.3;
  letter-spacing: -0.01em; margin: 0 0 9px; min-height: 2.6em;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.sc-person { display: flex; align-items: center; gap: 8px; margin-bottom: 11px; font-size: 13px; font-weight: 600; color: var(--text-secondary); min-width: 0; }
.sc-person > span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.sc-meta { display: flex; flex-direction: column; gap: 5px; margin: 0 0 12px; }
.sc-meta > div { display: flex; align-items: center; gap: 7px; min-width: 0; }
.sc-meta dt { flex-shrink: 0; color: var(--text-muted); display: flex; margin: 0; }
.sc-meta dd { margin: 0; font-size: 12.5px; color: var(--text-secondary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* margin-top:auto pins the footer to the bottom edge, so price and actions sit
   on one baseline across the whole row regardless of title length. */
.sc-foot { margin-top: auto; padding-top: 12px; border-top: 1px solid var(--border); display: flex; align-items: flex-end; justify-content: space-between; gap: 10px; }
.sc-price { min-width: 0; }
.sc-amount { display: block; font-family: var(--font-display); font-size: 17px; font-weight: 700; letter-spacing: -0.01em; font-variant-numeric: tabular-nums; }
.sc-avail { display: block; font-size: 11px; font-weight: 700; color: var(--text-muted); margin-top: 1px; }
.sc-avail.today { color: var(--status-good); }
.sc-avail.none { color: var(--status-warning); }
.sc-actions { display: flex; gap: 6px; flex-shrink: 0; }

/* ---- Discipline filter chips -------------------------------------------
   Same icon and tone as the cards they filter. */
.chip-filter { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 18px; }
.filter-chip {
  display: inline-flex; align-items: center; gap: 6px; min-height: 36px; padding: 7px 13px;
  border-radius: var(--radius-pill); border: 1px solid var(--border-strong); background: var(--surface);
  font-size: 12.5px; font-weight: 700; color: var(--text-secondary);
  transition: background var(--dur-fast) var(--ease-standard), border-color var(--dur-fast) var(--ease-standard), color var(--dur-fast) var(--ease-standard);
}
.filter-chip:hover { border-color: var(--tone-line, var(--accent)); color: var(--tone-deep, var(--accent-strong)); }
.filter-chip .icon { color: var(--tone-line, var(--accent)); }
.filter-chip.on { background: var(--tone-bg, var(--accent-soft)); border-color: var(--tone-line, var(--accent)); color: var(--tone-deep, var(--accent-ink)); }
.filter-chip.on .icon { color: var(--tone-deep, var(--accent-ink)); }

@media (max-width: 620px) {
  .session-card { padding: 14px 14px 12px; border-radius: 16px; }
  .sc-name { font-size: 15px; }
  /* The filter row scrolls sideways rather than wrapping into four tall rows
     that push the sessions themselves below the fold. */
  .chip-filter {
    flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none;
    margin-left: -16px; margin-right: -16px; padding: 2px 16px 8px;
  }
  .chip-filter::-webkit-scrollbar { display: none; }
  .filter-chip { flex-shrink: 0; }
}

/* ============================================================================
   Lyfe SESSION PASS + QR CHECK-IN
   Built like a boarding pass: a warm identity header, a perforated divider,
   then the code on clean white. The pass may be colourful; the code may not.
   ========================================================================== */
.pass {
  border-radius: 20px; overflow: hidden; background: var(--surface);
  border: 1px solid var(--border); box-shadow: 0 14px 40px -18px rgba(20,22,31,0.3);
}
.pass-head { background: linear-gradient(150deg, #FFF0C7 0%, #FFD8B8 100%); padding: 18px 20px 20px; text-align: center; color: #342D28; }
.pass-brand { display: inline-flex; align-items: center; gap: 6px; font-size: 10.5px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.09em; color: #8A5A06; }
.pass-title { font-family: var(--font-display); font-size: 20px; font-weight: 700; letter-spacing: -0.01em; margin: 8px 0 2px; color: #342D28; }
.pass-with { font-size: 13px; font-weight: 600; color: #74685D; }
.pass-when { display: flex; align-items: center; justify-content: center; gap: 7px; margin-top: 10px; font-size: 14.5px; font-weight: 700; color: #342D28; }
.pass-dot { color: #B8A38F; }
.pass-where { display: inline-flex; align-items: center; gap: 5px; margin-top: 5px; font-size: 12.5px; color: #74685D; font-weight: 600; }

/* Perforation, the detail that makes it read as a pass rather than a card.
   Purely decorative and outside the code's quiet zone. */
.pass-perf { position: relative; height: 20px; background: linear-gradient(180deg, #FFD8B8 0%, #FFD8B8 50%, var(--surface) 50%, var(--surface) 100%); display: flex; align-items: center; }
.pass-perf .pp-line { flex: 1; height: 0; border-top: 2px dashed color-mix(in srgb, #B8825A 45%, transparent); margin: 0 12px; }
.pass-perf .pp-l, .pass-perf .pp-r { width: 20px; height: 20px; border-radius: 50%; background: var(--page-bg); flex-shrink: 0; }
.pass-perf .pp-l { margin-left: -10px; }
.pass-perf .pp-r { margin-right: -10px; }

.pass-body { padding: 4px 20px 20px; text-align: center; }
/* Pure white, generous, no overlay of any kind, this is the scannable area. */
.pass-qr {
  display: block; width: 208px; max-width: 100%; margin: 6px auto 12px; padding: 10px;
  background: #FFFFFF; border: 1px solid var(--border); border-radius: 14px; cursor: zoom-in;
  transition: transform var(--dur-fast) var(--ease-standard);
}
.pass-qr:hover { transform: scale(1.02); }
.pass-qr .qr-svg { display: block; }
.pass-ref { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 13px; font-weight: 700; letter-spacing: 0.08em; color: var(--text-primary); }
.pass-status { display: flex; gap: 8px; justify-content: center; flex-wrap: wrap; margin-top: 12px; }
.pass-flag { display: inline-flex; align-items: center; gap: 5px; padding: 5px 10px; border-radius: var(--radius-pill); font-size: 11.5px; font-weight: 700; background: var(--surface-alt); color: var(--text-secondary); }
.pass-flag.on { background: #E0F0DA; color: #16691B; }
.pass-flag.warn { background: #FDF3DC; color: #8A5A06; }
.pass-hint { font-size: 12.5px; color: var(--text-muted); margin: 12px 0 0; }
.pass-hint.good { color: #16691B; font-weight: 600; }
.pass-modal .pass-actions { display: flex; flex-direction: column; gap: 8px; margin-top: 16px; }

/* Enlarged code, as big as the sheet allows, nothing else competing. */
.pass-zoom { text-align: center; }
.pass-zoom .pz-code { background: #FFFFFF; border-radius: 16px; padding: 14px; margin-bottom: 14px; }
.pass-zoom .pz-ref { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 15px; font-weight: 700; letter-spacing: 0.08em; }
.pass-zoom .pz-hint { font-size: 12.5px; color: var(--text-muted); margin: 6px 0 14px; }

/* ---- Scanner result states --------------------------------------------- */
.scan-result { text-align: center; }
.scan-result h3 { font-family: var(--font-display); font-size: 19px; margin: 12px 0 4px; }
.sr-badge { width: 60px; height: 60px; border-radius: 50%; display: flex; align-items: center; justify-content: center; margin: 0 auto; color: #fff; }
.sr-badge.good { background: linear-gradient(135deg, #0F864D, #12A05C); }
.sr-badge.bad  { background: linear-gradient(135deg, #A82B2B, #D03B3B); }
.sr-badge.warn { background: linear-gradient(135deg, #8A5A06, #EDA100); }
.sr-badge.info { background: linear-gradient(135deg, #17508F, #2A78D6); }
.sr-badge.pop { animation: srPop 0.42s var(--ease-glide); }
@keyframes srPop { 0% { transform: scale(0.5); opacity: 0; } 60% { transform: scale(1.08); } 100% { transform: scale(1); opacity: 1; } }
.sr-guest { display: flex; align-items: center; gap: 12px; text-align: left; padding: 13px; margin: 14px 0; background: var(--surface-alt); border-radius: 14px; }
.sr-guest strong { display: block; font-size: 14.5px; }
.sr-guest span { display: block; font-size: 12px; color: var(--text-secondary); margin-top: 1px; }
.sr-flags { display: flex; gap: 8px; justify-content: center; flex-wrap: wrap; margin-bottom: 12px; }
.sr-ref { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12px; letter-spacing: 0.06em; color: var(--text-muted); margin-bottom: 14px; }
.scan-result .btn-block + .btn-block { margin-top: 8px; }

/* ---- Booking progress timeline ----------------------------------------- */
.bk-progress { list-style: none; margin: 0; padding: 0; }
.bk-progress li { display: flex; gap: 12px; padding-bottom: 16px; position: relative; }
.bk-progress li:not(:last-child)::before { content: ""; position: absolute; left: 10px; top: 22px; bottom: 0; width: 2px; background: var(--border); }
.bk-progress li.done:not(:last-child)::before { background: var(--status-good); }
.bp-mark { width: 22px; height: 22px; border-radius: 50%; flex-shrink: 0; border: 2px solid var(--border-strong); background: var(--surface); display: flex; align-items: center; justify-content: center; color: #fff; z-index: 1; }
.bk-progress li.done .bp-mark { background: var(--status-good); border-color: var(--status-good); }
.bp-body strong { display: block; font-size: 13.5px; font-weight: 700; }
.bk-progress li:not(.done) .bp-body strong { color: var(--text-muted); font-weight: 600; }
.bp-body span { display: block; font-size: 11.5px; color: var(--text-muted); margin-top: 1px; }

@media (max-width: 620px) {
  .pass-qr { width: 240px; padding: 12px; }
  .pass-title { font-size: 18px; }
}

/* ---- Check-in desk ------------------------------------------------------
   Volunteers work standing up, one-handed, often in sun. Scanning is the whole
   job, so it gets the largest target on the screen. */
.scan-hero {
  display: flex; gap: 16px; align-items: center; flex-wrap: wrap;
  background: var(--tone-grad, linear-gradient(135deg, #E8F8F1, #CFEDE1));
  border: 1px solid color-mix(in srgb, var(--tone-line, var(--accent)) 26%, transparent);
  border-radius: 18px; padding: 18px; margin-bottom: 18px;
}
.scan-hero .sh-body { flex: 1; min-width: 220px; }
.scan-hero .sh-label { display: flex; align-items: center; gap: 7px; font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em; color: var(--tone-deep); }
.scan-hero .sh-value { font-family: var(--font-display); font-size: 30px; font-weight: 700; letter-spacing: -0.02em; margin: 6px 0 12px; font-variant-numeric: tabular-nums; }
.scan-hero .sh-value span { font-size: 14px; font-weight: 600; color: var(--tone-deep); letter-spacing: 0; }
.scan-hero .scan-cta { min-height: 64px; min-width: 220px; font-size: 16px; gap: 10px; flex-shrink: 0; }

.queue-row { display: flex; align-items: center; gap: 12px; padding: 11px 0; border-bottom: 1px solid var(--border); }
.queue-row:last-child { border-bottom: none; }
.queue-row .qr-body { flex: 1; min-width: 0; }
.queue-row .qr-body strong { display: block; font-size: 14px; }
.queue-row .qr-body span { display: block; font-size: 12px; color: var(--text-secondary); margin-top: 1px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

@media (max-width: 620px) {
  .scan-hero { flex-direction: column; align-items: stretch; }
  .scan-hero .scan-cta { width: 100%; min-height: 60px; }
}

/* The confirmation ticket stub now carries a real code, so it needs a genuine
   white quiet zone rather than the decorative treatment the old block pattern
   could get away with. */
.ticket-stub .stub-qr {
  display: block; width: 132px; max-width: 100%; padding: 8px; margin: 0 auto 8px;
  background: #FFFFFF; border: 1px solid var(--border); border-radius: 12px; cursor: zoom-in;
}
.ticket-stub .stub-qr .qr-svg { display: block; }

/* ---- Command card: two-column hero ------------------------------------
   A card spanning two grid tracks needs two columns of content. A single
   narrow stack down the left leaves the right half empty and opens a dead
   band above the footer; the rail gives the width a job. */
.card-command .cc-split { display: grid; grid-template-columns: minmax(0,1fr) auto; gap: 24px; align-items: start; margin-top: 2px; }
.card-command .cc-main { min-width: 0; }
.card-command .cc-title-row { align-items: center; gap: 12px; }
.card-command .cc-countdown {
  font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .06em;
  padding: 4px 10px; border-radius: var(--radius-pill);
  background: rgba(255,244,230,0.14); color: #F7E9DD; white-space: nowrap;
}
.card-command .cc-countdown.live { background: rgba(217,89,76,0.28); color: #FFE3DE; }
.card-command .cc-sub { margin-top: 6px; }

/* The rail is separated by a hairline rather than a box, so it reads as one
   card rather than a card containing another card. */
.card-command .cc-rail {
  min-width: 208px; max-width: 260px; padding-left: 24px;
  border-left: 1px solid rgba(255,244,230,0.12);
}
.card-command .cc-rail-label { font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: .07em; color: #9A8F82; margin-bottom: 9px; }
.card-command .cc-guest { display: flex; align-items: center; gap: 10px; min-width: 0; }
/* Scoped to .cc-guest-id, NOT to `.cc-guest > div`. The avatar is itself a div
   child of .cc-guest, so a positional selector here also captured the avatar's
   `.av-initials` span and repainted the initials muted grey on their gradient, 
   at (0,3,1) it beat `.avatar .av-initials` (0,2,0) and no amount of raising the
   avatar rule was the right fix. Naming the text block is. */
.card-command .cc-guest-id > strong { display: block; font-size: 13.5px; color: #FFF; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.card-command .cc-guest-id > span { display: block; font-size: 11.5px; color: #C9C0B5; margin-top: 1px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.card-command .cc-guest-id { min-width: 0; }
.card-command .cc-rail-flags { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 12px; }

@media (max-width: 900px) {
  /* Stacked, and the rail's divider becomes a top rule so it still reads as a
     separate group without a vertical line pointing nowhere. */
  .card-command .cc-split { grid-template-columns: minmax(0,1fr); gap: 16px; }
  .card-command .cc-rail { max-width: none; padding: 14px 0 0; border-left: none; border-top: 1px solid rgba(255,244,230,0.12); }
}

/* ============================================================================
   SUNLIT BANNER HEADER, the arrival moment on a portal's home screen
   Cream → Peach → Soft Tangerine → Warm Gold, the same wash as the attendee
   hero and the front door, sized down to a header band. Layered low-opacity
   radials rather than an end-to-end linear: the warmth pools at the right and
   bottom-right, and the title column sits over near-cream so the existing dark
   ink keeps its full contrast and no scrim is needed anywhere on it.
   ========================================================================== */
.page-header.ph-wash {
  position: relative; padding: 22px 26px; border-radius: var(--radius-hero);
  border: 1px solid var(--wash-line); margin-bottom: 22px; overflow: hidden;
  background-color: var(--wash-base);
  background-image:
    radial-gradient(88% 168% at 93% -34%, rgba(248,215,122,0.44) 0%, rgba(248,215,122,0.16) 36%, rgba(248,215,122,0) 66%),
    radial-gradient(70% 150% at 102% 128%, rgba(246,177,154,0.40) 0%, rgba(246,177,154,0.13) 42%, rgba(246,177,154,0) 74%),
    radial-gradient(44% 120% at 114% 56%, rgba(244,154, 98,0.24) 0%, rgba(244,154, 98,0) 68%),
    linear-gradient(112deg, rgba(255,232,213,0.00) 0%, rgba(255,232,213,0.24) 48%, rgba(255,232,213,0.54) 82%, rgba(255,232,213,0.30) 100%),
    linear-gradient(135deg, #FFFDF7 0%, #FFF8E8 56%, #FFF4E4 100%);
}
/* The tone hairline is the interior-page device, on the banner it would draw a
   hard line across a surface whose whole point is that nothing on it is hard. */
.page-header.ph-wash::after { display: none; }
.page-header.ph-wash h1 { color: var(--wash-ink); }
.page-header.ph-wash .ph-sub,
.page-header.ph-wash > p.muted { color: var(--wash-ink-soft); }
/* The lead icon reverts to white-on-wash: the tone-tinted chip was designed to
   sit on the frost page background and goes muddy over warm cream. */
.page-header.ph-wash .ph-icon {
  background: #FFFFFF; color: var(--tone-fg, var(--ink-tangerine));
  box-shadow: 0 2px 10px rgba(160,104,52,0.12), inset 0 0 0 1px rgba(196,120,64,0.14);
}
/* Outline buttons in the banner: the page border colour is a cool grey that
   reads as a smudge on the wash, so they take the wash's own warm hairline. */
.page-header.ph-wash .btn-outline {
  background: rgba(255,255,255,0.80); border-color: rgba(196,120,64,0.30); color: var(--ink-tangerine);
}
.page-header.ph-wash .btn-outline:hover { background: #FFFFFF; border-color: rgba(196,120,64,0.50); }
.page-header.ph-wash .crumbs { color: var(--wash-ink-soft); }
@media (max-width: 620px) {
  .page-header.ph-wash { padding: 18px 16px; border-radius: 18px; }
}

/* ============================================================================
   Lyfe SUNLIT EDITORIAL, REFINEMENT LAYER
   Loaded after styles.css and premium.css.

   premium.css established the card FAMILIES (which surface a card gets, and
   how a row of cards aligns to each other). This layer governs what happens
   INSIDE a card: a predictable content frame, a typography scale, an internal
   spacing rhythm, and cross-card band alignment that comes from shared
   structure rather than from forcing a height.

   The governing rule of the whole file: symmetry is structural, never
   dimensional. Nothing here sets a height that content has to fit into.
   ========================================================================== */

:root {
  /* ---- Card typography scale --------------------------------------------
     One scale, used by every card in the product. The previous layer let
     individual cards pick their own sizes, which is how 10.5px labels ended
     up carrying real information. Nothing in a card goes below 11.5px. */
  --ct-eyebrow: 11.5px;   /* category / label above the content            */
  --ct-title:   15.5px;   /* card title, session name, person's name       */
  --ct-title-lg:17.5px;   /* title when it IS the card's primary content   */
  --ct-kpi-sm:  24px;     /* figure in a compact strip card                */
  --ct-kpi:     30px;     /* figure in a standard primary card             */
  --ct-kpi-lg:  38px;     /* the one figure a page exists to communicate   */
  --ct-support: 13.5px;   /* supporting metric, secondary value            */
  --ct-meta:    12.5px;   /* metadata, timestamps, counts                  */
  --ct-cta:     13.5px;   /* in-card action label                          */

  /* ---- Internal card rhythm ---------------------------------------------
     4 / 8 / 12 / 16 / 20 / 24. Every gap inside a card is one of these. */
  --ci-1: 4px; --ci-2: 8px; --ci-3: 12px; --ci-4: 16px; --ci-5: 20px; --ci-6: 24px;

  --card-pad: 22px;          /* standard premium card, desktop             */
  --card-pad-compact: 18px;  /* compact dashboard card                     */

  /* ---- Slate chrome ------------------------------------------------------
     The operator console (admin sidebar + topbar) is a distinct surface from
     the warm attendee-facing product, a calm, neutral dark grey-blue that
     reads as serious command-centre furniture regardless of which event
     theme's accent colour is running through the rest of the product. The
     active-nav highlight still derives from --accent via color-mix so it
     stays in step with whatever theme an organizer publishes. */
  --chrome-bg: #171B23;
  --chrome-bg-raised: #1D222C;
  --chrome-border: rgba(226,232,245,0.08);
  --chrome-text: #94A0B4;
  --chrome-text-hover: #EAEEF5;
  --chrome-text-active: #FFFFFF;
  --chrome-active-bg: color-mix(in srgb, var(--accent) 22%, transparent);

  /* ---- Espresso, the command surface ----------------------------------
     Was #241C17 → #17120F, effectively black, which made every other card on
     the admin dashboard look like a secondary citizen. Warm espresso at a
     lower contrast keeps the card first without flattening its neighbours. */
  --espresso: linear-gradient(142deg, #44291C 0%, #362219 54%, #3E2519 100%);
}

/* ============================================================================
   1. THE CARD CONTENT FRAME
   Four bands, always in the same order, always in the same place:

     .cf-head   eyebrow / category / status         (auto)
     .cf-lede   the primary figure and its context  (auto)
     .cf-body   chart / progress / list / detail    (minmax(0,1fr))
     .cf-foot   insight or the single CTA           (auto)

   The body track is `minmax(0, 1fr)`, not `1fr`. `1fr` is `minmax(auto, 1fr)`,
   whose auto minimum refuses to shrink below the content's intrinsic size, 
   which is precisely how a grid child ends up overflowing its own card. The
   explicit 0 minimum lets the track shrink and lets its own overflow rules
   take over, so a long list scrolls inside the card instead of bursting out.
   ========================================================================== */
.cardf { display: grid; grid-template-rows: auto auto minmax(0,1fr) auto; min-width: 0; }
.cardf > * { min-width: 0; }
.cf-head { display: flex; align-items: center; gap: var(--ci-2); flex-wrap: wrap; min-height: 20px; }
.cf-head .cf-spacer { flex: 1 1 auto; }
.cf-lede { margin-top: var(--ci-2); }
.cf-body { min-width: 0; min-height: 0; margin-top: var(--ci-4); }
.cf-foot { margin-top: auto; padding-top: var(--ci-4); display: flex; align-items: center; gap: var(--ci-2); flex-wrap: wrap; }
.cf-foot .cf-spacer { flex: 1 1 auto; }
/* An empty band must not leave its margin behind. */
.cf-body:empty, .cf-lede:empty { display: none; margin: 0; }

/* Band alignment across a peer group. Setting a minimum on the LEDE band (not
   on the card) makes three cards' figures share a baseline and their bodies
   start on the same line, while each card is still free to grow as tall as its
   own content needs. This is the whole "symmetry without fixed height" idea in
   two declarations. */
.band > * { align-self: stretch; }
.band .cf-lede { min-height: var(--band-lede, 0); }
.band .cf-head { min-height: var(--band-head, 20px); }

/* ---- min-width: 0, systematically ---------------------------------------
   A flex or grid child's automatic minimum size is its content's intrinsic
   width. Any card holding a long session name, a practitioner's full name, a
   venue, or a monetary total will therefore push its own track wider than the
   card and overflow. Every card family and every flex row inside one is reset
   here rather than one component at a time. Components that need a real
   minimum (the command card's guest rail) declare it at higher specificity and
   still win. */
.card, .card-primary, .card-command, .card-flat, .kpi, .alert-card, .result-row { min-width: 0; }
.card > *, .card-primary > *, .card-command > *, .card-flat > *,
.card .flex > *, .card .flex-between > *,
.card-primary .flex > *, .card-command .flex > *,
.kpi > *, .alert-card > *, .result-row > * { min-width: 0; }

/* ---- Never truncate the load-bearing values ----------------------------
   Money, times, statuses and primary actions wrap or shrink; they do not get
   an ellipsis. A truncated total is worse than a wrapped one. */
.money, .cp-value, .cc-value, .kpi-value, .cf-figure,
.chip, .status-chip, .btn { text-overflow: clip; }
.cp-value, .cc-value, .kpi-value, .cf-figure { overflow-wrap: normal; word-break: keep-all; }

/* ---- Primary titles get two lines, not an ellipsis ---------------------
   "Private Yoga Therapy Session" is the thing the user is looking for. It is
   allowed to take two lines; only the description beneath it may be clamped. */
.cf-title, .cc-value.is-text, .rr-body > strong, .ag-title, .sd-name {
  line-height: 1.25; overflow-wrap: anywhere;
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden;
}
.cf-desc { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden; }

/* ============================================================================
   2. CARD TYPOGRAPHY + PADDING, APPLIED
   ========================================================================== */
.card { padding: var(--card-pad); display: flex; flex-direction: column; }
.card.compact { padding: var(--card-pad-compact); }
.card-primary, .card-command { padding: var(--card-pad); }

.cf-eyebrow, .cp-label, .cc-label, .kpi .kpi-label {
  font-size: var(--ct-eyebrow); font-weight: 700; text-transform: uppercase; letter-spacing: 0.055em;
}
.cf-figure, .cp-value { font-family: var(--font-display); font-size: var(--ct-kpi); font-weight: 700; letter-spacing: -0.022em; line-height: 1.08; font-variant-numeric: tabular-nums; }
.cf-figure.lg, .card-command .cc-value { font-size: clamp(30px, 2.9vw, var(--ct-kpi-lg)); }
.cf-support, .cp-sub, .card-command .cc-sub { font-size: var(--ct-support); }
.cf-meta { font-size: var(--ct-meta); color: var(--text-secondary); }
.card .btn-sm, .cf-foot .btn { font-size: var(--ct-cta); }

/* The 10.5px labels under the command card's stat row and the KPI strip's
   sub-line were the two smallest pieces of real information in the product. */
.card-command .cc-stat span { font-size: 11.5px; letter-spacing: 0.04em; }
.card-command .cc-stat b { font-size: 18px; }
.kpi .kpi-sub { font-size: var(--ct-meta); color: var(--text-secondary); margin-top: var(--ci-1); line-height: 1.4; }
.kpi .kpi-value { font-size: var(--ct-kpi-sm); }
.kpi-strip .kpi .kpi-value { font-size: var(--ct-kpi-sm); }

/* Text never touches an edge, and the figure always gets air under it. */
.kpi { padding: var(--card-pad-compact) var(--card-pad-compact) calc(var(--card-pad-compact) - 2px); }
.card-foot, .cf-foot { margin-top: auto; padding-top: var(--ci-4); }

/* ============================================================================
   3. STATUS CHIPS
   Were 11px uppercase in a 3px-tall padding box, legible only if you already
   knew what they said. The vocabulary in engine.js is already title case
   ("Partially Paid"), so dropping the transform costs nothing and buys back
   word shapes. The leading dot stays: it is the non-colour cue that keeps the
   status readable to anyone who cannot separate the fills.
   ========================================================================== */
.chip {
  min-height: 26px; padding: 4px 11px; gap: 6px; border-radius: var(--radius-pill);
  font-size: 11.5px; font-weight: 700; text-transform: none; letter-spacing: 0;
  line-height: 1.3; align-items: center;
}
.chip::before { width: 7px; height: 7px; }
/* A chip that carries its own live indicator drops the static dot, otherwise
   the label reads "• • Live". */
.chip:has(> .live-dot)::before { display: none; }
/* A chip inside a dense table row or a compact list may drop to 24px, but no
   lower, and it keeps the same type size. */
.data-table .chip, .result-row .chip, .ag-row .chip { min-height: 24px; padding: 3px 10px; }

/* Status stack, two labelled states read far faster than two uppercase pills
   sitting shoulder to shoulder, because the label says which is which. */
.status-stack { display: grid; gap: var(--ci-2); min-width: 0; }
.ss-row { display: grid; grid-template-columns: minmax(0,auto) 1fr; align-items: center; gap: var(--ci-3); min-width: 0; }
.ss-key { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em; opacity: .75; }
.ss-val { display: flex; min-width: 0; }

/* ============================================================================
   4. OPERATIONAL ALERTS
   Same three alerts, given a structure: what it is, how many, why it matters,
   one action. Peer alerts share a grid so their headline, context and CTA all
   sit on the same three lines regardless of text length.
   ========================================================================== */
.alert-strip { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: var(--ci-3); margin-bottom: var(--ci-5); }
.alert-card {
  display: grid; grid-template-columns: auto minmax(0,1fr);
  grid-template-rows: auto minmax(0,1fr) auto; gap: 2px var(--ci-3);
  align-items: start; padding: var(--ci-4); border-radius: var(--radius-lg); min-width: 0;
}
.alert-card > .ac-ico {
  grid-row: 1 / span 3; width: 38px; height: 38px; border-radius: 12px;
  display: flex; align-items: center; justify-content: center; flex-shrink: 0;
  background: rgba(255,255,255,0.72); box-shadow: inset 0 0 0 1px rgba(0,0,0,0.05);
}
.alert-card .ac-key { font-size: 10.5px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.08em; opacity: .8; }
.alert-card .ac-head { font-size: 14px; font-weight: 700; line-height: 1.3; min-width: 0; overflow-wrap: anywhere; }
.alert-card .ac-head b { font-variant-numeric: tabular-nums; }
.alert-card .ac-why { font-size: var(--ct-meta); font-weight: 600; opacity: .85; margin-top: var(--ci-1); }
.alert-card .ac-cta { margin-top: var(--ci-3); }
.alert-card .ac-cta .btn { background: rgba(255,255,255,0.82); border-color: rgba(0,0,0,0.10); color: inherit; font-weight: 700; }
.alert-card .ac-cta .btn:hover { background: #FFFFFF; }
@media (max-width: 420px) {
  .alert-card { grid-template-columns: minmax(0,1fr); }
  .alert-card > .ac-ico { grid-row: auto; margin-bottom: var(--ci-2); }
}

/* ============================================================================
   5. ENRICHED STRIP CARDS
   The six small dashboard tiles were label / figure / one fragment, generic
   enough to belong to any product. Each now carries a second real fact and a
   category identity: a 2px top line and a tinted icon plate, not a saturated
   fill. A tile stays a neutral surface; only its edge and its icon are tinted.
   ========================================================================== */
/* Guarded at 621px. Below that premium.css turns the strip into a snap-
   scrolling rail with `grid-template-columns: none`, and this file loads after
   it, so an unguarded rule here reinstates explicit tracks underneath
   `grid-auto-flow: column` and lays six tiles out in one 1000px row. */
@media (min-width: 621px) {
  .kpi-strip { display: grid; grid-template-columns: repeat(auto-fit, minmax(178px, 1fr)); gap: var(--ci-3); margin-bottom: var(--ci-5); }
}
.kpi.kpi-rich { display: grid; grid-template-rows: auto auto minmax(0,1fr); gap: 0; border-top-width: 2px; border-top-color: var(--tone-line, var(--accent)); }
/* Two lines reserved for the label. "Active attendees" and "Waitlist demand"
   wrap at these widths and "Outstanding" does not, so without a reserved row
   the figures in a six-tile strip sit on two different baselines, the exact
   ragged-row defect this pass exists to remove, in miniature. Reserving the
   row costs one line of white on the short labels and buys a shared baseline
   for every figure and every fact line beneath them. */
.kpi.kpi-rich .kpi-label { min-height: 30px; align-items: flex-start; line-height: 1.3; }
.kpi.kpi-rich .kpi-ico { margin-top: -1px; }
.kpi.kpi-rich .kpi-label { color: var(--tone-deep, var(--text-secondary)); }
.kpi.kpi-rich .kpi-ico { background: var(--tone-bg, var(--accent-soft)); color: var(--tone-deep, var(--accent-ink)); }
.kpi.kpi-rich .kpi-value { margin-top: var(--ci-2); }
.kpi-facts { margin-top: var(--ci-2); display: grid; gap: 3px; align-content: start; }
.kpi-facts span { font-size: var(--ct-meta); color: var(--text-secondary); line-height: 1.35; overflow-wrap: anywhere; }
.kpi-facts span b { font-weight: 700; color: var(--text-primary); font-variant-numeric: tabular-nums; }

/* ============================================================================
   6. LIVE OPERATIONS LIST
   "Happening now" was four equal-weight fragments per row. Time is what an
   operator scans by, so time gets its own column and its own weight.
   ========================================================================== */
.now-list { display: grid; gap: var(--ci-2); min-width: 0; }
.now-row {
  display: grid; grid-template-columns: 74px minmax(0,1fr) auto; align-items: center;
  gap: var(--ci-3); width: 100%; text-align: left; min-width: 0;
  padding: var(--ci-3) var(--ci-3) var(--ci-3) 0; border: none; background: none;
  border-radius: var(--radius-md); position: relative;
}
.now-row::before {
  content: ""; position: absolute; left: 0; top: 8px; bottom: 8px; width: 2px; border-radius: 2px;
  background: var(--row-line, var(--border-strong));
}
.now-row:hover { background: var(--surface-mist); }
/* One line, always. At 58px "10:15 AM" broke after the digits and pushed the
   state word onto a third line, so three consecutive rows each had a different
   internal rhythm. */
.now-time { font-family: var(--font-display); font-size: 14.5px; font-weight: 700; font-variant-numeric: tabular-nums; letter-spacing: -0.01em; padding-left: var(--ci-3); white-space: nowrap; }
.now-time small { display: block; font-size: 10.5px; font-weight: 700; letter-spacing: .05em; color: var(--text-muted); }
.now-body { min-width: 0; }
.now-body strong { display: block; font-size: 14px; font-weight: 650; line-height: 1.3; overflow-wrap: anywhere; }
.now-body span { display: block; font-size: var(--ct-meta); color: var(--text-secondary); margin-top: 2px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* A live dot that pulses once every few seconds rather than blinking: it says
   "this is current" without pulling the eye off the data. */
.live-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--status-good-fill); box-shadow: 0 0 0 0 color-mix(in srgb, var(--status-good-fill) 60%, transparent); animation: livePulse 2.8s var(--ease-standard) infinite; }
@keyframes livePulse { 0%,100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--status-good-fill) 55%, transparent); } 45% { box-shadow: 0 0 0 5px transparent; } }
@media (prefers-reduced-motion: reduce) { .live-dot { animation: none; } }
@media (max-width: 560px) {
  /* Recomposed, not shrunk: the status leaves the third column and sits under
     the session, so the title keeps the full width of the card. */
  .now-row { grid-template-columns: 70px minmax(0,1fr); row-gap: var(--ci-2); }
  .now-row > .chip { grid-column: 2; justify-self: start; }
  .now-body span { white-space: normal; }
}

/* ============================================================================
   7. CHARTS, closer legends, less dead canvas
   ========================================================================== */
.donut-wrap { gap: var(--ci-5); align-items: center; }
.donut-legend { gap: var(--ci-2); min-width: 0; }
.leg-item { font-size: var(--ct-support); gap: var(--ci-2); }
.leg-val { font-variant-numeric: tabular-nums; font-weight: 700; }
.leg-pct { font-size: var(--ct-meta); color: var(--text-secondary); }
/* A reading of the chart, drawn from the same numbers the chart plots, so the
   figure is not left to speak entirely for itself. */
.chart-read { margin-top: var(--ci-4); padding-top: var(--ci-3); border-top: 1px solid var(--border); font-size: var(--ct-support); color: var(--text-secondary); }
.chart-read b { color: var(--text-primary); font-weight: 700; }

/* Ranked demand list, was a label, a long track and a number, which left a
   band of empty canvas down the middle of the card at desktop widths. The
   label now sits above its own bar, so the bar uses the full width and the
   rows pack to roughly half the height. */
.demand-list { display: grid; gap: var(--ci-4); min-width: 0; }
.demand-row { display: grid; gap: 5px; min-width: 0; }
.demand-head { display: flex; align-items: baseline; gap: var(--ci-3); min-width: 0; }
.demand-head .dh-name { flex: 1; min-width: 0; font-size: var(--ct-support); font-weight: 650; line-height: 1.3; overflow-wrap: anywhere; }
.demand-head .dh-val { flex-shrink: 0; font-size: var(--ct-support); font-weight: 700; font-variant-numeric: tabular-nums; }
.demand-head .dh-val span { font-weight: 500; color: var(--text-secondary); font-size: var(--ct-meta); }
.demand-track { height: 9px; border-radius: 999px; background: var(--surface-alt); overflow: hidden; }
.demand-fill { height: 100%; border-radius: 999px; background: var(--series-1); transition: width var(--dur-slow) var(--ease-glide); }

/* Gauges: the label was sitting on the bar. */
.gauge-head { margin-bottom: 6px; }
.gauge .gh-name { font-size: var(--ct-support); }
.gauge .gh-val { font-size: var(--ct-support); font-weight: 700; font-variant-numeric: tabular-nums; }
.gauge-track { height: 8px; }

/* ============================================================================
   8. THE COMMAND SURFACE, warm espresso, not black
   ========================================================================== */
.card-command { background: var(--espresso); box-shadow: 0 14px 34px -16px rgba(54,34,25,0.50); }
.card-command::after { background: radial-gradient(circle, color-mix(in srgb, var(--tone-line, var(--accent)) 26%, transparent) 0%, transparent 70%); }
.card-command .cc-label { color: #C9B7A8; }
.card-command .cc-sub { color: #DCCBBD; }
.card-command .cc-stat span { color: #C2AF9F; }
.card-command .cc-rail-label { color: #C2AF9F; }
.card-command .cc-divider { background: rgba(255,244,230,0.14); }
/* Outline buttons on espresso: one rule instead of the inline styles that were
   repeated at six call sites. */
.card-command .btn-outline { border-color: rgba(255,244,230,0.26); color: #F6EEE6; background: rgba(255,244,230,0.07); }
.card-command .btn-outline:hover { background: rgba(255,244,230,0.14); border-color: rgba(255,244,230,0.42); }
.card-command .btn-quiet { border-color: rgba(255,244,230,0.16); color: #D6C7B9; background: transparent; }
.card-command .btn-quiet:hover { background: rgba(255,244,230,0.08); color: #F6EEE6; }

/* A softer internal panel for the guest, instead of a hard vertical rule
   between the booking and the person. */
.card-command .cc-panel {
  background: rgba(255,244,230,0.07); border: 1px solid rgba(255,244,230,0.10);
  border-radius: 14px; padding: var(--ci-4); min-width: 0;
}
/* Measured against the panel's actual composited ground (rgb(88,57,42)), not
   against the card's base gradient: #B5A493 came out at 4.28:1 there. */
.card-command .cc-panel .ss-key { color: #C9B7A8; }

/* ============================================================================
   9. WARM CHROME DETAIL
   ========================================================================== */
.side-group-label { color: #9E9084; margin: 20px 10px 7px; font-size: 10.5px; }
.side-group-label:first-child { margin-top: 6px; }
.side-link { padding: 9px 10px; font-size: 13.5px; }
.side-link.active { background: var(--chrome-active-bg); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent) 22%, transparent); }
.sidebar-nav { padding: 12px 10px 20px; }
.topbar .search-input { background: rgba(255,244,230,0.06); }
.topbar .search-input input::placeholder { color: #8B8078; }
.topbar .search-input .icon { color: #8B8078; }

/* Demo-only chrome. Kept out of the layout's load-bearing structure so that
   removing `data-demo` in production changes nothing about the composition. */
[data-demo-only] { opacity: 0.9; }

/* ============================================================================
   10. PRACTITIONER, the greeting banner
   A wide, nearly empty band became the practitioner's status line: the day,
   what is on it, and the two things they actually open from here.
   ========================================================================== */
.studio-head {
  position: relative; overflow: hidden; border-radius: var(--radius-hero);
  border: 1px solid var(--wash-line); padding: var(--ci-6);
  margin-bottom: var(--ci-5);
  background-color: #FFF9EC;
  background-image:
    radial-gradient(80% 150% at 92% -30%, rgba(248,215,122,0.42) 0%, rgba(248,215,122,0.15) 38%, rgba(248,215,122,0) 68%),
    radial-gradient(66% 140% at 100% 126%, rgba(252,227,213,0.72) 0%, rgba(252,227,213,0.22) 44%, rgba(252,227,213,0) 76%),
    linear-gradient(114deg, rgba(255,240,220,0.00) 0%, rgba(255,240,220,0.42) 52%, rgba(255,240,220,0.78) 88%);
  display: grid; grid-template-columns: minmax(0,1fr) auto; gap: var(--ci-5); align-items: center;
}
.sh-main { min-width: 0; }
.sh-greet { font-family: var(--font-display); font-size: clamp(22px, 2.4vw, 27px); font-weight: 700; letter-spacing: -0.02em; line-height: 1.15; margin: 0; color: var(--wash-ink); }
.sh-day { font-size: var(--ct-support); color: var(--wash-ink-soft); margin-top: 5px; font-weight: 600; }
.sh-facts { display: flex; flex-wrap: wrap; gap: var(--ci-2); margin-top: var(--ci-4); }
.sh-fact {
  display: inline-flex; align-items: baseline; gap: 6px; min-height: 30px; padding: 5px 12px;
  background: rgba(255,255,255,0.78); border: 1px solid color-mix(in srgb, var(--accent) 16%, transparent); border-radius: var(--radius-pill);
  font-size: var(--ct-meta); font-weight: 600; color: var(--wash-ink-soft);
}
.sh-fact b { font-size: 15px; font-weight: 700; color: var(--wash-ink); font-variant-numeric: tabular-nums; }
.sh-actions { display: flex; gap: var(--ci-2); flex-shrink: 0; }
.studio-head .btn-outline { background: rgba(255,255,255,0.82); border-color: color-mix(in srgb, var(--accent) 30%, transparent); color: var(--ink-tangerine); }
.studio-head .btn-outline:hover { background: #FFFFFF; border-color: color-mix(in srgb, var(--accent) 50%, transparent); }
@media (max-width: 760px) {
  .studio-head { grid-template-columns: minmax(0,1fr); gap: var(--ci-4); padding: var(--ci-5) var(--ci-4); }
  .sh-actions { width: 100%; }
  .sh-actions .btn { flex: 1; justify-content: center; }
}

/* ---- Premium agenda -----------------------------------------------------
   A three-column grid with a fixed time gutter, so every session's title
   starts on the same vertical line and the status can never collide with it.
   State is carried by the rail colour AND the chip, never by colour alone. */
.agenda { display: grid; gap: var(--ci-2); min-width: 0; }
.ag-row {
  display: grid; grid-template-columns: 72px minmax(0,1fr) auto; align-items: center;
  gap: var(--ci-4); min-width: 0; width: 100%; text-align: left;
  padding: var(--ci-3) var(--ci-3) var(--ci-3) 0; border: none; background: none;
  border-radius: var(--radius-md); position: relative;
}
.ag-row::before {
  content: ""; position: absolute; left: 0; top: 6px; bottom: 6px; width: 3px; border-radius: 3px;
  background: var(--row-line, var(--border-strong));
}
.ag-row:hover { background: var(--surface-mist); }
.ag-row.is-done { opacity: 0.72; }
.ag-row.is-live { background: color-mix(in srgb, var(--accent) 7%, transparent); }
.ag-time { padding-left: var(--ci-4); font-size: 14px; font-weight: 700; font-variant-numeric: tabular-nums; letter-spacing: -0.01em; }
.ag-time small { display: block; font-size: 11px; font-weight: 600; color: var(--text-muted); letter-spacing: 0; }
.ag-body { min-width: 0; }
.ag-title { font-size: 14px; font-weight: 650; }
.ag-sub { font-size: var(--ct-meta); color: var(--text-secondary); margin-top: 2px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
@media (max-width: 620px) {
  .ag-row { grid-template-columns: 62px minmax(0,1fr); row-gap: var(--ci-2); }
  .ag-row > .chip { grid-column: 2; justify-self: start; }
  .ag-sub { white-space: normal; }
}

/* ---- Overflow menu ------------------------------------------------------
   Three or four equal buttons crammed into a card footer made none of them
   look like the action. One primary, one secondary, the rest behind a menu. */
.act-row { display: flex; align-items: center; gap: var(--ci-2); flex-wrap: wrap; min-width: 0; }
.act-row .act-spacer { flex: 1 1 auto; }
.more-btn {
  width: 34px; height: 34px; border-radius: var(--radius-sm); flex-shrink: 0;
  display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid var(--border-strong); background: var(--surface); color: var(--text-secondary);
  font-weight: 700; letter-spacing: 1px; font-size: 14px; line-height: 1;
}
.more-btn:hover { border-color: var(--accent); color: var(--accent-ink); }
.card-command .more-btn { border-color: rgba(255,244,230,0.22); background: rgba(255,244,230,0.07); color: #E4D8CC; }
.card-command .more-btn:hover { background: rgba(255,244,230,0.15); color: #FFF; border-color: rgba(255,244,230,0.4); }
@media (max-width: 560px) {
  .act-row > .btn { flex: 1 1 auto; justify-content: center; }
  .act-row > .more-btn { flex: 0 0 auto; }
}

/* ============================================================================
   11. WELCOME, presence without decoration
   The screen was correct and empty: a small lockup and three placeholder-ish
   tiles floating in a very large canvas. The fix is proportion and a defined
   centre, not ornament.
   ========================================================================== */
.welcome-shell {
  background-color: #FFF9EF;
  background-image:
    radial-gradient(58% 46% at 84% 4%, rgba(251,232,183,0.78) 0%, rgba(251,232,183,0.26) 44%, rgba(251,232,183,0) 72%),
    radial-gradient(52% 44% at 6% 98%, rgba(245,196,179,0.62) 0%, rgba(245,196,179,0.20) 46%, rgba(245,196,179,0) 76%),
    radial-gradient(44% 38% at 100% 76%, rgba(250,219,198,0.66) 0%, rgba(250,219,198,0) 70%),
    radial-gradient(70% 52% at 50% 46%, rgba(255,253,248,0.86) 0%, rgba(255,253,248,0.30) 52%, rgba(255,253,248,0) 78%),
    linear-gradient(152deg, #FFFDF8 0%, #FFF9EF 56%, #FFF4E6 100%);
}
.welcome-inner { max-width: 452px; }
/* A defined centre: the content sits on a barely-there panel, so the eye has
   an edge to hold onto instead of a field of cream. No shadow stack, no
   border, just a lift of white and one hairline. */
.welcome-inner::before {
  content: ""; position: absolute; inset: -34px -30px -30px; z-index: -1;
  border-radius: 34px; background: rgba(255,255,255,0.50);
  border: 1px solid color-mix(in srgb, var(--accent) 10%, transparent);
  box-shadow: 0 30px 70px -40px rgba(150,96,48,0.22);
}
.welcome-logo { gap: 12px; margin-bottom: 22px; }
.welcome-logo img { width: 46px; height: 46px; }
.welcome-logo span { font-size: 25px; letter-spacing: -0.02em; }
.welcome-title { font-size: clamp(32px, 7.4vw, 43px); margin-bottom: 12px; }
.welcome-sub { font-size: 16px; max-width: 24em; margin-bottom: 24px; }

/* Stat pills, not stat cards: the numbers are reassurance, not content. */
/* What Lyfe does, not how much of it this particular festival has loaded.
   The three counts that used to sit here, practitioners, session types,
   venues, read as fixed inventory and shrank the product to one catalogue. */
.welcome-points {
  list-style: none; padding: 0; margin: 0 0 26px;
  display: grid; gap: var(--ci-2); text-align: left; justify-items: start;
}
.welcome-points li {
  display: grid; grid-template-columns: auto minmax(0,1fr); align-items: center; gap: 11px;
  width: 100%; min-height: 44px; padding: 10px 14px; min-width: 0;
  background: rgba(255,255,255,0.72); border: 1px solid color-mix(in srgb, var(--accent) 13%, transparent); border-radius: 14px;
  font-size: var(--ct-support); font-weight: 600; color: var(--wash-ink-soft);
}
.welcome-points .icon { color: var(--ink-tangerine); flex-shrink: 0; }

/* Three levels, visibly three. */
/* Dark ink on the tangerine fill, matching every other primary button in the
   product. White on this gradient measures 2.5:1, the depth here has to come
   from the gradient and the shadow, not from inverting the label. */
.welcome-shell .btn-primary {
  background: linear-gradient(135deg, #F47A3D 0%, #F5904F 100%);
  color: var(--accent-contrast); box-shadow: 0 10px 24px -12px rgba(196,90,36,0.55);
  border: 1px solid rgba(180,80,30,0.18);
}
.welcome-shell .btn-primary:hover { background: linear-gradient(135deg, #EE7033 0%, #F08644 100%); }
.welcome-signin { background: rgba(255,255,255,0.92); box-shadow: 0 4px 14px -8px rgba(150,96,48,0.28); }
.welcome-explore { margin-top: 14px; }
.welcome-explore .we-sub { font-size: var(--ct-meta); }
.welcome-foot { margin-top: 22px; font-size: var(--ct-meta); line-height: 1.5; }
.welcome-foot b { display: block; font-weight: 700; color: var(--wash-ink); font-size: 13px; margin-bottom: 1px; }
@media (max-width: 520px) {
  .welcome-inner::before { inset: -22px -16px -18px; border-radius: 26px; }
}

/* ============================================================================
   12. RESPONSIVE, recompose, never shrink
   Type gets LARGER on small screens, not smaller: a phone is held further from
   nothing and closer to everything, and these figures are the reason the
   screen exists.
   ========================================================================== */
@media (min-width: 621px) and (max-width: 1024px) {
  .kpi-strip { grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); }
}
@media (max-width: 700px) {
  :root {
    --card-pad: 18px; --card-pad-compact: 16px;
    --ct-eyebrow: 11.5px; --ct-support: 14px; --ct-meta: 13px; --ct-cta: 14px;
    --ct-kpi-sm: 25px; --ct-title: 16px;
  }
  .card-command .cc-stat b { font-size: 17px; }
  .card-command .cc-stat span { font-size: 11.5px; }
  /* The espresso card's stat row goes two-up rather than squeezing three
     tabular figures into a phone's width. */
  .card-command .cc-stat-row { grid-template-columns: repeat(2, minmax(0,1fr)); gap: var(--ci-3) var(--ci-4); }
}
/* The KPI strip's own phone treatment lives in premium.css: below 620px it
   becomes a snap-scrolling rail rather than six full-width tiles that push the
   real content off the screen. That rule sets `grid-template-columns: none`
   with `grid-auto-flow: column`, so ANY later `grid-template-columns` on
   `.kpi-strip`, even inside a narrower media query, reinstates explicit
   tracks while auto-flow is still column, and the strip lays six 228px tiles
   out in one 1000px row. Hence the min-width guard on the rule above and the
   absence of one here: the rail is the phone layout. */
@media (max-width: 430px) {
  .alert-strip { grid-template-columns: minmax(0,1fr); }
  .donut-wrap { flex-direction: column; gap: var(--ci-4); }
  .donut-legend { width: 100%; }
}

/* Stat row on a light primary card, the light-surface twin of the espresso
   card's .cc-stat-row, so the two cards beside each other carry the same
   sub-structure at the same sizes instead of one being hand-styled inline. */
.cp-stat-row { display: grid; grid-template-columns: repeat(3, minmax(0,1fr)); gap: var(--ci-3); }
.cp-stat-row > div { min-width: 0; }
.cp-stat-row b { display: block; font-size: 18px; font-weight: 700; letter-spacing: -0.01em; font-variant-numeric: tabular-nums; color: var(--text-primary); }
.cp-stat-row span { display: block; font-size: 11.5px; color: var(--tone-deep, var(--text-secondary)); text-transform: uppercase; letter-spacing: .04em; font-weight: 700; margin-top: 2px; }
@media (max-width: 700px) { .cp-stat-row { grid-template-columns: repeat(2, minmax(0,1fr)); gap: var(--ci-3) var(--ci-4); } }

/* Menu sheet, the body of an overflow menu. */
.menu-sheet { display: grid; gap: 4px; }
.menu-item {
  display: flex; align-items: center; gap: 11px; width: 100%; text-align: left;
  min-height: 46px; padding: 11px 13px; border-radius: var(--radius-md);
  border: 1px solid transparent; background: none; font-size: 14px; font-weight: 600; color: var(--text-primary);
}
.menu-item:hover { background: var(--surface-mist); border-color: var(--border); }
.menu-item .icon { color: var(--text-secondary); flex-shrink: 0; }
.menu-item.danger { color: var(--status-critical); }
.menu-item.danger .icon { color: var(--status-critical); }
.menu-item.danger:hover { background: #FDF1F0; border-color: #F3D6D3; }

/* ---- The body band absorbs the slack ------------------------------------
   With `minmax(0,1fr)` the body track grows to whatever height the tallest
   card in the row establishes, which is right, but only if something inside
   it grows too. Otherwise the extra height lands as a dead band between the
   last stat and the footer, which is what the first pass produced. Making the
   body a grid whose chart row is `minmax(34px,1fr)` hands the slack to the
   trend line, so a taller card gets a taller chart rather than a bigger gap. */
.cf-body.body-chart { display: grid; grid-template-rows: minmax(34px,1fr) auto auto; align-content: stretch; }
.cf-body.body-chart > .sparkline {
  /* Capped and centred in its band. The band takes the slack so the footer
     still lines up with its neighbours', but a trend line stretched to 140px
     stops being a sparkline and starts being an area chart competing with the
     figure above it. */
  width: 100%; height: 100%; min-height: 40px; max-height: 88px; align-self: center;
}
.cf-body.body-stack { display: grid; align-content: space-between; gap: var(--ci-4); }

/* The espresso card's two subjects, side by side. The rail is now a panel, so
   the vertical hairline that used to separate them goes: a panel and a rule
   doing the same job at the same place is one device too many. */
.card-command .cc-split { display: grid; grid-template-columns: minmax(0,1fr) minmax(216px, 288px); gap: var(--ci-6); align-items: start; }
.card-command .cc-rail { border-left: none; padding-left: 0; }
.card-command .cc-when { font-size: var(--ct-meta); color: #B9A797; margin-top: var(--ci-3); }
.card-command .cc-value.is-text { margin-top: 6px; }
.card-command .cf-lede:empty { display: none; }
@media (max-width: 900px) {
  .card-command .cc-split { grid-template-columns: minmax(0,1fr); gap: var(--ci-4); }
}

/* The explore action reads as one line plus a caption, so the glyph and label
   are grouped rather than sitting as loose children of a flex column. */
.welcome-explore .we-top { display: inline-flex; align-items: center; gap: 7px; }

/* The "Available Today" flag was 10px uppercase, the smallest type in the
   product, carrying the one fact that decides whether a card is worth reading.
   Sentence case at 11.5px in the same pill geometry as every other chip. */
.badge-today {
  font-size: 11.5px; text-transform: none; letter-spacing: 0;
  padding: 4px 11px; min-height: 26px; display: inline-flex; align-items: center;
}

/* ===========================================================================
   13. SMART MATCH, the config-driven attendee journey. Sunlight entering the
   page, not a gradient graphic (brief §21): the same wash tokens the hero and
   welcome screen already use, at low opacity, over the product's own cream, 
   never a new palette invented just for this screen.
   =========================================================================== */

/* Full-bleed: .sm-shell used to only cancel the .container's own 24px
   padding, so on any viewport wider than the container's 1180px max-width it
   sat as a narrow card with large, unstyled empty margins on both sides (and,
   since it only sized to its own content, empty space below too). The
   left/right calc breaks it out to the true viewport edge regardless of how
   wide the container is; the shell then fills that width itself with the
   sunlit wash instead of leaving it bare. min-height targets the remaining
   viewport below the two-row sticky topbar so the page reads as one
   intentional full-height screen rather than a card floating above blank
   space, while staying min- (not fixed-) so long question/result content is
   still free to grow and scroll normally. */
.sm-shell {
  position: relative; z-index: 0;
  min-height: calc(100dvh - 132px);
  width: 100vw; margin-left: calc(50% - 50vw); margin-right: calc(50% - 50vw); margin-top: -24px; margin-bottom: 0;
  padding: 56px 24px 72px;
  display: flex; align-items: center; justify-content: center;
}
/* The wash used to live on a `position: fixed` pseudo-element so it would
   cover the full viewport regardless of how much bottom padding sat below
   the content. That broke as soon as the entrance-animation work landed:
   `.container`'s `animation: viewIn` ends on `transform: translateY(0)`,
   and per spec ANY transform value other than the keyword `none`, even an
   identity translate, makes that element the containing block for its
   `position: fixed` descendants. So the "fixed" wash was actually anchored
   to `.container`'s own box the whole time, which only ever grows to fit
   its content: scrolling past that box's bottom edge left the wash behind
   and exposed the plain page background above it as a gap.
   Painting the wash on `body` instead sidesteps the containing-block trap
   entirely, body is never transformed, and a real background naturally
   extends to cover the full scrollable area, no fixed positioning needed. */
body.sm-wash {
  background-color: var(--wash-base); background-image: var(--wash-sunlit);
}
@media (max-width: 700px) { .sm-shell { min-height: calc(100dvh - 108px); margin-top: -16px; padding: 32px 16px 48px; } }

/* -- Entry -- */
.sm-entry {
  max-width: 620px; width: 100%; text-align: center;
  background: rgba(255,255,255,0.62); border: 1px solid var(--wash-line);
  border-radius: var(--radius-hero); padding: 48px 40px; backdrop-filter: blur(6px);
}
@media (max-width: 700px) { .sm-entry { padding: 34px 22px; } }
.sm-kicker {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: var(--ct-eyebrow); font-weight: 700; letter-spacing: 0.02em;
  color: var(--ink-tangerine); background: color-mix(in srgb, var(--accent) 12%, transparent);
  padding: 6px 14px; border-radius: var(--radius-pill); margin-bottom: 18px;
}
.sm-entry h1 {
  font-family: var(--font-display); color: var(--wash-ink);
  font-size: clamp(26px, 5vw, 36px); line-height: 1.15; letter-spacing: -0.02em;
  margin: 0 0 12px;
}
.sm-entry > p { font-size: 15px; line-height: 1.55; color: var(--wash-ink-soft); max-width: 40ch; margin: 0 auto 18px; }
.sm-time-note {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: var(--ct-meta); color: var(--wash-ink-soft); margin-bottom: 28px;
}
.sm-entry-actions { display: flex; flex-direction: column; align-items: center; gap: 12px; }
.sm-entry-actions .btn-lg { min-height: 52px; padding: 0 34px; font-size: 15px; }
.sm-entry-foot { margin-top: 26px; font-size: 12px; color: var(--wash-ink-soft); border-top: 1px solid var(--wash-line); padding-top: 18px; }

/* -- Question screens -- */
.sm-question { max-width: 820px; width: 100%; }
.sm-progress { display: flex; justify-content: space-between; font-size: var(--ct-meta); color: var(--wash-ink-soft); font-weight: 600; margin-bottom: 8px; }
.sm-progress-track { height: 5px; border-radius: 999px; background: rgba(154,110,70,0.16); overflow: hidden; margin-bottom: 26px; }
.sm-progress-fill { height: 100%; background: linear-gradient(90deg, #F49A62, #F8D77A); border-radius: inherit; transition: width var(0.25s) var(--ease-standard, ease); }
.sm-question h2 {
  font-family: var(--font-display); color: var(--wash-ink); font-size: clamp(21px, 4vw, 27px);
  line-height: 1.25; letter-spacing: -0.01em; margin: 0 0 6px;
}
.sm-question .sm-sub { font-size: 14px; color: var(--wash-ink-soft); margin: 0 0 22px; }

.sm-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: var(--ci-3); }
.sm-card {
  position: relative; display: flex; flex-direction: column; align-items: flex-start; gap: 8px;
  min-height: 128px; padding: 20px 18px; text-align: left;
  background: rgba(255,255,255,0.72); border: 1.5px solid var(--wash-line); border-radius: var(--radius-md);
  transition: transform var(--dur-fast, 0.15s) var(--ease-standard, ease), border-color var(--dur-fast, 0.15s), box-shadow var(--dur-fast, 0.15s);
}
.sm-card:hover { transform: translateY(-2px); border-color: var(--tone-line, var(--ink-tangerine)); }
.sm-card[aria-checked="true"], .sm-card.on {
  border-color: var(--tone-line, var(--ink-tangerine)); background: var(--tone-bg, color-mix(in srgb, var(--accent) 8%, transparent));
  box-shadow: 0 6px 18px -10px color-mix(in srgb, color-mix(in srgb, var(--accent) 60%, black) 35%, transparent);
}
.sm-card-ico { color: var(--tone-fg, var(--ink-tangerine)); }
.sm-card-label { font-size: var(--ct-title); font-weight: 700; color: var(--wash-ink); line-height: 1.3; }
.sm-card-desc { font-size: var(--ct-meta); color: var(--wash-ink-soft); line-height: 1.4; }
.sm-card-check {
  position: absolute; top: 14px; right: 14px; width: 22px; height: 22px; border-radius: 50%;
  background: var(--tone-fg, var(--ink-tangerine)); color: #fff; display: flex; align-items: center; justify-content: center;
}

.sm-cards-day { grid-template-columns: repeat(auto-fit, minmax(84px, 1fr)); }
.sm-day {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px;
  min-height: 84px; padding: 14px 8px; border-radius: var(--radius-md);
  background: rgba(255,255,255,0.72); border: 1.5px solid var(--wash-line);
}
.sm-day:hover { border-color: var(--ink-tangerine); transform: translateY(-2px); }
.sm-day.on { border-color: var(--ink-tangerine); background: color-mix(in srgb, var(--accent) 10%, transparent); box-shadow: 0 6px 18px -10px color-mix(in srgb, color-mix(in srgb, var(--accent) 60%, black) 35%, transparent); }
.sm-day .sd-name { font-size: var(--ct-meta); font-weight: 600; color: var(--wash-ink-soft); text-transform: uppercase; letter-spacing: 0.03em; }
.sm-day .sd-num { font-size: 20px; font-weight: 700; color: var(--wash-ink); }

.sm-nav { display: flex; align-items: center; justify-content: space-between; margin-top: 22px; }
.sm-nav .link-btn { color: var(--wash-ink-soft); }

/* -- Results -- */
.sm-chip-row { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 22px; }
.sm-pref-chip {
  display: inline-flex; align-items: center; gap: 6px; min-height: 30px;
  padding: 6px 13px; border-radius: var(--radius-pill); border: 1px solid var(--wash-line);
  background: #fff; font-size: var(--ct-meta); font-weight: 600; color: var(--wash-ink);
}
.sm-pref-chip:hover { border-color: var(--ink-tangerine); color: var(--ink-tangerine); }

.sm-hero-label { display: flex; align-items: center; gap: 6px; font-size: var(--ct-eyebrow); font-weight: 700; color: var(--ink-tangerine); margin-bottom: 10px; }

.sm-best { position: relative; padding: 28px; overflow: visible; }
.sm-best-badge {
  position: absolute; top: -12px; left: 26px; background: linear-gradient(135deg, #F47A3D, #F8D77A);
  color: #3A2317; font-size: 11.5px; font-weight: 700; padding: 5px 14px; border-radius: var(--radius-pill);
  box-shadow: 0 4px 12px -4px rgba(196,90,36,0.45);
}
.sm-best-top { display: flex; align-items: center; gap: 14px; margin: 8px 0 20px; flex-wrap: wrap; }
.sm-best-id { min-width: 0; flex: 1; }
.sm-best-svc { font-size: var(--ct-title-lg); font-weight: 700; color: var(--text-primary); display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.sm-best-with { font-size: var(--ct-support); color: var(--muted); margin-top: 2px; }
.sm-why { background: var(--surface-alt); border-radius: var(--radius-md); padding: 16px 18px; margin-bottom: 18px; }
.sm-why-title { font-size: var(--ct-eyebrow); font-weight: 700; color: var(--text-primary); margin-bottom: 10px; }
.sm-why-row { display: flex; align-items: flex-start; gap: 9px; font-size: var(--ct-support); color: var(--text-primary); padding: 5px 0; }
.sm-why-row svg { color: #1BAF7A; flex-shrink: 0; margin-top: 2px; }
.sm-best-facts { display: flex; flex-wrap: wrap; gap: 8px 18px; font-size: var(--ct-support); color: var(--muted); font-weight: 600; margin-bottom: 18px; }
.sm-avail-label { font-size: var(--ct-eyebrow); font-weight: 700; color: var(--text-primary); margin-bottom: 10px; }

.sm-alt { padding: var(--card-pad-compact); }

.sm-nomatch { text-align: center; padding: 40px 32px; }
.sm-nomatch-ico {
  width: 52px; height: 52px; border-radius: 50%; margin: 0 auto 16px;
  display: flex; align-items: center; justify-content: center;
  background: var(--tone-bg, color-mix(in srgb, var(--accent) 12%, transparent)); color: var(--ink-tangerine);
}
.sm-nomatch h3 { font-size: 18px; color: var(--text-primary); margin: 0 0 8px; }
.sm-nomatch > p { max-width: 44ch; margin: 0 auto 22px; }
.sm-fallbacks { display: flex; flex-direction: column; gap: 8px; max-width: 420px; margin: 0 auto; }
.sm-fallback {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  min-height: 48px; padding: 10px 18px; border-radius: var(--radius-md);
  border: 1.5px solid var(--border-strong); background: var(--surface); font-size: 14px; font-weight: 600; color: var(--text-primary);
}
.sm-fallback:hover { border-color: var(--ink-tangerine); color: var(--ink-tangerine); }
.sm-fallback-count { font-size: var(--ct-meta); font-weight: 700; color: var(--ink-tangerine); background: color-mix(in srgb, var(--accent) 12%, transparent); padding: 3px 10px; border-radius: var(--radius-pill); }

@media (max-width: 430px) {
  .sm-best-badge { position: static; display: inline-block; margin-bottom: 12px; }
  .sm-best-top { gap: 10px; }
}

/* ===========================================================================
   14. SMART MATCH, Admin Configuration Centre. Plain card/list/table
   furniture reusing the product's existing primitives (.card, .chip, .switch,
   dataTable) rather than inventing a parallel visual language for one screen.
   =========================================================================== */
.sm-adm-qlist { display: flex; flex-direction: column; gap: 10px; }
.sm-adm-row { padding: var(--card-pad-compact); }

.sm-adm-choice-list { display: flex; flex-direction: column; gap: 8px; max-height: 50vh; overflow-y: auto; }
.sm-adm-choice-row {
  display: flex; align-items: flex-start; gap: 12px; padding: 12px;
  border: 1px solid var(--border); border-radius: var(--radius-md); min-width: 0;
}
.sm-adm-choice-ico {
  width: 32px; height: 32px; border-radius: 10px; flex-shrink: 0;
  display: flex; align-items: center; justify-content: center;
  background: var(--tone-bg); color: var(--tone-fg);
}
.sm-adm-tagchip { font-size: 10.5px; min-height: auto; padding: 3px 9px; margin: 0 4px 4px 0; }

/* The one piece of chrome the attendee shell doesn't normally carry, shown
   only while an admin is previewing an unpublished draft (App.smStartPreview).
   Sits above the shell itself so it survives every navigation the admin makes
   while previewing, including clicking into a session from the results screen. */
.sm-preview-banner {
  position: sticky; top: 0; z-index: 50;
  display: flex; align-items: center; justify-content: center; gap: 16px; flex-wrap: wrap;
  background: var(--espresso); color: #F8F2E9; padding: 10px 20px; font-size: 13px; font-weight: 600;
}
.sm-preview-banner .btn {
  background: rgba(255,255,255,0.14); color: #fff; border: 1px solid rgba(255,255,255,0.28);
  min-height: 32px; padding: 5px 14px; font-size: 12.5px;
}
.sm-preview-banner .btn:hover { background: rgba(255,255,255,0.22); }

/* ===========================================================================
   15. DARK THEME
   A toggle (App.toggleTheme, next to the notification bell in both topbars),
   persisted per-browser, defaulting to the visitor's OS preference on first
   visit. `[data-theme="dark"]` on <html> is the single switch everything else
   below reacts to.

   Palette: a neutral dark slate with a cool blue undertone, never warm/brown
, so the product's tangerine brand colour reads as an accent ON the theme
   rather than the theme itself. The token layer does most of the work:
   --page-bg/--surface/--border/--text-* etc. are consumed pervasively
   throughout the product, so redefining them here re-themes admin tables,
   cards, forms, modals, the attendee shell, Smart Match's results screen and
   the wallet/booking flows without touching another rule.

   What the tokens can't reach on their own falls into two groups, both
   handled explicitly below:
     1. Screens authored with their own literal colour instead of a token, 
        the hero, welcome and sign-in "sunlit wash" screens, the practitioner
        greeting band, and Smart Match's entry/question screens, each built
        from layered radial gradients plus translucent "glass" cards floating
        on them. These get a night counterpart: the same composition, the
        same glass-card language, recoloured to the same cool slate rather
        than inverted into something muddy.
     2. Components whose text colour is only correct because it was authored
        against a background that is ALWAYS light (a tone-tinted card, an
        accent-soft chip) rather than against the page's own tokens. Flipping
        --text-primary to a light colour for dark mode is exactly right for
        the 95% of the product sitting on --surface/--page-bg, but for this
        remaining handful, the fix is an explicit dark-mode text colour, not
        the inherited (now-light) default. Each is called out at its rule.

   Deliberately EXCLUDED from all of this, on purpose:
     - the QR pass (.pz-code) and the wallet balance card artwork, both are
       printed/physical-object surfaces (a scannable code, a card face) that
       must look the same regardless of which theme is reading them;
     - the semantic colour families (.tone-*, .chip-*, .callout-*, .alert-card,
       .badge-today) as SURFACES, each already carries its own light
       background and dark foreground, so they work unchanged as small
       coloured chips sitting on a dark page, the same way they would on any
       other coloured surface;
     - the dark sidebar/topbar chrome (--chrome-*, --espresso), it was already
       dark in light mode, by design, so it needs no theme-specific override.
   ========================================================================== */
:root[data-theme="dark"] {
  color-scheme: dark;
  --page-bg: #12141A;
  --surface: #1A1D24;
  --surface-alt: #20232C;
  --surface-sand: #262A34;
  --surface-clay: #262A34;
  --surface-mist: #181B22;
  --surface-1: #15171D;
  --border: #2A2E38;
  --border-strong: #3B404C;
  --text-primary: #F2F4F8;
  --text-secondary: #ADB3C0;
  --text-muted: #8D93A2;
  --muted: #8D93A2;
  --gridline: #2A2E38;

  --accent-soft: #262A34;
  /* --ink-tangerine and --accent-ink are the same value, used throughout as
     "brand-coloured text/icon on a light ground", a role almost every
     surface now plays in reverse. A brighter tangerine keeps the same brand
     hue legible on the dark grounds it now actually sits on. */
  --ink-tangerine: #F5A366;
  --accent-ink: #F5A366;

  /* Ink-safe status colours were tuned for LIGHT backgrounds; on a dark
     surface the same hues need to lift, not darken, to stay legible. */
  --status-good: #34D399;
  --status-warning: #FBBF24;
  --status-serious: #F5824D;
  --status-critical: #F76A5E;
  --status-info: #5EB3E0;
  --status-pending: #E8A93F;
  --status-neutral: #A7ACB8;

  /* The wash tokens flip from sunrise cream to a cool slate night, the same
     layered-radial recipe, at low alpha over the same dark ground as the rest
     of the app, so hero/welcome/sign-in/Smart Match feel like part of one
     dark product instead of a separate warm graphic pasted onto it. */
  --wash-base: #12141A;
  --wash-ink: #F2F4F8;
  --wash-ink-soft: #ADB3C0;
  --wash-line: rgba(232,236,245,0.12);
  --wash-sunlit:
    radial-gradient(118% 148% at 86% -22%, rgba(120,140,168,0.16) 0%, rgba(120,140,168,0.06) 33%, rgba(120,140,168,0) 61%),
    radial-gradient(84% 118% at 101% 110%, rgba(90,110,140,0.16) 0%, rgba(90,110,140,0.05) 40%, rgba(90,110,140,0) 71%),
    radial-gradient(58%  98% at 113%  60%, color-mix(in srgb, var(--accent) 26%, transparent) 0%, transparent 66%),
    radial-gradient(70% 120% at  -8%  92%, rgba(70, 86, 112,0.20) 0%, rgba(70, 86, 112,0) 64%),
    linear-gradient(116deg, rgba(14,16,21,0) 0%, rgba(14,16,21,0.30) 44%, rgba(10,11,15,0.66) 79%, rgba(10,11,15,0.38) 100%),
    linear-gradient(135deg, #171922 0%, #12141A 54%, #14161D 100%);

  /* A near-black canvas swallows a soft rgba(0,0,0,X) shadow entirely, so
     elevation reads through a slightly stronger shadow plus the hairline
     border every card already carries, the shadow becomes a second, quieter
     cue rather than the only one. */
  --shadow-1: 0 8px 24px rgba(0,0,0,0.40);
  --shadow-2: 0 16px 36px rgba(0,0,0,0.50);
  --shadow-pop: 0 20px 48px rgba(0,0,0,0.62);

  /* The "glass" card floating on a sunlit wash was rgba(255,255,255,X), a
     translucent lift toward white. Its night counterpart lifts toward a cool
     slate instead, so it still reads as a card sitting a shade above the
     wash rather than a bright cutout punched into a dark screen. */
  --glass-rgb: 36, 40, 49;
}

/* ---- Full-surface wash screens: same composition, dark tokens now supply
   the colour, so one rule per screen is enough. ---- */
:root[data-theme="dark"] .auth-brand,
:root[data-theme="dark"] .studio-head,
:root[data-theme="dark"] .welcome-shell,
:root[data-theme="dark"] .page-header.ph-wash {
  background-color: var(--wash-base);
  background-image: var(--wash-sunlit);
}

/* ---- Glass cards/chips/rings floating on a wash surface ---- */
:root[data-theme="dark"] .hero-eyebrow { background: rgba(var(--glass-rgb),0.62); }
:root[data-theme="dark"] .hero-search .search-input { background: var(--surface); }
:root[data-theme="dark"] .btn-hero-ghost { background: rgba(var(--glass-rgb),0.74); }
:root[data-theme="dark"] .btn-hero-ghost:hover { background: rgba(var(--glass-rgb),0.92); }
:root[data-theme="dark"] .hero-art span:nth-child(3) { background: rgba(var(--glass-rgb),0.34); }
:root[data-theme="dark"] .auth-logo .mark { background: var(--surface); }
:root[data-theme="dark"] .auth-rings span:nth-child(3) { background: rgba(var(--glass-rgb),0.32); }
:root[data-theme="dark"] .welcome-signin { background: rgba(var(--glass-rgb),0.92); }
:root[data-theme="dark"] .welcome-signin:hover { background: rgba(var(--glass-rgb),1); }
:root[data-theme="dark"] .welcome-explore:hover { background: rgba(var(--glass-rgb),0.6); }
:root[data-theme="dark"] .welcome-points li { background: rgba(var(--glass-rgb),0.72); }
:root[data-theme="dark"] .welcome-inner::before { background: rgba(var(--glass-rgb),0.55); border-color: rgba(232,236,245,0.10); }
:root[data-theme="dark"] .studio-head .sh-fact { background: rgba(var(--glass-rgb),0.78); }
:root[data-theme="dark"] .studio-head .btn-outline { background: rgba(var(--glass-rgb),0.82); }
:root[data-theme="dark"] .studio-head .btn-outline:hover { background: rgba(var(--glass-rgb),1); }
:root[data-theme="dark"] .page-header.ph-wash .ph-icon { background: var(--surface); }
:root[data-theme="dark"] .page-header.ph-wash .btn-outline { background: rgba(var(--glass-rgb),0.80); }
:root[data-theme="dark"] .page-header.ph-wash .btn-outline:hover { background: rgba(var(--glass-rgb),1); }
:root[data-theme="dark"] .sm-entry { background: rgba(var(--glass-rgb),0.62); }
:root[data-theme="dark"] .sm-card { background: rgba(var(--glass-rgb),0.72); }
:root[data-theme="dark"] .sm-day { background: rgba(var(--glass-rgb),0.72); }
:root[data-theme="dark"] .sm-pref-chip { background: var(--surface); }

/* ---- Small accent "pop" circles authored as a literal white on a coloured
   track, a dark elevated surface plus the bright accent colour reads the
   same way (a lifted, coloured chip) without vanishing into a dark card. ---- */
:root[data-theme="dark"] .role-switch-light button.active,
:root[data-theme="dark"] .demo-card:hover .dc-ico,
:root[data-theme="dark"] .pm-card.selected .pm-icon {
  background: var(--surface);
}
:root[data-theme="dark"] .demo-card:hover .dc-ico,
:root[data-theme="dark"] .pm-card.selected .pm-icon {
  color: var(--accent);
}

/* ---- True dark-mode tone surfaces ----------------------------------------
   Every .tone-X family only ever defined a LIGHT wash (--tone-bg/--tone-grad)
   paired with a DARK ink (--tone-fg/--tone-deep), correct on a light page,
   but it meant every tone-tinted card (.card-primary, .card-insight,
   .kanban-col.attention, the .kpi-rich icon chip) kept that pale card sitting
   inside an otherwise-dark shell, because nothing ever told the tone itself
   to invert. This block is that inversion: a rich, saturated dark surface
   tied to the page's own --surface tokens (never a flat black), a light
   saturated tint of the same hue for text/icons, and a punchier glow, so
   the SAME markup (tone-X class, --tone-bg/--tone-deep/--tone-grad/--tone-glow
   consumers) renders as an intentional dark card instead of a light leftover.
   The text-color patch block right below still reads var(--tone-deep, ...), 
   now that --tone-deep resolves to a light tint here, those patches keep
   working unchanged and automatically become correct on the new dark bg. */
:root[data-theme="dark"] .tone-terracotta { --tone-bg: color-mix(in srgb, #F47A3D 30%, var(--surface) 70%); --tone-fg: color-mix(in srgb, #F47A3D 70%, white 30%); --tone-deep: color-mix(in srgb, #F47A3D 70%, white 30%); --tone-glow: color-mix(in srgb, #F47A3D 42%, transparent); --tone-grad: linear-gradient(135deg, color-mix(in srgb, #F47A3D 26%, var(--surface) 74%) 0%, color-mix(in srgb, #F47A3D 12%, var(--surface-alt) 88%) 100%); }
:root[data-theme="dark"] .tone-gold       { --tone-bg: color-mix(in srgb, #F8C94C 28%, var(--surface) 72%); --tone-fg: color-mix(in srgb, #F8C94C 72%, white 28%); --tone-deep: color-mix(in srgb, #F8C94C 72%, white 28%); --tone-glow: color-mix(in srgb, #F8C94C 40%, transparent); --tone-grad: linear-gradient(135deg, color-mix(in srgb, #F8C94C 24%, var(--surface) 76%) 0%, color-mix(in srgb, #F8C94C 11%, var(--surface-alt) 89%) 100%); }
:root[data-theme="dark"] .tone-aqua       { --tone-bg: color-mix(in srgb, #39AFA3 30%, var(--surface) 70%); --tone-fg: color-mix(in srgb, #39AFA3 68%, white 32%); --tone-deep: color-mix(in srgb, #39AFA3 68%, white 32%); --tone-glow: color-mix(in srgb, #39AFA3 42%, transparent); --tone-grad: linear-gradient(135deg, color-mix(in srgb, #39AFA3 26%, var(--surface) 74%) 0%, color-mix(in srgb, #39AFA3 12%, var(--surface-alt) 88%) 100%); }
:root[data-theme="dark"] .tone-blue       { --tone-bg: color-mix(in srgb, #4B87A6 30%, var(--surface) 70%); --tone-fg: color-mix(in srgb, #4B87A6 66%, white 34%); --tone-deep: color-mix(in srgb, #4B87A6 66%, white 34%); --tone-glow: color-mix(in srgb, #4B87A6 42%, transparent); --tone-grad: linear-gradient(135deg, color-mix(in srgb, #4B87A6 26%, var(--surface) 74%) 0%, color-mix(in srgb, #4B87A6 12%, var(--surface-alt) 88%) 100%); }
:root[data-theme="dark"] .tone-violet     { --tone-bg: color-mix(in srgb, #A472C4 30%, var(--surface) 70%); --tone-fg: color-mix(in srgb, #A472C4 66%, white 34%); --tone-deep: color-mix(in srgb, #A472C4 66%, white 34%); --tone-glow: color-mix(in srgb, #A472C4 42%, transparent); --tone-line: #A472C4; --tone-grad: linear-gradient(135deg, color-mix(in srgb, #A472C4 26%, var(--surface) 74%) 0%, color-mix(in srgb, #A472C4 12%, var(--surface-alt) 88%) 100%); }
:root[data-theme="dark"] .tone-green      { --tone-bg: color-mix(in srgb, #4EA860 30%, var(--surface) 70%); --tone-fg: color-mix(in srgb, #4EA860 66%, white 34%); --tone-deep: color-mix(in srgb, #4EA860 66%, white 34%); --tone-glow: color-mix(in srgb, #4EA860 42%, transparent); --tone-line: #4EA860; --tone-grad: linear-gradient(135deg, color-mix(in srgb, #4EA860 26%, var(--surface) 74%) 0%, color-mix(in srgb, #4EA860 12%, var(--surface-alt) 88%) 100%); }
:root[data-theme="dark"] .tone-magenta    { --tone-bg: color-mix(in srgb, #EF6A5B 30%, var(--surface) 70%); --tone-fg: color-mix(in srgb, #EF6A5B 70%, white 30%); --tone-deep: color-mix(in srgb, #EF6A5B 70%, white 30%); --tone-glow: color-mix(in srgb, #EF6A5B 44%, transparent); --tone-grad: linear-gradient(135deg, color-mix(in srgb, #EF6A5B 26%, var(--surface) 74%) 0%, color-mix(in srgb, #EF6A5B 12%, var(--surface-alt) 88%) 100%); }
:root[data-theme="dark"] .tone-slate      { --tone-bg: color-mix(in srgb, #9A8C7F 26%, var(--surface) 74%); --tone-fg: color-mix(in srgb, #C7BCAF 100%, white 0%); --tone-deep: color-mix(in srgb, #C7BCAF 100%, white 0%); --tone-glow: color-mix(in srgb, #9A8C7F 30%, transparent); --tone-grad: linear-gradient(135deg, color-mix(in srgb, #9A8C7F 20%, var(--surface) 80%) 0%, color-mix(in srgb, #9A8C7F 9%, var(--surface-alt) 91%) 100%); }
:root[data-theme="dark"] .tone-money      { --tone-bg: color-mix(in srgb, #2FA876 30%, var(--surface) 70%); --tone-fg: color-mix(in srgb, #2FA876 66%, white 34%); --tone-deep: color-mix(in srgb, #2FA876 66%, white 34%); --tone-glow: color-mix(in srgb, #2FA876 42%, transparent); --tone-line: #2FA876; --tone-grad: linear-gradient(135deg, color-mix(in srgb, #2FA876 26%, var(--surface) 74%) 0%, color-mix(in srgb, #2FA876 12%, var(--surface-alt) 88%) 100%); }
:root[data-theme="dark"] .tone-volume     { --tone-bg: color-mix(in srgb, #4B87A6 30%, var(--surface) 70%); --tone-fg: color-mix(in srgb, #4B87A6 66%, white 34%); --tone-deep: color-mix(in srgb, #4B87A6 66%, white 34%); --tone-glow: color-mix(in srgb, #4B87A6 42%, transparent); --tone-grad: linear-gradient(135deg, color-mix(in srgb, #4B87A6 26%, var(--surface) 74%) 0%, color-mix(in srgb, #4B87A6 12%, var(--surface-alt) 88%) 100%); }
:root[data-theme="dark"] .tone-growth     { --tone-bg: color-mix(in srgb, #A472C4 30%, var(--surface) 70%); --tone-fg: color-mix(in srgb, #A472C4 66%, white 34%); --tone-deep: color-mix(in srgb, #A472C4 66%, white 34%); --tone-glow: color-mix(in srgb, #A472C4 42%, transparent); --tone-line: #A472C4; --tone-grad: linear-gradient(135deg, color-mix(in srgb, #A472C4 26%, var(--surface) 74%) 0%, color-mix(in srgb, #A472C4 12%, var(--surface-alt) 88%) 100%); }
:root[data-theme="dark"] .tone-caution    { --tone-bg: color-mix(in srgb, #E2A73E 28%, var(--surface) 72%); --tone-fg: color-mix(in srgb, #E2A73E 72%, white 28%); --tone-deep: color-mix(in srgb, #E2A73E 72%, white 28%); --tone-glow: color-mix(in srgb, #E2A73E 40%, transparent); --tone-line: #E2A73E; --tone-grad: linear-gradient(135deg, color-mix(in srgb, #E2A73E 24%, var(--surface) 76%) 0%, color-mix(in srgb, #E2A73E 11%, var(--surface-alt) 89%) 100%); }
:root[data-theme="dark"] .tone-failure    { --tone-bg: color-mix(in srgb, #E14B3B 30%, var(--surface) 70%); --tone-fg: color-mix(in srgb, #E14B3B 70%, white 30%); --tone-deep: color-mix(in srgb, #E14B3B 70%, white 30%); --tone-glow: color-mix(in srgb, #E14B3B 44%, transparent); --tone-line: #E14B3B; --tone-grad: linear-gradient(135deg, color-mix(in srgb, #E14B3B 26%, var(--surface) 74%) 0%, color-mix(in srgb, #E14B3B 12%, var(--surface-alt) 88%) 100%); }
:root[data-theme="dark"] .tone-capacity   { --tone-bg: color-mix(in srgb, #2C978C 30%, var(--surface) 70%); --tone-fg: color-mix(in srgb, #2C978C 66%, white 34%); --tone-deep: color-mix(in srgb, #2C978C 66%, white 34%); --tone-glow: color-mix(in srgb, #2C978C 42%, transparent); --tone-line: #2C978C; --tone-grad: linear-gradient(135deg, color-mix(in srgb, #2C978C 26%, var(--surface) 74%) 0%, color-mix(in srgb, #2C978C 12%, var(--surface-alt) 88%) 100%); }

/* --av-tint is the plate colour behind a practitioner card's avatar
   (.photo-band), six pastel gradients, one per --av-g family, with no dark
   variant. On a dark card these render exactly as the screenshot showed: a
   pale mint/lavender/peach rectangle sitting inside an otherwise-dark grid.
   Re-derived per family from the SAME --av-grad hues the avatar circle
   itself already uses, mixed into the dark surface instead of white, so the
   band still visibly differs by person while reading as an intentional dark
   plate rather than a leftover light one. */
:root[data-theme="dark"] .av-g1 { --av-tint: linear-gradient(135deg, color-mix(in srgb, #4A73C3 30%, var(--surface) 70%) 0%, color-mix(in srgb, #7A5BEC 30%, var(--surface-alt) 70%) 100%); }
:root[data-theme="dark"] .av-g2 { --av-tint: linear-gradient(135deg, color-mix(in srgb, #9157D6 30%, var(--surface) 70%) 0%, color-mix(in srgb, #CB3990 30%, var(--surface-alt) 70%) 100%); }
:root[data-theme="dark"] .av-g3 { --av-tint: linear-gradient(135deg, color-mix(in srgb, #0E8376 32%, var(--surface) 68%) 0%, color-mix(in srgb, #127F9B 32%, var(--surface-alt) 68%) 100%); }
:root[data-theme="dark"] .av-g4 { --av-tint: linear-gradient(135deg, color-mix(in srgb, #B85A25 30%, var(--surface) 70%) 0%, color-mix(in srgb, #C34D5B 30%, var(--surface-alt) 70%) 100%); }
:root[data-theme="dark"] .av-g5 { --av-tint: linear-gradient(135deg, color-mix(in srgb, #0E7D48 32%, var(--surface) 68%) 0%, color-mix(in srgb, #0C7A6F 32%, var(--surface-alt) 68%) 100%); }
:root[data-theme="dark"] .av-g6 { --av-tint: linear-gradient(135deg, color-mix(in srgb, #5A5BD6 30%, var(--surface) 70%) 0%, color-mix(in srgb, #2C73D9 30%, var(--surface-alt) 70%) 100%); }
:root[data-theme="dark"] .practitioner-card .photo-band .avatar { border-color: var(--surface); }

/* .alert-card.warn/.info (the Command Centre "PAYMENTS"/"UNPAID"/"WAITLIST"
   strip) were two flat hardcoded light hexes with no theme awareness at all, 
   the exact "left in light mode" pattern this pass targets, plus an icon
   chip and CTA button both hardcoded to translucent WHITE, correct only when
   the card itself is light. Rebuilt on the same colour-mix-with-surface
   recipe as the tone system above, and the chip/button flipped to a
   translucent LIGHT wash so they still read as "lifted" off a now-dark card. */
:root[data-theme="dark"] .alert-card.warn { border-color: color-mix(in srgb, #FBBF24 32%, transparent); background: color-mix(in srgb, #FBBF24 15%, var(--surface) 85%); color: color-mix(in srgb, #FBBF24 74%, white 26%); }
:root[data-theme="dark"] .alert-card.info { border-color: color-mix(in srgb, #5EB3E0 32%, transparent); background: color-mix(in srgb, #5EB3E0 15%, var(--surface) 85%); color: color-mix(in srgb, #5EB3E0 74%, white 26%); }
:root[data-theme="dark"] .alert-card > .ac-ico { background: rgba(255,255,255,0.10); box-shadow: inset 0 0 0 1px rgba(255,255,255,0.10); }
:root[data-theme="dark"] .alert-card .ac-cta .btn { background: rgba(255,255,255,0.10); border-color: rgba(255,255,255,0.18); color: inherit; }
:root[data-theme="dark"] .alert-card .ac-cta .btn:hover { background: rgba(255,255,255,0.18); }

/* ---- Text sitting on a background that is ALWAYS light, tone-tinted or
   pastel, regardless of theme (group 2 above). Left alone, this text
   inherits the page's now-light --text-primary/--text-secondary and
   disappears against its own card. Each gets an explicit dark-safe colour
   instead, --tone-fg/--tone-deep when a specific tone is in play (they are
   always a dark, saturated ink, unaffected by theme by design), a fixed dark
   warm-neutral otherwise. ---- */
:root[data-theme="dark"] .next-up { color: var(--tone-fg, #2A2D35); }
:root[data-theme="dark"] .next-up .muted { color: var(--tone-fg, #2A2D35); opacity: 0.75; }
:root[data-theme="dark"] .cp-value { color: var(--tone-deep, #2A2D35); }
:root[data-theme="dark"] .card-action:hover .ca-title,
:root[data-theme="dark"] .card-action:hover .ca-sub { color: var(--tone-deep, #2A2D35); }
:root[data-theme="dark"] .choice-card.selected .cc-label { color: var(--tone-deep, #2A2D35); }
:root[data-theme="dark"] .path-card.selected .pc-body strong,
:root[data-theme="dark"] .path-card.selected .pc-body span { color: var(--tone-deep, #2A2D35); }
:root[data-theme="dark"] .qs-card:hover strong,
:root[data-theme="dark"] .qs-card:hover span { color: var(--tone-deep, #2A2D35); }

/* .card-primary carries a tone-tinted background at all times; setting the
   colour at the container catches every unstyled descendant (bare <b> tags
   used for the small sub-stat numbers, e.g. the BOOKINGS card's 107/46/14)
   without having to chase each one individually. More specific rules like
   .cp-label/.cp-sub/.cp-value already set their own dark-safe colour above
   and win over this by specificity, so they're unaffected. */
:root[data-theme="dark"] .card-primary { color: var(--tone-deep, #2A2D35); }
:root[data-theme="dark"] .cp-stat-row b { color: var(--tone-deep, #2A2D35); }

/* Gauge/progress-row label + value pairs (e.g. "Attendance rate 90%") need a
   dark-safe colour ONLY when the gauge sits inside a tone-tinted
   always-light container (.card-primary/.card-command), a gauge inside a
   plain .card is already correct, since that card's own background is the
   normal (now dark) surface and the text should just inherit the page's
   light text tokens as-is. */
:root[data-theme="dark"] .card-primary .gh-name,
:root[data-theme="dark"] .card-primary .gh-val,
:root[data-theme="dark"] .card-command .gh-name,
:root[data-theme="dark"] .card-command .gh-val { color: var(--tone-deep, #2A2D35); }

/* .card-insight (e.g. "Room to fill") sits on the same always-light
   tone-tinted background as .card-primary/.card-command siblings. */
:root[data-theme="dark"] .card-insight,
:root[data-theme="dark"] .card-insight .ci-text { color: var(--tone-deep, #2A2D35); }

/* The "Needs attention" kanban column keeps its own always-light tone-bg
   fill (unlike its peer columns, which just carry a coloured top border on
   the normal dark surface), its heading and hint text need the same
   dark-safe ink as the rest of the tone-tinted family. */
:root[data-theme="dark"] .kanban-col.attention h4,
:root[data-theme="dark"] .kanban-col.attention .kb-hint { color: var(--tone-deep, #2A2D35); }

/* .kpi-label was using --tone-deep (a dark ink meant for pairing with a
   light tone-tinted background) directly as the label text colour on these
   small KPI tiles, but the tile itself sits on the ordinary (now dark)
   --surface, not a tone-bg pastel, so the category identity should live in
   the icon chip and border accent only, and the label text should follow
   the page's normal secondary text colour like everywhere else. */
:root[data-theme="dark"] .kpi-label { color: var(--text-secondary); }

/* The "COLLECTED REVENUE" / "UP NEXT" signature dark surface was a warm
   espresso brown, hardcoded independent of theme. In dark mode the rest of
   the product is already a cool slate, so this card should recede into that
   same family rather than standing out as an orphaned brown card. */
:root[data-theme="dark"] {
  --espresso: linear-gradient(142deg, #2A2E38 0%, #1E212A 54%, #262A34 100%);
}
:root[data-theme="dark"] .card-command { border-color: rgba(232,236,245,0.10); }

/* ---- Theme toggle button: sun in dark mode, moon in light, handled by
   App.initTheme/toggleTheme picking the icon; nothing further needed here. ---- */

/* ===========================================================================
   16. TOASTS + SIDE PANELS, premium pass
   The toast used --text-primary as its own background, which is dark-brown
   in light mode (a fine "ink chip" look) but flips to near-white the moment
   --text-primary re-themes for dark mode, while its text stayed literally
   #fff, so the notification became pale text on a pale chip. Rebuilt as an
   explicit dark glass card in both themes (a toast is a system notification,
   not page content, it earns its own fixed surface rather than tracking
   the page's light/dark state), with a coloured left rail + icon chip per
   type standing in for the old flat-recolour approach, and a touch more
   presence (border, deeper shadow) so it reads as a floating system surface
   rather than a plain rectangle.
   ========================================================================== */
.toast {
  position: relative; overflow: hidden;
  display: flex; align-items: center; gap: 11px; background: #22262F; color: #F2F4F8;
  padding: 12px 14px 12px 16px; border-radius: var(--radius-md); font-size: 13px;
  box-shadow: 0 18px 40px -12px rgba(10,12,16,0.55), 0 1px 0 rgba(255,255,255,0.04) inset;
  border: 1px solid rgba(255,255,255,0.08);
  max-width: 400px; animation: toastIn var(--dur-base) var(--ease-glide);
}
.toast::before {
  content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 3px; background: #8D93A2;
}
.toast .icon { flex-shrink: 0; }
.toast .toast-msg { flex: 1; line-height: 1.4; }
.toast.success::before { background: #4ADE80; } .toast.success .icon { color: #4ADE80; }
.toast.error { background: #2E1B1B; } .toast.error::before { background: #F0755E; } .toast.error .icon { color: #F0947F; }
.toast.warning { background: #2E2712; } .toast.warning::before { background: #E6B24E; } .toast.warning .icon { color: #E6B24E; }
.toast.info::before { background: #7FB8D6; } .toast.info .icon { color: #7FB8D6; }
.toast-action {
  background: rgba(255,255,255,0.10); border: 1px solid rgba(255,255,255,0.12); color: #fff; font-weight: 700; font-size: 12px;
  padding: 5px 10px; border-radius: var(--radius-sm); flex-shrink: 0;
}
.toast-action:hover { background: rgba(255,255,255,0.20); }
.toast-close { background: none; border: none; color: rgba(255,255,255,0.55); display: flex; padding: 2px; flex-shrink: 0; }
.toast-close:hover { color: #fff; }

/* Side panels (the right-edge detail drawers, booking detail, offering
   editor, etc.) were a plain flush rectangle. A rounded leading edge plus a
   slightly elevated header gives the drawer a sense of being a distinct
   surface floated over the page rather than a clipped-off slice of it. */
.side-panel-overlay {
  background: rgba(10,12,16,0.5); backdrop-filter: blur(2px);
  align-items: stretch; padding: 14px;
}
.side-panel {
  border-radius: 20px; overflow: hidden; border: 1px solid var(--border);
  box-shadow: -28px 0 56px -24px rgba(10,12,16,0.35), var(--shadow-pop);
}
@media (max-width: 640px) {
  .side-panel-overlay { padding: 0; }
  .side-panel { border-radius: 0; border: none; }
}
.side-panel .dlg-head {
  padding: 18px 22px; background: var(--surface-alt);
  border-bottom: 1px solid var(--border);
}
.side-panel .dlg-title { font-size: 16px; letter-spacing: -0.01em; }
.side-panel .dlg-body { padding: 20px 22px 22px; }
.side-panel .panel-section { border-bottom-color: var(--border); }
:root[data-theme="dark"] .side-panel-overlay { background: rgba(4,5,7,0.62); }

/* ===========================================================================
   17. MONEY INPUT
   Every price/amount field in the product was a bare <input type="number">
   with "(KES)" tacked onto the label, functional, but it reads as a generic
   quantity box rather than a currency field. One component, used everywhere
   money is entered through the generic form system: a fixed currency prefix
   inside the same bordered field, so the unit is never separated from the
   value the way a suffix-label is.
   ========================================================================== */
.money-input {
  display: flex; align-items: stretch; border-radius: var(--radius-sm);
  border: 1px solid var(--border-strong); background: var(--surface); overflow: hidden;
}
.money-input:focus-within { outline: 2px solid var(--accent); outline-offset: 1px; border-color: var(--accent); }
.money-input-prefix {
  display: flex; align-items: center; padding: 0 10px; font-size: 12.5px; font-weight: 700;
  letter-spacing: .02em; color: var(--text-secondary); background: var(--surface-alt);
  border-right: 1px solid var(--border-strong); flex-shrink: 0;
}
.money-input input {
  border: none; border-radius: 0; background: transparent; padding: 9px 11px; flex: 1; min-width: 0;
  color: var(--text-primary); font-variant-numeric: tabular-nums; font-weight: 600;
}
.money-input input:focus { outline: none; }
/* The field-level focus/border rules in styles.css target `.field input`
   directly; scope them off when the input lives inside .money-input's own
   bordered group so the two focus rings don't stack. */
.field .money-input input:focus { outline: none; border: none; }

/* ===========================================================================
   18. THEME STUDIO (Admin-only)
   The preview column intentionally sets its own CSS custom properties on
   .theme-preview-frame inline (see themePreviewVars in views.js) rather than
   touching :root, so an Admin can try out a draft colour without it ever
   leaking onto the surrounding Theme Studio chrome, let alone the rest of
   the app. Only Engine.publishEventTheme (via applyEventTheme in core.js)
   is allowed to change the real :root tokens.
   ========================================================================== */
.theme-studio-layout { display: grid; grid-template-columns: 1fr 380px; gap: 20px; align-items: start; }
@media (max-width: 1000px) { .theme-studio-layout { grid-template-columns: 1fr; } }
/* Grid items default to min-width:auto, sized to fit their content's own
   minimum, not the track. With a narrow track (a collapsed single column on
   a phone) that let this column's content dictate a minimum wider than the
   viewport instead of the other way around. */
.theme-studio-main, .theme-studio-preview { min-width: 0; }
.theme-preset-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(130px, 1fr)); gap: 10px; }
.theme-preset-card {
  display: flex; flex-direction: column; align-items: center; gap: 8px; padding: 14px 10px;
  border-radius: var(--radius-md); border: 1.5px solid var(--border); background: var(--surface);
  font-size: 12.5px; font-weight: 700; color: var(--text-primary); text-align: center;
}
.theme-preset-card:hover { border-color: var(--border-strong); }
.theme-preset-card.selected { border-color: var(--accent); box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 18%, transparent); }
.theme-preset-card:disabled { opacity: .55; cursor: not-allowed; }
.tp-swatch { width: 36px; height: 36px; border-radius: 50%; box-shadow: inset 0 0 0 1px rgba(0,0,0,0.08); }
.tp-swatch-custom { display: flex; align-items: center; justify-content: center; background: var(--surface-alt); color: var(--text-muted); }
.swatch-btn { width: 30px; height: 30px; border-radius: 50%; border: 2px solid transparent; box-shadow: inset 0 0 0 1px rgba(0,0,0,0.08); flex-shrink: 0; }
.swatch-btn.selected { border-color: var(--text-primary); }
.swatch-btn:disabled { opacity: .5; }
.swatch-dot { width: 10px; height: 10px; border-radius: 50%; flex-shrink: 0; margin-top: 4px; box-shadow: inset 0 0 0 1px rgba(0,0,0,0.15); }
.theme-history-list { display: flex; flex-direction: column; gap: 12px; max-height: 260px; overflow-y: auto; }
.theme-history-row { display: flex; gap: 10px; align-items: flex-start; font-size: 13px; }
.theme-preview-card { position: sticky; top: 16px; }
.theme-preview-frame {
  background: var(--page-bg); border-radius: var(--radius-lg); border: 1px solid var(--border);
  padding: 20px; display: flex; align-items: center; justify-content: center; min-height: 220px;
}

/* ---- Premium colour picker (Theme Studio accent / secondary accent) ---- */
.cp-field { margin-bottom: 14px; }
.cp-field:last-child { margin-bottom: 0; }
.cp-field > label { display: block; font-size: 12.5px; font-weight: 700; color: var(--text-muted); margin-bottom: 6px; }
.cp-control { display: flex; align-items: center; gap: 10px; }
.cp-swatch-btn {
  width: 38px; height: 38px; border-radius: 10px; border: 1.5px solid var(--border);
  box-shadow: inset 0 0 0 1px rgba(0,0,0,0.06); flex-shrink: 0; cursor: pointer;
}
.cp-swatch-btn:hover:not(:disabled) { border-color: var(--border-strong); transform: translateY(-1px); }
.cp-swatch-btn:disabled { opacity: .5; cursor: not-allowed; }
.cp-hex-input { flex: 1 1 auto; min-width: 0; }
.cp-panel {
  margin-top: 12px; padding: 14px; border-radius: var(--radius-md);
  border: 1px solid var(--border); background: var(--surface-alt);
  animation: valueTick var(--dur-fast) var(--ease-standard);
}
.cp-sv {
  position: relative; width: 100%; height: 150px; border-radius: var(--radius-sm);
  cursor: crosshair; touch-action: none; overflow: hidden;
  background:
    linear-gradient(to top, #000 0%, transparent 100%),
    linear-gradient(to right, #fff 0%, transparent 100%),
    hsl(var(--cp-hue, 24deg) 100% 50%);
}
.cp-sv-thumb {
  position: absolute; top: 0; left: 100%; width: 16px; height: 16px; margin: -8px 0 0 -8px;
  border-radius: 50%; border: 2px solid #fff; box-shadow: 0 0 0 1px rgba(0,0,0,0.35), 0 2px 6px rgba(0,0,0,0.35);
  pointer-events: none;
}
.cp-hue-track {
  position: relative; width: 100%; height: 14px; margin-top: 12px; border-radius: 999px;
  cursor: pointer; touch-action: none;
  background: linear-gradient(to right, #f00, #ff0, #0f0, #0ff, #00f, #f0f, #f00);
}
.cp-hue-thumb {
  position: absolute; top: 50%; left: 0; width: 18px; height: 18px; margin: -9px 0 0 -9px;
  border-radius: 50%; border: 2px solid #fff; background: transparent;
  box-shadow: 0 0 0 1px rgba(0,0,0,0.35), 0 2px 6px rgba(0,0,0,0.35); pointer-events: none;
}
.cp-rgb-row { margin-top: 10px; font-size: 11.5px; font-weight: 600; color: var(--text-muted); letter-spacing: .02em; }
@media (max-width: 480px) {
  .cp-sv { height: 130px; }
}

/* ==========================================================================
   SECTION 19: PLATFORM LAYER (landing, showcase, organizer onboarding)
   The platform gate sits in front of the existing app. It borrows the
   welcome-shell / onboard-shell visual language already established for
   auth so it feels native, then adds a small set of its own classes for
   the things those screens don't have: the three-action landing menu, the
   showcase role grid, the persistent showcase banner, and the Setup Home
   roadmap + module toggles used while an organizer configures a new event.
   ========================================================================== */
.platform-shell {
  min-height: 100dvh; display: flex; align-items: center; justify-content: center;
  position: relative; overflow: hidden; padding: 32px 20px;
  background: var(--wash-bg, linear-gradient(160deg, #FFF8EE 0%, #FDEFE0 46%, #FCE6D6 100%));
}
.platform-art { position: absolute; inset: 0; pointer-events: none; }
.platform-inner {
  position: relative; z-index: 1; max-width: 460px; width: 100%; text-align: center;
  background: rgba(255,255,255,0.7); border: 1px solid rgba(255,255,255,0.6);
  border-radius: 24px; padding: 40px 32px; box-shadow: 0 30px 70px -30px rgba(60,40,20,0.28);
  backdrop-filter: blur(18px);
}
.platform-actions { display: flex; flex-direction: column; gap: 10px; margin-top: 8px; }
.platform-actions .btn { flex-direction: column; gap: 2px; padding-top: 12px; padding-bottom: 12px; height: auto; min-height: 52px; }
.platform-actions .btn > span:first-child:not(.pf-sub) { display: inline-flex; align-items: center; gap: 8px; }
.pf-sub { display: block; font-size: 11.5px; font-weight: 500; opacity: 0.72; letter-spacing: 0; text-transform: none; }
.platform-signin-link { margin-top: 6px; font-size: 13px; }
@media (max-width: 480px) { .platform-inner { padding: 30px 22px; border-radius: 20px; } }

.showcase-role-grid { display: flex; flex-direction: column; gap: 10px; margin-top: 22px; text-align: left; }

.showcase-banner {
  display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap;
  padding: 10px 20px; background: linear-gradient(90deg, color-mix(in srgb, var(--accent) 14%, var(--surface)) 0%, var(--surface) 100%);
  border-bottom: 1px solid var(--border); font-size: 13px; font-weight: 600; color: var(--text-primary);
}
.showcase-banner strong { font-weight: 800; }
.showcase-banner .icon { color: var(--accent); vertical-align: -2px; }
.showcase-banner-actions { display: flex; align-items: center; gap: 8px; flex-shrink: 0; }
/* Bare .btn.btn-sm has no background/colour of its own, it relies on a
   parent to set both. Without this the button falls back to the browser's
   default button face (near-white) while inheriting the banner's own text
   colour, which is white in dark mode: white text on a near-white button,
   functionally invisible. Both actions here get an explicit surface. */
.showcase-banner .btn {
  background: var(--surface); color: var(--text-primary); border: 1.5px solid var(--border-strong);
  min-height: 32px; padding: 6px 14px; font-size: 12.5px; font-weight: 700;
}
.showcase-banner .btn:hover { background: var(--surface-alt); border-color: var(--accent); }
.showcase-banner-close {
  width: 28px; height: 28px; border-radius: 50%; display: flex; align-items: center; justify-content: center;
  background: transparent; border: none; color: var(--text-muted); flex-shrink: 0;
}
.showcase-banner-close:hover { background: var(--surface-alt); color: var(--text-primary); }

.setup-home-shell .onboard-card { max-width: 640px; }
.setup-roadmap { display: flex; flex-direction: column; gap: 8px; margin-top: 18px; }
.setup-card {
  display: flex; align-items: center; gap: 12px; width: 100%; text-align: left;
  padding: 13px 14px; border-radius: var(--radius-md); border: 1.5px solid var(--border);
  background: var(--surface); font-size: 13.5px; font-weight: 700; color: var(--text-primary);
  transition: border-color var(--dur-base) var(--ease-standard), transform var(--dur-base) var(--ease-standard);
}
.setup-card:not(:disabled):hover { border-color: var(--border-strong); transform: translateY(-1px); }
.setup-card:disabled { cursor: default; }
.setup-card .icon:last-child { margin-left: auto; color: var(--text-muted); flex-shrink: 0; }
.setup-card.done { border-color: color-mix(in srgb, var(--accent-gold, var(--accent)) 55%, var(--border)); }
.setup-card.done .sc-check { background: var(--accent-gold, var(--accent)); color: #fff; }
.setup-card.upcoming {
  background: transparent; border-style: dashed; color: var(--text-secondary); font-weight: 600;
  cursor: default; box-shadow: none;
}
.setup-card.upcoming .sc-check { background: var(--surface-alt); }
.setup-card.upcoming .small.muted { margin-left: auto; flex-shrink: 0; }
.sc-check {
  width: 22px; height: 22px; border-radius: 50%; flex-shrink: 0; display: flex; align-items: center;
  justify-content: center; background: var(--surface-alt); color: var(--text-muted);
}

.module-toggle-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 8px; }
.module-toggle {
  display: grid; grid-template-columns: auto auto 1fr auto; align-items: center; gap: 6px 8px;
  padding: 10px 12px; border-radius: var(--radius-md);
  border: 1.5px solid var(--border); background: var(--surface); font-size: 12.5px; font-weight: 700;
  color: var(--text-primary); cursor: pointer;
}
.module-toggle:has(input:checked) { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 8%, var(--surface)); }
.module-toggle input { flex-shrink: 0; }
.module-toggle .icon { flex-shrink: 0; color: var(--text-muted); }
.module-toggle > span:not(.small) { min-width: 0; line-height: 1.3; }
.module-toggle.locked { opacity: 0.7; cursor: default; }
.module-toggle .small.muted { flex-shrink: 0; white-space: nowrap; font-weight: 600; text-align: right; }

:root[data-theme="dark"] .platform-shell { background: linear-gradient(160deg, #1B1D24 0%, #17181E 55%, #121317 100%); }
:root[data-theme="dark"] .platform-inner { background: rgba(28,30,37,0.72); border-color: rgba(232,236,245,0.10); }
:root[data-theme="dark"] .showcase-banner { border-color: rgba(232,236,245,0.10); }
:root[data-theme="dark"] .setup-card.upcoming { color: var(--text-secondary); }

