@charset "UTF-8";
/* Firewalker - global stylesheet. HAND-MAINTAINED: edit this file directly.
   Never regenerate it from wwwroot/scss (those sources are stale legacy except
   _tokens.scss) — see .claude/rules/scss.md. */
/* Self-hosted Inter font */
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/fonts/inter/Inter-Regular.woff2") format("woff2");
}
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("/fonts/inter/Inter-Medium.woff2") format("woff2");
}
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("/fonts/inter/Inter-SemiBold.woff2") format("woff2");
}
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("/fonts/inter/Inter-Bold.woff2") format("woff2");
}
/* Self-hosted Outfit (variable, latin subset) — the display face on EVERY
   surface per "Firewalker Type Decision v1.0": same geometric construction as
   the wordmark. Usage caps at weight 600 (700+ goes bulbous). */
@font-face {
  font-family: "Outfit";
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url("/fonts/outfit/Outfit-Latin-Variable.woff2") format("woff2");
}
/* Self-hosted JetBrains Mono (variable, latin subset) — the data face
   (--fw-font-data): code, ids, raw values. Forward-provisioned by the Type
   Decision v1.0: no selector consumes --fw-font-data yet, and that is free —
   browsers only fetch a @font-face on first use. Do not preload this file. */
@font-face {
  font-family: "JetBrains Mono";
  font-style: normal;
  font-weight: 100 800;
  font-display: swap;
  src: url("/fonts/jetbrains-mono/JetBrainsMono-Latin-Variable.woff2") format("woff2");
}
/* ============================================
   CSS VARIABLES (from tokens)
   ============================================ */
:root {
  /* Colors — "Firewalker Color System" v1.1 (claude.ai/design, APPROVED): one
     brand colour and it is a gradient, the ramp #FF6B2C → #F08A2F → #E0A030.
     --color-primary is the ramp BASE: gradient starts, outlines and text
     accents only — never a solid area. -light/-dark are hover/pressed steps. */
  --color-primary: #ff6b2c;
  --color-primary-light: #ff8551;
  --color-primary-dark: #e05a20;
  /* Primary family steps (status-table shape): text is the lightened cut for
     small warm text, deep is the light-theme/print substitute. */
  --color-primary-text: #ff8a54;
  --color-primary-deep: #9a3409;
  /* Ember mid — the ramp's own midpoint and the WORKHORSE: every flat control,
     link and small fill reads this (the travel rule: a gradient only carries
     ≥140px of travel; below that the object takes the midpoint flat). */
  --color-ember-mid: #f08a2f;
  --color-ember-mid-light: #f59d47;
  --color-ember-mid-dark: #d97a1f;
  /* Secondary is the ramp TIP — Gold. Earned, accumulated, complete: never on
     a control that invites, only on one that confirms. */
  --color-secondary: #e0a030;
  --color-secondary-light: #f2c46a;
  --color-secondary-dark: #d08f22;
  --color-accent: #ff4d4d;
  /* Text/icon color on primary-filled surfaces — dark ink (6.5:1+ on any
     point of the warm ramp in both themes) */
  --color-on-primary: #1a120d;
  /* The primary-CTA ramp (approved: 100deg, base → #E8A83E). Carried only by
     the .btn--primary.btn--lg tier and hero CTAs — travel ≥140px; every other
     filled control is flat --color-ember-mid. Two ramp fills per screen max. */
  --gradient-primary: linear-gradient(100deg, #ff6b2c 0%, #e8a83e 100%);
  /* Progress fills run the FULL ramp at 90deg — hotter reads further along. */
  --gradient-progress: linear-gradient(90deg, #ff6b2c 0%, #e0a030 100%);
  /* The same ramp unbundled into stops that position-mapped indicators (the
     Stepper) can interpolate at i/(n−1). ramp-* is the connector band — a 2px
     stroke may carry the full base→tip run; fill-* is the solid-area band and
     stays mid→tip (§03: base is never a solid area). */
  --progress-ramp-start: #ff6b2c;
  --progress-ramp-end: #e0a030;
  --progress-fill-start: #f08a2f;
  --progress-fill-end: #e0a030;
  /* Accent used AS FOREGROUND on the ground (selected nav icon, dock item).
     Flat foreground = the midpoint, per the travel rule. */
  --color-accent-ink: var(--color-ember-mid);
  /* Dating-section override palette (Turn 19: rose replaces ember inside the
     Dating section only — see .app--dating below). -light/-dark derived. */
  --color-rose: #e2607e;
  --color-rose-light: #ea7d97;
  --color-rose-dark: #c94e6c;
  --color-on-rose: #2a0c14;
  /* Completion semantic — reserved for the check control, nothing else.
     Aligned to the approved Success base (settled jade, not celebratory). */
  --color-positive: #4fa97a;
  --color-on-positive: #0a2418;
  /* Brand lockup gold (mark + wordmark) — invariant across brand styles,
     flipped to the deep gold on light backgrounds for legibility */
  --brand-gold: #e0a030;
  /* Lockup fills, split so the wordmark's arm (the swoosh over the F) can
     carry a second value independently of the letters — see the LOGO LOCKUP
     EXPERIMENTS block below.
     Default is the APPROVED ramp lockup ("Firewalker Color System" §05):
     the mark ramps base→tip along the flame (bottom ember, top gold), the
     letters are Bone, and the arm carries the same ramp along its own length.
     The vars feed `background:` under the mask layers, so a gradient is a
     legal value (the retired legacy lockup already relied on this). The arm's stops
     sit at 3%/10% because the arm occupies only the first ~43/770 of the
     wordmark box (59deg ≈ the swoosh's own diagonal) — the mask hides the
     rest. Champagne (14x/15x) variants are retired; still selectable under
     the switcher's Deprecated group. */
  --brand-mark-color: linear-gradient(0deg, #ff6b2c 0%, #e0a030 100%);
  --brand-word-letters: #f2ece4;
  --brand-word-arm: linear-gradient(59deg, #ff6b2c 3%, #e0a030 10%);
  /* Background colors */
  --bg-dark: #0d0d0d;
  --bg-dark-secondary: #1a1a1a;
  --bg-dark-tertiary: #262626;
  --bg-dark-card: #1f1f1f;
  --bg-dark-elevated: #2d2d2d;
  /* Background hover states (slightly lighter in dark mode) */
  --bg-card-hover: #242424;
  --bg-elevated-hover: #383838;
  --bg-input-hover: #363636;
  /* Text colors */
  --text-primary: #f2ece4; /* Bone — approved brand text on dark (16.6:1) */
  --text-secondary: rgba(255, 255, 255, 0.7);
  --text-tertiary: rgba(255, 255, 255, 0.5);
  --text-muted: rgba(255, 255, 255, 0.38);
  /* Status colors — approved five-family system, each with five steps:
     surface 10% / border 32% / base / text / deep. Surface+border (and the
     pill 15% / icon-chip 18% / ring 45% alphas) compose at use-sites via
     color-mix on the base; text and deep are literal steps below. Text is the
     lightened cut for small copy (base at 0.875rem is legible but tiring);
     deep is the light-theme and print substitute. */
  --color-success: #4fa97a;
  --color-success-text: #7fd3a5;
  --color-success-deep: #1e7a4c;
  --color-warning: #f97316;
  --color-warning-text: #fba05e;
  --color-warning-deep: #b4530a;
  --color-error: #ff6369;
  --color-error-text: #ff9095;
  --color-error-deep: #b42318;
  --color-info: #7fa8d9;
  --color-info-text: #a9c6e8;
  --color-info-deep: #215bae;
  --color-neutral: #8a8a8a;
  --color-neutral-text: #c9c4bd;
  --color-neutral-deep: #3a3a3a;
  /* Area accents (bottom-dock active tint). Gold = the ramp tip. */
  --color-gold: #e0a030;
  --color-pink: #ec407a;
  /* Legacy SCSS palette, aliased from _tokens.scss ($color-purple etc.) so the
     hex lives only here — same single-source pattern as the tokens above. */
  --color-purple: #ab47bc;
  --color-teal: #26a69a;
  --color-indigo: #5c6bc0;
  --color-lime: #9ccc65;
  /* Chart chrome + categorical series (approved order: slate leads so a
     multi-series chart never opens on the brand; the warm slot sits last).
     Series 5 doubles as error, series 2 as success. */
  --chart-grid-line: rgba(255, 255, 255, 0.1);
  --chart-cat-1: #7fa8d9;
  --chart-cat-2: #4fa97a;
  --chart-cat-3: #c48bd9;
  --chart-cat-4: #6fc8c8;
  --chart-cat-5: #ff6369;
  --chart-cat-6: #f08a2f;
  /* Habit/metric user palette — one slot per SemanticColorId the picker
     offers (ids are persisted per trackable; renderers resolve them via
     getColorPreview in ClientApp/components/color-picker.tsx). Seeded with
     the pre-token literals so existing habits keep their exact colors;
     five slots still carry deprecated Material hues (blue, green, red,
     amber, info) pending a design pass — retune them here, per theme,
     without touching components. */
  --habit-color-orange: var(--color-primary); /* the brand slot — follows the active style */
  --habit-color-blue: #2196f3;
  --habit-color-green: #4caf50;
  --habit-color-purple: #ab47bc;
  --habit-color-red: #f44336;
  --habit-color-amber: #ff9800;
  --habit-color-cyan: #00bcd4;
  --habit-color-pink: #ec407a;
  --habit-color-info: #29b6f6;
  --habit-color-teal: #26a69a;
  --habit-color-indigo: #5c6bc0;
  --habit-color-lime: #9ccc65;
  /* Typography — "Firewalker Type Decision v1.0": Outfit for display (the
     ≥20px tiers plus the 11px eyebrow, its one exception), Inter for
     everything else. The boundary is hard at 20px. Weight caps at 600 on
     Outfit. tabular-nums is MANDATORY on every Outfit numeral — Outfit's
     proportional figures make "1111" 61% narrower than tabular, so a counter
     ticking 11→12 visibly jumps; the setting lives here on the token, not on
     components. */
  --fw-font-display: "Outfit", "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  --fw-font-text: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  --fw-font-data: "JetBrains Mono", ui-monospace, "Cascadia Mono", Menlo, monospace;
  /* display steps — Outfit 600, negative tracking */
  --fw-display-1: 600 62px/1 var(--fw-font-display);
  --fw-display-2: 600 44px/1.08 var(--fw-font-display);
  --fw-title: 600 32px/1.15 var(--fw-font-display);
  --fw-heading: 600 22px/1.25 var(--fw-font-display);
  --fw-track-display: -0.03em;
  --fw-track-display-2: -0.025em;
  --fw-track-title: -0.02em;
  --fw-track-heading: -0.015em;
  /* the one Outfit step under 20px */
  --fw-eyebrow: 600 11px/1 var(--fw-font-display);
  --fw-track-eyebrow: 0.2em;
  /* mandatory on every Outfit numeral */
  --fw-nums: tabular-nums;
  /* Spacing */
  --spacing-xs: 4px;
  --spacing-sm: 8px;
  --spacing-md: 16px;
  --spacing-lg: 24px;
  --spacing-xl: 32px;
  --spacing-2xl: 48px;
  /* Border radius */
  --radius-sm: 8px;
  --radius-md: 12px;
  --radius-lg: 16px;
  --radius-xl: 24px;
  --radius-full: 9999px;
  /* Component sizes */
  --top-nav-height: 72px;
  --mobile-top-bar-height: 56px;
  --bottom-nav-height: 64px;
  /* Bottom-nav clearance for the iOS home indicator (installed PWA). The raw
     safe-area inset (~34px) leaves a large empty gap under the tab icons; trim it so
     the bar reads as compact as the desktop/Chrome one while still clearing the
     indicator. 0 in browsers/desktop (inset is 0 there). Tune the subtrahend to taste:
     larger = tighter to the edge, 0px = full inset. */
  --nav-inset-bottom: max(env(safe-area-inset-bottom, 0px) - 24px, 0px);
  --site-footer-height: 89px;
  --navbar-height: var(--top-nav-height);
  --left-rail-width: 280px;
  --right-panel-width: 320px;
  --content-max-width: 1120px;
  --shell-max-width: 1200px;
  --content-padding-x: 32px;
  --content-padding-y: 32px;
  --radius-card: 20px;
  --radius-control: 14px;
  /* Overlay colors for transparency effects (dark theme) */
  --overlay-subtle: rgba(255, 255, 255, 0.02);
  --overlay-light: rgba(255, 255, 255, 0.05);
  --overlay-medium: rgba(255, 255, 255, 0.1);
  --overlay-hover: rgba(255, 255, 255, 0.15);
  --overlay-strong: rgba(255, 255, 255, 0.2);
  --border-faint: rgba(255, 255, 255, 0.04);
  --border-subtle: rgba(255, 255, 255, 0.08);
  --border-medium: rgba(255, 255, 255, 0.12);
  --tick-color: rgba(255, 255, 255, 0.4);
  --tick-color-strong: rgba(255, 255, 255, 0.7);
  /* Component-specific colors */
  --gradient-streak: linear-gradient(135deg, #5c3d1e 0%, #3d2815 100%);
  --shadow-card: 0 2px 8px rgba(0, 0, 0, 0.3);
  --shadow-elevated: 0 4px 16px rgba(0, 0, 0, 0.4);
  --dashed-border-color: var(--overlay-strong);
  /* Header/Nav colors */
  --header-bg: #1a1a1a;
  --header-border: rgba(255, 255, 255, 0.05);
  /* Scrollbar colors */
  --scrollbar-thumb: rgba(255, 255, 255, 0.2);
  --scrollbar-thumb-hover: rgba(255, 255, 255, 0.3);
  /* Motion (theme-invariant) — re-tune app-wide animation here */
  --transition-fast: 100ms;
  --transition-base: 150ms;
  --transition-slow: 200ms;
  --transition-slower: 300ms;
  --transition-slowest: 400ms;
  --ease-standard: ease;
  --ease-emphasized: cubic-bezier(0.4, 0, 0.2, 1);
  /* Scrim scale (theme-invariant black overlays) */
  --scrim-weak: rgba(0, 0, 0, 0.4);
  --scrim: rgba(0, 0, 0, 0.5);
  --scrim-strong: rgba(0, 0, 0, 0.7);
  /* Elevation scale (theme-aware; light variants overridden below) */
  --shadow-low: 0 1px 3px rgba(0, 0, 0, 0.2);
  --shadow-card-lift: 0 8px 24px rgba(0, 0, 0, 0.4);
  --shadow-dropdown: 0 8px 24px rgba(0, 0, 0, 0.4);
  --shadow-modal: 0 20px 60px rgba(0, 0, 0, 0.5);
  /* Sizing scale (theme-invariant) */
  --touch-target: 44px;
  --size-icon-sm: 24px;
  --size-icon-base: 36px;
  --size-icon-md: 32px;
  --size-icon-lg: 40px;
  --size-icon-xl: 48px;
  --size-avatar-md: 56px;
  --size-avatar-lg: 80px;
}

@media (min-width: 1600px) {
  :root {
    --shell-max-width: 1800px;
  }
}
/* ============================================
   LIGHT THEME
   ============================================ */
/* The light theme is ONE palette ("Firewalker Color System" §06 + user call,
   Aug 2026): warm paper, ink text, and the ramp run DEEPER (#C2410C → #9A3409)
   because gold cannot reach contrast on paper. It applies to data-theme=light
   under any of the four supported brand styles AND to data-brand=light (the
   switcher's Light option). The block lives AFTER the brand-style experiments
   so it out-cascades the retuned dark palettes at equal specificity — see
   "LIGHT SURFACE (approved)" below. The deprecated id-x and v10-x combos keep
   their own higher-specificity light corrections. */

/* ============================================
   BRAND STYLE EXPERIMENTS (`data-brand` on <html>)
   ============================================ */
/* Runtime brand-style switch (localStorage `firewalker-brand-style`, applied
   pre-paint in _Layout; floating switcher widget). Absent attribute = the
   APPROVED ramp system (:root above). The four SUPPORTED styles are:
     (default)   — the ramp: dual ember→gold, CTA gradient, ramp lockup
     ember-led   — single colour: everything pins to the ramp's midpoint flat
     gold-led    — single colour: everything pins to the ramp's tip flat
     light       — the one light palette (warm paper, deep ramp) — see the
                   LIGHT SURFACE block below
   Both single-colour styles are the surface-pin mechanism of the approved doc
   (§02b): a whole screen may sit at one point of the ramp; these pin the whole
   product there. Status colours are the approved set from :root and are NOT
   re-pinned per style. Everything after the LIGHT SURFACE block (option-a,
   id-*, v10-*, theme-gradient, lockup experiments) is DEPRECATED —
   kept for reference under the switcher's Deprecated group; see
   docs/brand/DEPRECATIONS.md. */
[data-brand=gold-led] {
  /* Gold pin: flat controls, links and accents all take the tip. Ink text on
     gold (white on gold is 2.3:1). Gradients collapse flat — a single-colour
     style has no ramp to draw. */
  --color-primary: #e0a030;
  --color-primary-light: #f2c46a;
  --color-primary-dark: #d08f22;
  --color-primary-text: #f2c46a;
  --color-primary-deep: #8f5a0a;
  --color-ember-mid: #e0a030;
  --color-ember-mid-light: #f2c46a;
  --color-ember-mid-dark: #d08f22;
  --color-secondary: #e0a030;
  --color-secondary-light: #f2c46a;
  --color-secondary-dark: #d08f22;
  --color-on-primary: #17110a;
  --gradient-primary: linear-gradient(100deg, #e0a030 0%, #e0a030 100%);
  --gradient-progress: linear-gradient(90deg, #e0a030 0%, #e0a030 100%);
  /* Position ramp moves in VALUE where it can't move in hue: dark gold step
     forward to the pin (Stepper §04). */
  --progress-ramp-start: #d08f22;
  --progress-ramp-end: #e0a030;
  --progress-fill-start: #d08f22;
  --progress-fill-end: #e0a030;
  --color-gold: #e0a030;
  /* Lockup drops to flat gold as one object — never one ramped, one flat. */
  --brand-mark-color: #e0a030;
  --brand-word-letters: #f2ece4;
  --brand-word-arm: #e0a030;
}

[data-brand=ember-led] {
  /* Ember pin: the approved "Ember flat" surface — every control takes the
     midpoint #F08A2F (inherited from :root), the base stays for outlines and
     text accents, and the tip collapses to the midpoint so nothing ramps.
     Gold remains the earned/reward token (that meaning survives the pin). */
  --color-primary: #ff6b2c;
  --color-primary-light: #ff8551;
  --color-primary-dark: #e05a20;
  --color-secondary: #f08a2f;
  --color-secondary-light: #f59d47;
  --color-secondary-dark: #d97a1f;
  --gradient-primary: linear-gradient(100deg, #f08a2f 0%, #f08a2f 100%);
  --gradient-progress: linear-gradient(90deg, #f08a2f 0%, #f08a2f 100%);
  /* Position ramp moves in VALUE where it can't move in hue: dark ember step
     forward to the pin (Stepper §04). */
  --progress-ramp-start: #d97a1f;
  --progress-ramp-end: #f08a2f;
  --progress-fill-start: #d97a1f;
  --progress-fill-end: #f08a2f;
  /* Lockup drops to flat ember as one object (the one-colour cut, §05). */
  --brand-mark-color: #ff6b2c;
  --brand-word-letters: #f2ece4;
  --brand-word-arm: #ff6b2c;
}

/* ============================================
   LIGHT SURFACE (approved) — data-theme=light OR data-brand=light
   ============================================ */
/* "Firewalker Color System" §06: light is a constraint, not a lesser theme.
   Gold cannot travel to paper (#E0A030 on warm paper is ~2.1:1), so the ramp
   runs DEEPER instead of brighter: #C2410C → #9A3409 (4.7:1 / 6.6:1 for text).
   One palette for every route into light: the OS preference, the theme toggle,
   and the switcher's Light brand style all land here. Placed after the
   supported dark styles so it out-cascades them at equal specificity; the
   deprecated experiments' own [data-theme=light][data-brand=…] combos are more
   specific and keep their historical behaviour. Neutrals are the v1.0 warm
   paper set. The lockup keeps BOTH ramps (mark + arm) — value contrast comes
   from the ink letters (§05: "Warm Paper. Letters to Ink. Both ramps stay as
   they are."). */
[data-theme=light],
[data-brand=light] {
  /* Deep ramp */
  --color-primary: #c2410c;
  --color-primary-light: #d4520f;
  --color-primary-dark: #a93709;
  --color-primary-text: #d4520f;
  --color-primary-deep: #9a3409;
  --color-ember-mid: #c2410c;
  --color-ember-mid-light: #d4520f;
  --color-ember-mid-dark: #a93709;
  --color-secondary: #9a3409;
  --color-secondary-light: #b4430c;
  --color-secondary-dark: #7d2b07;
  --color-on-primary: #fff;
  --color-accent-ink: #c2410c;
  --gradient-primary: linear-gradient(100deg, #c2410c 0%, #9a3409 100%);
  --gradient-progress: linear-gradient(90deg, #c2410c 0%, #9a3409 100%);
  /* Position ramp runs DEEPER, matching the light gradient direction —
     "further along" means deeper here (Stepper §04); white ink on all stops. */
  --progress-ramp-start: #c2410c;
  --progress-ramp-end: #9a3409;
  --progress-fill-start: #c2410c;
  --progress-fill-end: #9a3409;
  /* No gold on light — earned states take the deep ember. */
  --color-gold: #c2410c;
  /* Status drops to each family's DEEP step (the light-theme substitute). */
  --color-success: #1e7a4c;
  --color-success-text: #1e7a4c;
  --color-success-deep: #14573a;
  --color-warning: #b4530a;
  --color-warning-text: #b4530a;
  --color-warning-deep: #8a3f07;
  --color-error: #b42318;
  --color-error-text: #b42318;
  --color-error-deep: #8a1a12;
  --color-info: #215bae;
  --color-info-text: #215bae;
  --color-info-deep: #18437f;
  --color-neutral: #6e6e6e;
  --color-neutral-text: #55504a;
  --color-neutral-deep: #3a3a3a;
  --color-positive: #1e7a4c;
  /* Chart chrome on paper */
  --chart-grid-line: rgba(0, 0, 0, 0.08);
  /* Lockup: ink letters, both ramps stay (standard bright ramp on paper). */
  --brand-gold: #8f5a0a;
  --brand-mark-color: linear-gradient(0deg, #ff6b2c 0%, #e0a030 100%);
  --brand-word-letters: #17110a;
  --brand-word-arm: linear-gradient(59deg, #ff6b2c 3%, #e0a030 10%);
  /* Warm-paper neutrals (v1.0 2c) + derived hover/overlay/scrollbar steps */
  --bg-dark: #f7f3ee;
  --bg-dark-secondary: #fffdfa;
  --bg-dark-tertiary: #efe9e1;
  --bg-dark-card: #fffdfa;
  --bg-dark-elevated: #ebe3d9;
  --bg-card-hover: #f7f1e8;
  --bg-elevated-hover: #e2d8ca;
  --bg-input-hover: #e2d8ca;
  --text-primary: #17110a;
  --text-secondary: rgba(23, 17, 10, 0.7);
  /* Ink alphas retuned 2026-08-07: tertiary 0.55→0.62 clears WCAG AA 4.5:1
     for small text on every paper surface (worst case #e2d8ca inputs);
     muted 0.38→0.55 clears the 3:1 large-text/UI floor everywhere while
     staying the weakest step (0.7 > 0.62 > 0.55). Muted remains sub-AA for
     <18px text by design — de-emphasized microcopy only; informative small
     text belongs on tertiary or above. */
  --text-tertiary: rgba(23, 17, 10, 0.62);
  --text-muted: rgba(23, 17, 10, 0.55);
  /* Habit palette on paper: the bright slots fall under the 3:1 large-text
     floor against --bg-dark-card, so they re-pin to same-hue deepened values
     (minimum darkening to clear 3.2:1 on #fffdfa). purple/red/pink/indigo
     already clear and keep their :root values; orange follows --color-primary
     (#c2410c here) via the :root alias. */
  --habit-color-blue: #2092ec;
  --habit-color-green: #46a14a;
  --habit-color-amber: #cc7a00;
  --habit-color-cyan: #009cb0;
  --habit-color-info: #2297cc;
  --habit-color-teal: #249e92;
  --habit-color-lime: #75994c;
  --overlay-subtle: rgba(58, 42, 30, 0.03);
  --overlay-light: rgba(58, 42, 30, 0.05);
  --overlay-medium: rgba(58, 42, 30, 0.09);
  --overlay-hover: rgba(58, 42, 30, 0.13);
  --overlay-strong: rgba(58, 42, 30, 0.2);
  --border-faint: rgba(58, 42, 30, 0.07);
  --border-subtle: rgba(58, 42, 30, 0.11);
  --border-medium: rgba(58, 42, 30, 0.2);
  --tick-color: rgba(23, 17, 10, 0.35);
  --tick-color-strong: rgba(23, 17, 10, 0.55);
  --gradient-streak: linear-gradient(135deg, #f5e9d8 0%, #eddcc2 100%);
  --shadow-card: 0 1px 3px rgba(58, 32, 14, 0.07);
  --shadow-elevated: 0 4px 16px rgba(58, 32, 14, 0.1);
  --dashed-border-color: rgba(58, 42, 30, 0.25);
  --header-bg: #fffdfa;
  --header-border: rgba(58, 42, 30, 0.1);
  --scrollbar-thumb: rgba(58, 42, 30, 0.25);
  --scrollbar-thumb-hover: rgba(58, 42, 30, 0.35);
  --shadow-low: 0 1px 3px rgba(58, 32, 14, 0.06);
  --shadow-card-lift: 0 8px 24px rgba(58, 32, 14, 0.12);
  --shadow-dropdown: 0 8px 24px rgba(58, 32, 14, 0.12);
  --shadow-modal: 0 20px 60px rgba(58, 32, 14, 0.18);
}

/* ============================================
   DATING SECTION ACCENT OVERRIDE (Turn 19)
   ============================================ */
/* The single accent exception in the product: rose replaces ember once you've
   navigated INTO the Dating section (body carries .app--dating, set in
   _Layout). Scoped to the section, not to dating content — a dating card on
   Home stays ember. Remapping the primary family means every primary button,
   link, active state and the dock inside Dating flips with no per-component
   work; --color-accent-ink follows automatically. */
.app--dating {
  --color-primary: var(--color-rose);
  --color-primary-light: var(--color-rose-light);
  --color-primary-dark: var(--color-rose-dark);
  --color-on-primary: var(--color-on-rose);
  --gradient-primary: linear-gradient(100deg, var(--color-rose) 0%, var(--color-rose-light) 100%);
  /* Brand lockup goes rose inside Dating too — the section differentiator
     lives in the mark, not in a louder CTA ramp. Structure mirrors the
     approved ramp lockup (base->tip on the mark, same ramp on the arm);
     letters stay Bone via :root. Dark-only nuance: a carried
     data-theme=light re-pins the .lp element back to the ember lockup
     (element-level pin beats this body-level block), matching how the rest
     of the page already falls back to ember on light-carried routes. */
  --brand-mark-color: linear-gradient(0deg, var(--color-rose-dark) 0%, var(--color-rose-light) 100%);
  --brand-word-arm: linear-gradient(59deg, var(--color-rose-dark) 3%, var(--color-rose-light) 10%);
}

/* Dating is softer, not other: pill buttons inside the section (same
   components, rounder shape). Icon buttons are already circular. */
.app--dating .btn {
  border-radius: var(--radius-full);
}

/* ============================================
   PUBLIC CHROME THEME PIN
   ============================================ */
/* Anonymous marketing/auth pages are pinned dark: a data-theme="light" carried in
   localStorage from a previous session must not flip the public funnel mid-stream
   (the landing the visitor arrives from is always dark). Mirrors every variable the
   light theme overrides above — keep the two lists in sync. The approved LIGHT
   SURFACE overrides the brand + status tokens too, so those are re-pinned to the
   :root ramp values here as well. */
.app--public {
  /* Brand — approved ramp (mirror :root) */
  --color-primary: #ff6b2c;
  --color-primary-light: #ff8551;
  --color-primary-dark: #e05a20;
  --color-primary-text: #ff8a54;
  --color-primary-deep: #9a3409;
  --color-ember-mid: #f08a2f;
  --color-ember-mid-light: #f59d47;
  --color-ember-mid-dark: #d97a1f;
  --color-secondary: #e0a030;
  --color-secondary-light: #f2c46a;
  --color-secondary-dark: #d08f22;
  --color-on-primary: #1a120d;
  --color-accent-ink: var(--color-ember-mid);
  --gradient-primary: linear-gradient(100deg, #ff6b2c 0%, #e8a83e 100%);
  --gradient-progress: linear-gradient(90deg, #ff6b2c 0%, #e0a030 100%);
  --progress-ramp-start: #ff6b2c;
  --progress-ramp-end: #e0a030;
  --progress-fill-start: #f08a2f;
  --progress-fill-end: #e0a030;
  --color-gold: #e0a030;
  --color-positive: #4fa97a;
  /* Status — approved dark set (mirror :root) */
  --color-success: #4fa97a;
  --color-success-text: #7fd3a5;
  --color-success-deep: #1e7a4c;
  --color-warning: #f97316;
  --color-warning-text: #fba05e;
  --color-warning-deep: #b4530a;
  --color-error: #ff6369;
  --color-error-text: #ff9095;
  --color-error-deep: #b42318;
  --color-info: #7fa8d9;
  --color-info-text: #a9c6e8;
  --color-info-deep: #215bae;
  --color-neutral: #8a8a8a;
  --color-neutral-text: #c9c4bd;
  --color-neutral-deep: #3a3a3a;
  --chart-grid-line: rgba(255, 255, 255, 0.1);
  --bg-dark: #0d0d0d;
  --bg-dark-secondary: #1a1a1a;
  --bg-dark-tertiary: #262626;
  --bg-dark-card: #1f1f1f;
  --bg-dark-elevated: #2d2d2d;
  --bg-card-hover: #242424;
  --bg-elevated-hover: #383838;
  --bg-input-hover: #363636;
  --text-primary: #f2ece4; /* Bone — approved brand text on dark (16.6:1) */
  --text-secondary: rgba(255, 255, 255, 0.7);
  --text-tertiary: rgba(255, 255, 255, 0.5);
  --text-muted: rgba(255, 255, 255, 0.38);
  --overlay-subtle: rgba(255, 255, 255, 0.02);
  --overlay-light: rgba(255, 255, 255, 0.05);
  --overlay-medium: rgba(255, 255, 255, 0.1);
  --overlay-hover: rgba(255, 255, 255, 0.15);
  --overlay-strong: rgba(255, 255, 255, 0.2);
  --border-faint: rgba(255, 255, 255, 0.04);
  --border-subtle: rgba(255, 255, 255, 0.08);
  --border-medium: rgba(255, 255, 255, 0.12);
  --tick-color: rgba(255, 255, 255, 0.4);
  --tick-color-strong: rgba(255, 255, 255, 0.7);
  --gradient-streak: linear-gradient(135deg, #5c3d1e 0%, #3d2815 100%);
  --shadow-card: 0 2px 8px rgba(0, 0, 0, 0.3);
  --shadow-elevated: 0 4px 16px rgba(0, 0, 0, 0.4);
  --dashed-border-color: var(--overlay-strong);
  --header-bg: #1a1a1a;
  --header-border: rgba(255, 255, 255, 0.05);
  --scrollbar-thumb: rgba(255, 255, 255, 0.2);
  --scrollbar-thumb-hover: rgba(255, 255, 255, 0.3);
  --shadow-low: 0 1px 3px rgba(0, 0, 0, 0.2);
  --shadow-card-lift: 0 8px 24px rgba(0, 0, 0, 0.4);
  --shadow-dropdown: 0 8px 24px rgba(0, 0, 0, 0.4);
  --shadow-modal: 0 20px 60px rgba(0, 0, 0, 0.5);
  --brand-gold: #e0a030;
  /* Default lockup back to its dark (approved ramp) values — the public funnel
     is always dark, so a carried data-theme="light" must not swap in the ink
     variant. */
  --brand-mark-color: linear-gradient(0deg, #ff6b2c 0%, #e0a030 100%);
  --brand-word-letters: #f2ece4;
  --brand-word-arm: linear-gradient(59deg, #ff6b2c 3%, #e0a030 10%);
}

/* Brand-style re-pins for the public chrome: a carried data-theme="light" must
   not swap in the light palette on the dark-pinned funnel. Values mirror the
   dark [data-brand=…] blocks above — keep in sync. */
[data-brand=gold-led] .app--public {
  --color-primary: #e0a030;
  --color-primary-light: #f2c46a;
  --color-primary-dark: #d08f22;
  --color-primary-text: #f2c46a;
  --color-primary-deep: #8f5a0a;
  --color-ember-mid: #e0a030;
  --color-ember-mid-light: #f2c46a;
  --color-ember-mid-dark: #d08f22;
  --color-secondary: #e0a030;
  --color-secondary-light: #f2c46a;
  --color-secondary-dark: #d08f22;
  --color-on-primary: #17110a;
  --gradient-primary: linear-gradient(100deg, #e0a030 0%, #e0a030 100%);
  --gradient-progress: linear-gradient(90deg, #e0a030 0%, #e0a030 100%);
  --progress-ramp-start: #d08f22;
  --progress-ramp-end: #e0a030;
  --progress-fill-start: #d08f22;
  --progress-fill-end: #e0a030;
  --color-gold: #e0a030;
  --brand-mark-color: #e0a030;
  --brand-word-letters: #f2ece4;
  --brand-word-arm: #e0a030;
}

[data-brand=ember-led] .app--public {
  --color-primary: #ff6b2c;
  --color-primary-light: #ff8551;
  --color-primary-dark: #e05a20;
  --color-secondary: #f08a2f;
  --color-secondary-light: #f59d47;
  --color-secondary-dark: #d97a1f;
  --gradient-primary: linear-gradient(100deg, #f08a2f 0%, #f08a2f 100%);
  --gradient-progress: linear-gradient(90deg, #f08a2f 0%, #f08a2f 100%);
  --progress-ramp-start: #d97a1f;
  --progress-ramp-end: #f08a2f;
  --progress-fill-start: #d97a1f;
  --progress-fill-end: #f08a2f;
  --brand-mark-color: #ff6b2c;
  --brand-word-letters: #f2ece4;
  --brand-word-arm: #ff6b2c;
}

/* Light brand style on the funnel: the funnel is an art-directed pinned-dark
   surface, so the Light option governs the product UI only — the funnel shows
   the default dark ramp. The base .app--public block above already re-pins
   every token the LIGHT SURFACE overrides, so this needs no extra values; the
   selector exists so the intent is explicit and greppable. */
[data-brand=light] .app--public {
  --color-primary: #ff6b2c;
}

/* The anonymous landing renders its own .lp chrome WITHOUT .app--public
   (PublicChrome=false), so the funnel re-pin above never reaches it — but the
   html-level LIGHT SURFACE block does (via the Light brand style OR a plain
   data-theme=light from the OS preference/theme toggle). Pin the .lp root
   back to the dark ramp on every light route (element-level, beats every
   html-level block). Mirrors :root — keep in sync. */
[data-brand=light] .lp,
[data-theme=light] .lp {
  --color-primary: #ff6b2c;
  --color-primary-light: #ff8551;
  --color-primary-dark: #e05a20;
  --color-primary-text: #ff8a54;
  --color-primary-deep: #9a3409;
  --color-ember-mid: #f08a2f;
  --color-ember-mid-light: #f59d47;
  --color-ember-mid-dark: #d97a1f;
  --color-secondary: #e0a030;
  --color-secondary-light: #f2c46a;
  --color-secondary-dark: #d08f22;
  --color-on-primary: #1a120d;
  --color-accent-ink: var(--color-ember-mid);
  --gradient-primary: linear-gradient(100deg, #ff6b2c 0%, #e8a83e 100%);
  --gradient-progress: linear-gradient(90deg, #ff6b2c 0%, #e0a030 100%);
  --progress-ramp-start: #ff6b2c;
  --progress-ramp-end: #e0a030;
  --progress-fill-start: #f08a2f;
  --progress-fill-end: #e0a030;
  --color-gold: #e0a030;
  --color-positive: #4fa97a;
  --color-success: #4fa97a;
  --color-success-text: #7fd3a5;
  --color-success-deep: #1e7a4c;
  --color-warning: #f97316;
  --color-warning-text: #fba05e;
  --color-warning-deep: #b4530a;
  --color-error: #ff6369;
  --color-error-text: #ff9095;
  --color-error-deep: #b42318;
  --color-info: #7fa8d9;
  --color-info-text: #a9c6e8;
  --color-info-deep: #215bae;
  --color-neutral: #8a8a8a;
  --color-neutral-text: #c9c4bd;
  --color-neutral-deep: #3a3a3a;
  --chart-grid-line: rgba(255, 255, 255, 0.1);
  --brand-gold: #e0a030;
  --brand-mark-color: linear-gradient(0deg, #ff6b2c 0%, #e0a030 100%);
  --brand-word-letters: #f2ece4;
  --brand-word-arm: linear-gradient(59deg, #ff6b2c 3%, #e0a030 10%);
  /* Dark neutrals too — the LIGHT SURFACE block overrides them at html level
     and the landing's art-directed darks must not inherit warm paper (mirrors
     the .app--public neutral pin — keep in sync). */
  --bg-dark: #0d0d0d;
  --bg-dark-secondary: #1a1a1a;
  --bg-dark-tertiary: #262626;
  --bg-dark-card: #1f1f1f;
  --bg-dark-elevated: #2d2d2d;
  --bg-card-hover: #242424;
  --bg-elevated-hover: #383838;
  --bg-input-hover: #363636;
  --text-primary: #f2ece4; /* Bone — approved brand text on dark (16.6:1) */
  --text-secondary: rgba(255, 255, 255, 0.7);
  --text-tertiary: rgba(255, 255, 255, 0.5);
  --text-muted: rgba(255, 255, 255, 0.38);
  --overlay-subtle: rgba(255, 255, 255, 0.02);
  --overlay-light: rgba(255, 255, 255, 0.05);
  --overlay-medium: rgba(255, 255, 255, 0.1);
  --overlay-hover: rgba(255, 255, 255, 0.15);
  --overlay-strong: rgba(255, 255, 255, 0.2);
  --border-faint: rgba(255, 255, 255, 0.04);
  --border-subtle: rgba(255, 255, 255, 0.08);
  --border-medium: rgba(255, 255, 255, 0.12);
  --tick-color: rgba(255, 255, 255, 0.4);
  --tick-color-strong: rgba(255, 255, 255, 0.7);
  --gradient-streak: linear-gradient(135deg, #5c3d1e 0%, #3d2815 100%);
  --shadow-card: 0 2px 8px rgba(0, 0, 0, 0.3);
  --shadow-elevated: 0 4px 16px rgba(0, 0, 0, 0.4);
  --dashed-border-color: var(--overlay-strong);
  --header-bg: #1a1a1a;
  --header-border: rgba(255, 255, 255, 0.05);
  --scrollbar-thumb: rgba(255, 255, 255, 0.2);
  --scrollbar-thumb-hover: rgba(255, 255, 255, 0.3);
  --shadow-low: 0 1px 3px rgba(0, 0, 0, 0.2);
  --shadow-card-lift: 0 8px 24px rgba(0, 0, 0, 0.4);
  --shadow-dropdown: 0 8px 24px rgba(0, 0, 0, 0.4);
  --shadow-modal: 0 20px 60px rgba(0, 0, 0, 0.5);
}

/* ============================================
   BASE STYLES
   ============================================ */
*, *::before, *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

html {
  font-size: 16px;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  scroll-behavior: smooth;
}

body {
  font-family: var(--fw-font-text);
  font-size: 1rem;
  line-height: 1.5;
  color: var(--text-primary);
  background: var(--bg-dark);
  min-height: 100vh;
  min-height: 100dvh;
  overflow-x: hidden;
}

/* Global "pretty" scrollbar — thin, inset, rounded — for EVERY scroll area on every
   page, every variant, every engine. main.css loads before the compiled component CSS,
   so this single bare (unscoped) definition is inherited app-wide by both CSS pipelines.
   Element-level opt-outs (intentionally-hidden horizontal strips like .tabs / .filter-chips)
   are more specific and still win. Uses the theme overlay vars so it flips for light mode. */
html {
  scrollbar-width: thin; /* Firefox */
  scrollbar-color: var(--overlay-medium) transparent; /* Firefox: thumb / track */
}

::-webkit-scrollbar {
  width: 10px;
  height: 10px;
}

::-webkit-scrollbar-track {
  background: transparent;
}

::-webkit-scrollbar-thumb {
  background: var(--overlay-medium);
  border-radius: var(--radius-full);
  border: 3px solid transparent; /* inset so the thumb reads thin */
  background-clip: padding-box;
}

::-webkit-scrollbar-thumb:hover {
  background: var(--overlay-strong);
  background-clip: padding-box;
}

::-webkit-scrollbar-corner {
  background: transparent;
}

/* Type Decision v1.0: the display face applies on every surface, not per
   brand style. h1–h4 sit at or above the 20px boundary → Outfit 600 with the
   scale's negative tracking (and tabular numerals, mandatory in Outfit);
   h5/h6 are below it → they stay on the text face. */
h1, h2, h3, h4 {
  font-family: var(--fw-font-display);
  font-weight: 600;
  font-variant-numeric: var(--fw-nums);
  color: var(--text-primary);
  margin-bottom: var(--spacing-md);
}

h5, h6 {
  font-family: var(--fw-font-text);
  font-weight: 700;
  line-height: 1.25;
  color: var(--text-primary);
  margin-bottom: var(--spacing-md);
}

h1 {
  font-size: 2.25rem;
  line-height: 1.15;
  letter-spacing: var(--fw-track-title);
}

h2 {
  font-size: 1.875rem;
  line-height: 1.15;
  letter-spacing: var(--fw-track-title);
}

h3 {
  font-size: 1.5rem;
  line-height: 1.25;
  letter-spacing: var(--fw-track-heading);
}

h4 {
  font-size: 1.25rem;
  line-height: 1.25;
  letter-spacing: var(--fw-track-heading);
}

h5 {
  font-size: 1.125rem;
}

h6 {
  font-size: 1rem;
}

p {
  margin-bottom: var(--spacing-md);
  color: var(--text-secondary);
}

a {
  color: var(--color-primary);
  text-decoration: none;
  transition: color 150ms ease;
}

a:hover {
  color: var(--color-primary-light);
}

img {
  max-width: 100%;
  height: auto;
  display: block;
}

ul, ol {
  list-style: none;
}

button {
  font-family: inherit;
  cursor: pointer;
  border: none;
  background: none;
}

input, textarea, select {
  font-family: inherit;
}

::selection {
  background: color-mix(in srgb, var(--color-primary) 30%, transparent);
  color: var(--text-primary);
}

:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
}

/* ============================================
   APP SHELL & LAYOUT
   ============================================ */
.app {
  height: 100vh;
  height: 100dvh;
  display: flex;
  flex-direction: column;
  background: var(--bg-dark);
  overflow: hidden;
}

.app-container {
  width: 100%;
  max-width: var(--shell-max-width);
  margin: 0 auto;
  padding: 0 var(--spacing-md);
}

@media (min-width: 1024px) {
  .app-container {
    padding: 0 var(--spacing-lg);
  }
}
/* Header */
.header {
  position: sticky;
  top: 0;
  z-index: 100;
  flex-shrink: 0;
  background: var(--header-bg);
  border-bottom: 1px solid var(--header-border);
  padding-top: env(safe-area-inset-top, 0);
  display: none;
}

@media (min-width: 1024px) {
  .header {
    display: block;
  }
}
/* Three-column nav: the side groups share the leftover space equally so the
   nav sits at the TRUE centre of the bar. `space-between` used to centre it
   only by accident — the left group (lockup + circle pill) is ~4x the width of
   the right (two 44px actions), which pushed the nav well right of centre.

   Grid rather than two `flex: 1 1 0` sides: a `1fr` track floors at its own
   min-content, a flex item with `min-width: 0` does not. The shell is capped at
   1200px all the way to 1599px, so an equal split gives each side ~280px while
   the left group (32px mark + 213px wordmark + divider + circle pill) needs
   ~355px — the flex version squeezed the group under its content and the
   unshrinkable lockup and the pill spilled out the right side, over the first
   nav item. Now the nav slides off-centre instead; nothing ever overlaps. */
.header__inner {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  height: var(--navbar-height);
  padding: 0 var(--spacing-md);
  max-width: var(--shell-max-width);
  margin: 0 auto;
}

.header__left {
  display: flex;
  align-items: center;
  gap: 20px;
}

/* The 15c hairline: ends the logo zone so the wordmark and the circle pill
   stop reading as one long string. Sits on the circle slot rather than the
   logo so it only appears when there is something to divide from. 20px each
   side rather than the doc's 28px — its mockup had four label-only nav items,
   ours has five with icons, and 28+1+28 does not fit at 1024px.

   `min-width: 0` is stage 1 of the shed order (see the block below the lockup):
   the pill is the only thing in the left group allowed to shrink, so a long
   city name gives ground — label ellipsised, then down to the globe alone —
   before the lockup loses so much as a pixel. */
.header__left > .header__circle-slot {
  min-width: 0;
  padding-left: 20px;
  border-left: 1px solid var(--border-medium);
}

/* Never compress the centre group — it is the thing being aligned to. (The
   `auto` track already floors at the nav's min-content; this keeps the intent
   explicit and still applies wherever .desktop-nav sits in a flex row.) */
.desktop-nav {
  flex-shrink: 0;
}

.header__logo {
  display: flex;
  align-items: center;
  /* The mark and the wordmark are both unshrinkable, so a squeezed lockup does
     not compress — it overflows. Take the shrink off the lockup entirely and
     leave it to the circle pill, which has an ellipsis to give. */
  flex-shrink: 0;
  /* 12px, not --spacing-sm: the brand book's lockup gap is 38% of the mark
     height, which at the 32px mark below is 12px ("Firewalker Pairings" 15c). */
  gap: 12px;
  font-size: 1.25rem;
  font-weight: 700;
  color: var(--text-primary);
  text-decoration: none;
}

.header__logo-icon {
  /* 32px per Turn 15: measured off the app nav, the flame was 16.5px — under
     the brand book's 24px floor, where the inner tongue starts closing, and
     small enough that the wordmark was out-weighing it. 32px puts the mark at
     roughly double the wordmark's height so it anchors the lockup, and still
     leaves 40px of slack in the 72px nav. */
  width: 2rem;
  height: 2rem;
}

/* Brand lockup (flame-in-broken-ring mark + drawn-wordmark voice).
   .brand-mark is a mask-tinted span so the mark follows --brand-mark-color at
   runtime (champagne by default, deep gold on light theme; overridden per
   lockup experiment); size comes from the host's icon class. */
.brand-mark {
  display: inline-block;
  flex-shrink: 0;
  background: var(--brand-mark-color);
  -webkit-mask: url("/images/brand/firewalker-mark.svg") no-repeat center/contain;
  mask: url("/images/brand/firewalker-mark.svg") no-repeat center/contain;
}

.brand-word {
  /* The drawn logotype ("Firewalker Wordmark v1.1", user-supplied master) as
     a mask-tinted box, same technique as .brand-mark — never re-typed in a
     font. SVG viewBox is 770×65 with letters on y25–61 (cap 36), so cap ≈
     0.554×height; the inner "Firewalker" text stays for screen readers
     (font-size 0). Every .brand-word height below is sized from that ratio —
     re-derive them all if the master's viewBox changes again.

     Painted as TWO stacked mask layers rather than one, so the arm (the
     swoosh over the F) can take a different fill from the letters. CSS mask
     layers composite into a single alpha channel and cannot be paired 1:1
     with background layers, hence two pseudo-elements. Both split SVGs share
     the master's viewBox, so they register exactly. */
  position: relative;
  display: inline-block;
  flex-shrink: 0;
  height: 1.125rem; /* cap ≈ 10px, matches the former 14px type */
  aspect-ratio: 770 / 65;
  font-size: 0;
  color: transparent;
  overflow: hidden;
}

.brand-word::before,
.brand-word::after {
  content: "";
  position: absolute;
  inset: 0;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-position: center;
  mask-position: center;
  -webkit-mask-size: contain;
  mask-size: contain;
}

/* Default fill for both layers is the same warm ramp as the landing key-word
   accents (lp-ember-text in _lp-core.scss) — keep the two in sync. Because
   each layer covers the full box, one ramp across two masks is identical to
   the single-mask version it replaced. */
.brand-word::before {
  background: var(--brand-word-letters);
  -webkit-mask-image: url("/images/brand/firewalker-wordmark-letters.svg");
  mask-image: url("/images/brand/firewalker-wordmark-letters.svg");
}

.brand-word::after {
  background: var(--brand-word-arm);
  -webkit-mask-image: url("/images/brand/firewalker-wordmark-arm.svg");
  mask-image: url("/images/brand/firewalker-wordmark-arm.svg");
}

@media (max-width: 767.98px) {
  .header__logo .brand-word {
    display: none;
  }
}

/* ---- Left-group shed order ----------------------------------------------
   A centred nav gives each side (shell − nav)/2. The nav is five icon+label
   items, ~610px, and --shell-max-width is 1200px until the viewport hits
   1600px — so from 1280 to 1599 each side has ~280px no matter how wide the
   monitor is. The left group wants 257px of lockup + 21px of divider + a
   ~150px city pill. Something has to give, and it gives in this order:

     1. the city pill drops its label, down to the globe alone
     2. the city pill goes
     3. the wordmark goes, leaving the 32px mark
     4. the mark goes

   Stage 1 is content-driven and needs no breakpoint — the pill is the only
   shrinkable thing in the group (`.header__logo` is flex-shrink: 0), so a long
   city name ellipsises away at any width before the lockup is touched. Stages
   2 and 3 are the width bands below. Stage 4 has no band: the desktop header
   is already replaced by the mobile one under 1024px, where a 32px mark still
   clears the budget by ~160px. The marketing header has no centre nav, so it
   keeps the full lockup throughout — hence :not(.landing-header). */

/* Stage 2 — everything below 1600px is on the 1200px shell, which cannot hold
   the pill and a centred nav at once. The city is also reachable from the
   account menu (Circles), the lockup is not reachable from anywhere. */
@media (max-width: 1599.98px) {
  .header:not(.landing-header) .header__circle-slot {
    display: none;
  }
}

/* Stage 3 — under 1280 the shell tracks the viewport and the budget drops
   under the 213px wordmark. Keep the 32px mark: Turn 15's point is that the
   mark, not the word, is the anchor, and 15d argues the word is redundant
   in-product at any width. */
@media (max-width: 1279.98px) {
  .header:not(.landing-header) .header__logo .brand-word {
    display: none;
  }
}

.header__circle-select {
  display: flex;
  align-items: center;
  gap: var(--spacing-xs);
  /* Symmetric 6px block padding: the pill is the only left-group item that is
     not a fixed square, so an uneven box here reads as a mis-aligned baseline
     against the 32px mark and the 44px actions. */
  padding: 6px var(--spacing-sm);
  max-width: 100%;
  background: var(--bg-dark-tertiary);
  border-radius: var(--radius-full);
  font-size: 0.875rem;
  color: var(--text-secondary);
  cursor: pointer;
  transition: all 150ms ease;
  border: none;
}

/* Only the label gives ground when the bar gets tight — the globe and chevron
   stay square so the pill never distorts. */
.header__circle-select > span {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
}

.header__circle-select:hover {
  background: var(--bg-dark-elevated);
  color: var(--text-primary);
}

.header__circle-select svg {
  width: 16px;
  height: 16px;
  flex-shrink: 0;
}

.header__right {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--spacing-sm);
}

.header__action {
  position: relative;
  width: 44px;
  height: 44px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: none;
  border-radius: var(--radius-full);
  color: var(--text-secondary);
  cursor: pointer;
  transition: all 150ms ease;
}

.header__action:hover {
  background: var(--overlay-light);
  color: var(--text-primary);
}

.header__action svg {
  width: 24px;
  height: 24px;
}

.account-menu {
  position: relative;
}

.account-menu > summary {
  list-style: none;
}

.account-menu > summary::-webkit-details-marker {
  display: none;
}

.account-menu__panel {
  position: absolute;
  top: calc(100% + var(--spacing-sm));
  right: 0;
  z-index: 130;
  min-width: 200px;
  padding: var(--spacing-sm);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
  background: var(--bg-dark-elevated);
  box-shadow: var(--shadow-elevated);
}

.account-menu__panel a,
.account-menu__panel button {
  display: block;
  width: 100%;
  padding: 10px 12px;
  border: 0;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--text-secondary);
  font: inherit;
  text-align: left;
  text-decoration: none;
  cursor: pointer;
}

.account-menu__panel a:hover,
.account-menu__panel button:hover {
  background: var(--overlay-light);
  color: var(--text-primary);
}

.account-menu__panel .account-menu__link--active {
  background: color-mix(in srgb, var(--color-primary) 10%, transparent);
  color: var(--color-primary);
}

/* Desktop Navigation */
.desktop-nav {
  display: none;
}

@media (min-width: 1024px) {
  .desktop-nav {
    display: flex;
    align-items: center;
    gap: var(--spacing-xs);
  }
}
.desktop-nav__link {
  display: flex;
  align-items: center;
  gap: var(--spacing-xs);
  padding: var(--spacing-sm) var(--spacing-md);
  font-size: 1rem;
  font-weight: 500;
  color: var(--text-secondary);
  text-decoration: none;
  border-radius: var(--radius-full);
  transition: all 150ms ease;
}

.desktop-nav__link:hover {
  background: var(--overlay-light);
  color: var(--text-primary);
}

/* Turn 19 neutral selection: the fill stays neutral, only the ICON carries the
   accent — the label is full ink. (The dock is the opposite: icon + label tint,
   no fill.) Inside Dating the icon tint follows the rose remap. */
.desktop-nav__link--active {
  color: var(--text-primary);
  font-weight: 600;
  background: var(--overlay-medium);
}

.desktop-nav__link--active .desktop-nav__icon {
  color: var(--color-accent-ink);
}

.desktop-nav__link--admin {
  margin-left: var(--spacing-sm);
  padding-left: var(--spacing-md);
  border-left: 1px solid var(--overlay-medium);
}

.desktop-nav__icon {
  width: 20px;
  height: 20px;
  flex-shrink: 0;
}

/* Bottom Navigation (Mobile) */
.bottom-nav {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 100;
  flex-shrink: 0;
  background: var(--header-bg);
  border-top: 1px solid var(--header-border);
  padding-bottom: var(--nav-inset-bottom);
}

@media (min-width: 1024px) {
  .bottom-nav {
    display: none;
  }
}
.bottom-nav__inner {
  display: flex;
  justify-content: space-evenly;
  align-items: center;
  height: var(--bottom-nav-height);
  max-width: 600px;
  margin: 0 auto;
  padding: 0 var(--spacing-xs);
}

/* Cross-document view transitions on EVERY viewport. Two jobs:
   1. Paint-hold: the old page's snapshot is held until the new document's first
      render, so a nav never flashes blank/unstyled chrome mid-swap. On desktop
      this is the whole point — root animates nothing (below), the swap is
      instant, just never through a blank frame.
   2. Mobile only: the dock (global and area variants) is one shared
      view-transition element, so entering/leaving an area crossfades it in
      place (the dock is display:none on desktop, so it simply isn't captured).
   Same-page dock taps don't transition — the active pill covers those.
   Reduced-motion keeps the transition (paint-hold prevents flashing — that IS
   the reduced-motion-friendly behavior) but drops the dock crossfade; see the
   override after the dock animation block below. */
@view-transition {
  navigation: auto;
}

.bottom-nav {
  view-transition-name: firewalker-area-dock;
}

/* Only the dock animates. The page swaps instantly beneath it — the default
   full-page crossfade reads as flicker — while the held snapshot still
   prevents any white flash during the document swap. */
::view-transition-old(root),
::view-transition-new(root) {
  animation: none;
}

/* The transition overlay must never trap input: if a cross-document
   transition wedges (interrupted nav, bfcache traversal), the snapshot
   layer would otherwise sit over the live page eating every click until
   a manual refresh. Belt to the pagereveal watchdog's suspenders. */
::view-transition {
  pointer-events: none;
}

/* Turn 19 neutral selection: pillars no longer own colours. Every dock tints
   its active tab (icon + label) with the one accent — no background pill. The
   Dating dock goes rose purely because it renders inside .app--dating, where
   the accent itself is remapped; no per-area accents remain. */
.bottom-nav--area {
  --dock-accent: var(--color-accent-ink);
}

.bottom-nav--area .bottom-nav__item--active {
  color: var(--dock-accent);
}

/* Home slot: the flame exits to Today from anywhere in the area. */
.bottom-nav--area .bottom-nav__item--home {
  border-right: 1px solid var(--border-subtle);
}

/* No accent override on the mark here any more: the slot now carries the brand
   mark, which is painted with --brand-mark-color to match the header lockup,
   and a `color` declaration would not reach a masked background anyway. */

/* One quick, decisive crossfade between the two dock variants. (No per-item
   settle animation on top — double motion read as jumpy/flickery.) */
::view-transition-group(firewalker-area-dock),
::view-transition-old(firewalker-area-dock),
::view-transition-new(firewalker-area-dock) {
  animation-duration: 200ms;
  animation-timing-function: cubic-bezier(0.2, 0, 0, 1);
}

/* Reduced motion: keep the transition's paint-hold (it PREVENTS flashing) but
   make the dock swap instant instead of crossfading. Must follow the 200ms
   block above to win the cascade. */
@media (prefers-reduced-motion: reduce) {
  ::view-transition-group(firewalker-area-dock),
  ::view-transition-old(firewalker-area-dock),
  ::view-transition-new(firewalker-area-dock) {
    animation: none;
  }
}

.bottom-nav__item {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  padding: var(--spacing-sm) var(--spacing-xs);
  color: var(--text-muted);
  text-decoration: none;
  transition: all 150ms ease;
  position: relative;
}

/* Account drawer: right-edge panel opened from the mobile header avatar.
   Edge drawer = identity/account; bottom sheets stay for transient lists. */
.account-drawer {
  position: fixed;
  inset: 0;
  z-index: 120;
}

.account-drawer__backdrop {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.62);
  opacity: 0;
  transition: opacity 200ms ease;
}

.account-drawer--open .account-drawer__backdrop {
  opacity: 1;
}

.account-drawer__panel {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  width: min(320px, 86vw);
  display: flex;
  flex-direction: column;
  padding: calc(env(safe-area-inset-top, 0px) + var(--spacing-md)) var(--spacing-md) calc(env(safe-area-inset-bottom, 0px) + var(--spacing-md));
  overflow-y: auto;
  background: var(--bg-dark-secondary);
  border-left: 1px solid var(--border-subtle);
  box-shadow: -12px 0 36px rgba(0, 0, 0, 0.4);
  transform: translateX(105%);
  transition: transform 220ms ease;
  outline: none;
}

.account-drawer--open .account-drawer__panel {
  transform: none;
}

@media (prefers-reduced-motion: reduce) {
  .account-drawer__backdrop,
  .account-drawer__panel {
    transition: none;
  }
}
.account-drawer__identity {
  display: flex;
  align-items: center;
  gap: var(--spacing-sm);
  padding-bottom: var(--spacing-md);
  border-bottom: 1px solid var(--border-subtle);
}

.account-drawer__avatar {
  width: 44px;
  height: 44px;
  border-radius: var(--radius-full);
  border: 2px solid var(--color-primary);
  object-fit: cover;
}

.account-drawer__who {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.account-drawer__name {
  color: var(--text-primary);
}

.account-drawer__meta {
  color: var(--text-tertiary);
  font-size: 0.8125rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.account-drawer__close {
  display: flex;
  padding: var(--spacing-xs);
  background: none;
  border: none;
  color: var(--text-secondary);
  cursor: pointer;
}

.account-drawer__links {
  flex: 1;
  display: flex;
  flex-direction: column;
  padding: var(--spacing-md) 0;
}

.account-drawer__links a {
  display: flex;
  align-items: center;
  min-height: 44px;
  padding: 0 var(--spacing-sm);
  border-radius: var(--radius-control);
  color: var(--text-primary);
  text-decoration: none;
}

.account-drawer__links a:hover {
  background: var(--overlay-light);
}

.account-drawer__logout button {
  width: 100%;
  min-height: 46px;
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-control);
  background: var(--overlay-light);
  color: var(--color-error);
  font: inherit;
  cursor: pointer;
}

.mobile-header__avatar-img {
  width: 32px;
  height: 32px;
  border-radius: var(--radius-full);
  object-fit: cover;
}

/* Hover must not repaint the active tab: it ties the area accent rule on
   specificity but sits later in the file, and tap-hover is sticky on touch. */
.bottom-nav__item:not(.bottom-nav__item--active):hover {
  color: var(--text-secondary);
}

.bottom-nav__item--active {
  color: var(--color-accent-ink);
}

.bottom-nav__item--active .bottom-nav__label {
  font-weight: 600;
}

.bottom-nav__item--admin {
  border-left: 1px solid var(--overlay-medium);
  margin-left: 2px;
  padding-left: var(--spacing-xs);
}

.bottom-nav__icon {
  width: 24px;
  height: 24px;
  transition: transform 150ms ease;
}

/* The Home slot wears the brand mark itself (same mask artwork as the header
   lockup) rather than the old standalone flame glyph — the app's home IS the
   brand. It takes .brand-mark's own --brand-mark-color rather than currentColor
   so the dock mark and the header mark are the same colour, and both follow the
   active brand / lockup theme. The active state still reads: the label under
   it carries the accent. */

/* Dating dock "Me": fixed icon box hosting the person glyph until DatingApp
   hydrates it with the dating primary photo + small heart badge. The 30px box
   is centered in the standard 24px icon row via negative margins so the dock
   height never changes; the photo stays identical in the active state (the
   label carries the accent). */
.bottom-nav__icon--me {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  margin: -3px 0;
}

.bottom-nav__icon--me svg {
  width: 24px;
  height: 24px;
}

.bottom-nav__me-photo {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border-radius: var(--radius-full);
  object-fit: cover;
}

.bottom-nav__me-heart {
  position: absolute;
  right: -3px;
  bottom: -2px;
  display: inline-flex;
  /* Dating dock only renders inside .app--dating, so this is rose (Turn 19) */
  color: var(--color-primary);
  filter: drop-shadow(0 0 1px rgba(0, 0, 0, 0.7));
}

.bottom-nav__me-heart svg {
  width: 12px;
  height: 12px;
}

.bottom-nav__label {
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.01em;
}

.bottom-nav__badge {
  position: absolute;
  top: 4px;
  right: calc(50% - 20px);
}

/* Mobile Header */
.mobile-header {
  position: sticky;
  top: 0;
  z-index: 100;
  flex-shrink: 0;
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: var(--spacing-md);
  padding-top: calc(env(safe-area-inset-top, 0) + var(--spacing-md));
  background: var(--header-bg);
  border-bottom: 1px solid var(--header-border);
}

@media (min-width: 1024px) {
  .mobile-header {
    display: none;
  }
}
/* Today shows the standard mobile header (flame + bell + avatar) like every
   other page; it insets via env(safe-area-inset-top) itself, which also covers
   the installed-PWA status bar (the old .is-standalone .app--today padding
   existed only because Today used to hide this header). */

/* Messages thread drill-in (mobile): React toggles this on .app; ChatHeader becomes the top bar */
.app--thread .mobile-header {
  display: none;
}

.app--dating-thread .bottom-nav {
  display: none;
}

/* Dating Focus Mode: the compact Dating header replaces the global mobile header;
   the shared area dock remains available below. */
.app--dating-focus .mobile-header {
  display: none;
}

/* Edge-to-edge: zero the .app-container + .app-frame__content gutters so the
   focus shell's header/footer (and the full-screen conversation thread) dock flush to
   the screen edges. Chain .app to beat `.app-frame--mobile .app-frame__content` (0,2,0)
   which loads later in the dist CSS. */
.app.app--dating-focus .app-container,
.app.app--dating-thread .app-container {
  padding-left: 0;
  padding-right: 0;
}

.app.app--dating-focus .app-frame__content,
.app.app--dating-thread .app-frame__content {
  padding: 0;
}

.mobile-header__inner {
  display: flex;
  justify-content: space-between;
  align-items: center;
  width: 100%;
  max-width: 600px;
  margin: 0 auto;
  /* Constant header height on every page: slot content (nav switch, page
     actions) must fit inside this row, never grow it (UIShell §7). */
  height: 40px;
}

.mobile-header__left {
  display: flex;
  align-items: center;
  gap: var(--spacing-sm);
}

/* Subpage mode: views that set ViewData["BackUrl"] render a leading back arrow */
.mobile-header__back {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--spacing-sm);
  margin-left: calc(-1 * var(--spacing-sm)); /* keep title x-position identical to arrowless pages */
  color: var(--text-primary);
  border-radius: var(--radius-full);
}

.mobile-header__back:hover {
  color: var(--color-primary);
}

.mobile-header__back svg {
  width: 24px;
  height: 24px;
}

.mobile-header__title {
  font-size: 1.125rem;
  font-weight: 600;
  color: var(--text-primary);
  margin: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.mobile-header__left {
  min-width: 0; /* let the title truncate instead of pushing the page actions to wrap */
}

.mobile-header__right {
  display: flex;
  align-items: center;
  gap: var(--spacing-sm);
  flex-shrink: 0;
}

/* Page-actions slot: React portals compact page controls here (MobileHeaderActions,
   AppFrame's mobileHeaderActions prop). app--hdr-actions hides the avatar;
   app--hdr-compact (section screens only) also hides the bell. Opted-in views
   additionally server-render the avatar [hidden] to avoid a pre-mount flash. */
.mobile-header__page-actions {
  display: flex;
  align-items: center;
  gap: var(--spacing-sm);
}

.mobile-header__page-actions:empty {
  display: none;
}

.mobile-header__avatar[hidden],
.app--hdr-actions .mobile-header__avatar {
  display: none;
}

.app--hdr-compact .mobile-header__right [data-notification-bell] {
  display: none;
}

/* Page-nav slot: a section switch portaled beside the title (MobileHeaderActions'
   nav). When populated (app--hdr-nav) the title goes visually hidden — it stays
   in the DOM as the screen's h1 for assistive tech. */
.mobile-header__page-nav {
  display: flex;
  align-items: center;
  min-width: 0;
}

.mobile-header__page-nav:empty {
  display: none;
}

.app--hdr-nav .mobile-header__title {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

.mobile-header__action {
  position: relative;
  background: none;
  border: none;
  color: var(--text-secondary);
  cursor: pointer;
  padding: var(--spacing-xs);
  display: flex;
  align-items: center;
  justify-content: center;
}

/* display: flex above would defeat the hidden attribute. The bell relies on it:
   it is server-rendered [hidden] off top-level screens and toggled by
   NotificationBellIsland on unread-only screens (UIShell §7). */
.mobile-header__action[hidden],
[data-notification-bell][hidden] {
  display: none;
}

/* Touch-target floor: the header row is a fixed 40px (UIShell §7), so the
   32px icon buttons keep their visual size and extend only their hit area. */
.mobile-header__action::after {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  width: max(100%, 44px);
  height: max(100%, 44px);
  transform: translate(-50%, -50%);
}

.mobile-header__action svg {
  width: 24px;
  height: 24px;
}

.mobile-header__action:hover {
  color: var(--text-primary);
}

.notification-dot {
  position: absolute;
  top: 4px;
  right: 4px;
  width: 8px;
  height: 8px;
  /* Unread is a slate INFO dot, never ember — a notification is information,
     not an invitation ("Firewalker Color System" §04). */
  background: var(--color-info);
  border-radius: var(--radius-full);
}

.notification-dot[hidden] {
  display: none;
}

/* Main Content */
.main-content {
  flex: 1;
  overflow-y: auto;
  overflow-x: hidden;
  padding-bottom: calc(var(--bottom-nav-height) + var(--nav-inset-bottom));
  -webkit-overflow-scrolling: touch;
}

@media (min-width: 1024px) {
  .main-content {
    padding-bottom: 0;
  }
}
.app--hide-footer .site-footer {
  display: none;
}

/* Authenticated shell renders no site footer (links live in the account
   drawer/menu); zero the height var so shell height math stays honest. */
.app--no-footer {
  --site-footer-height: 0px;
}

.app--focused .main-content {
  padding-bottom: 0;
}

/* Focus views keep the area dock; only true conversation threads go immersive. */
.app--area-dock.app--focused .main-content {
  padding-bottom: calc(var(--bottom-nav-height) + var(--nav-inset-bottom));
}

.app--area-dock.app--focused.app--dating-thread .main-content {
  padding-bottom: 0;
}

@media (max-width: 1023.98px) {
  .app--area-dock .ws-mobile-nav {
    display: none;
  }
}
/* Page Layout */
.page__header {
  margin-bottom: var(--spacing-lg);
}

.page__header--with-actions {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--spacing-md);
}

.page__title {
  font-size: 1.5rem;
  font-weight: 700;
  margin: 0;
}

@media (max-width: 767.98px) {
  .page__title {
    font-size: 1.25rem;
  }
}
.page__subtitle {
  color: var(--text-secondary);
  margin-top: var(--spacing-xs);
}

.page__actions {
  display: flex;
  gap: var(--spacing-sm);
}

/* ============================================
   BUTTONS
   ============================================ */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--spacing-sm);
  padding: var(--spacing-sm) var(--spacing-md);
  border: none;
  border-radius: var(--radius-md);
  font-family: inherit;
  font-size: 1rem;
  font-weight: 600; /* matches React .btn (_verification.scss) */
  cursor: pointer;
  transition: all 150ms ease;
  text-decoration: none;
}

/* Turn 19 disabled: surface fill + ink at 34%, in EVERY variant and both
   themes — never a translucent accent (keeps the dark on-accent ink from
   going dark-on-dark the moment the ground is dark). */
.btn:disabled {
  background: var(--bg-dark-elevated);
  border-color: transparent;
  color: color-mix(in srgb, var(--text-primary) 34%, transparent);
  opacity: 1;
  cursor: not-allowed;
}

/* Approved primary ("Firewalker Color System" §03): the filled control takes
   the ramp's MIDPOINT flat — the base is never a solid area. Hover/pressed
   shift along the mid's own steps; the lift + warm glow stays. */
.btn--primary {
  background: var(--color-ember-mid);
  color: var(--color-on-primary);
}

.btn--primary:hover:not(:disabled) {
  background: var(--color-ember-mid-light);
  /* Re-assert the label color: the global a:hover (primary-light) outspecifies
     .btn--primary on anchor buttons and washed the label into the hover bg. */
  color: var(--color-on-primary);
  transform: translateY(-1px);
  box-shadow: 0 4px 12px color-mix(in srgb, var(--color-ember-mid) 40%, transparent);
}

.btn--primary:active:not(:disabled) {
  background: var(--color-ember-mid-dark);
  transform: translateY(0);
  box-shadow: none;
}

/* The CTA tier — .btn--primary.btn--lg carries the brand ramp (travel rule:
   the gradient runs ≥140px at this size). One per screen, two per page.
   background is a gradient image, so it swaps rather than fades — background
   deliberately leaves the transition list (gradient↔gradient never
   interpolates). Single-colour brand styles
   collapse --gradient-primary to a flat fill, so this rule is safe under
   every data-brand. */
.btn--primary.btn--lg {
  background: var(--gradient-primary);
  transition: transform 0.15s ease, box-shadow 0.15s ease, border-color 0.15s ease;
}

.btn--primary.btn--lg:hover:not(:disabled) {
  background: var(--gradient-primary);
  transform: translateY(-1px);
  box-shadow: 0 6px 20px color-mix(in srgb, var(--color-primary) 30%, transparent);
}

.btn--primary.btn--lg:active:not(:disabled) {
  background: var(--gradient-primary);
  transform: translateY(0);
  box-shadow: none;
}

.btn--secondary {
  background: var(--bg-dark-tertiary);
  color: var(--text-primary);
}

.btn--secondary:hover:not(:disabled) {
  background: var(--bg-dark-elevated);
}

/* Approved outline: 2px of the ramp BASE at 55% (base appears only as an
   outline, a text colour or a gradient start — never a solid area), label from
   the primary family's text step. */
.btn--outline {
  background: transparent;
  border: 2px solid color-mix(in srgb, var(--color-primary) 55%, transparent);
  color: var(--color-primary-text);
}

.btn--outline:hover:not(:disabled) {
  border-color: var(--color-primary);
  color: var(--color-primary-text);
}

/* Approved ghost: warm text only, fill appears on hover. */
.btn--ghost {
  background: transparent;
  color: var(--color-primary-text);
  font-weight: 500;
}

.btn--ghost:hover:not(:disabled) {
  background: var(--overlay-light);
  color: var(--color-primary-text);
}

/* Turn 19 destructive: outlined, not filled — "Leave challenge", not a red
   primary. Hover deepens into a light fill. */
.btn--danger {
  background: transparent;
  border: 1px solid color-mix(in srgb, var(--color-error) 45%, transparent);
  color: var(--color-error);
}

.btn--danger:hover:not(:disabled) {
  background: color-mix(in srgb, var(--color-error) 12%, transparent);
  border-color: color-mix(in srgb, var(--color-error) 65%, transparent);
}

.btn--lg {
  padding: var(--spacing-md) var(--spacing-lg);
  font-size: 1.125rem;
}

.btn--sm {
  padding: var(--spacing-xs) var(--spacing-sm);
  font-size: 0.875rem;
}

.btn--full {
  width: 100%;
}

.btn--icon {
  width: 44px;
  height: 44px;
  padding: 0;
  border-radius: var(--radius-full);
}

/* ============================================
   FORM ELEMENTS
   ============================================ */
.form-group {
  margin-bottom: var(--spacing-md);
}

.form-label {
  display: block;
  margin-bottom: var(--spacing-xs);
  font-size: 0.875rem;
  font-weight: 500;
  color: var(--text-secondary);
}

.form-input {
  width: 100%;
  padding: var(--spacing-md);
  background: var(--bg-dark-tertiary);
  border: 1px solid var(--overlay-medium);
  border-radius: var(--radius-md);
  font-family: inherit;
  font-size: 1rem;
  color: var(--text-primary);
  transition: all 150ms ease;
}

.form-input::placeholder {
  color: var(--text-muted);
}

.form-input:focus {
  outline: none;
  border-color: var(--color-primary);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--color-primary) 20%, transparent);
}

.form-input:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.form-error {
  color: var(--color-error);
  font-size: 0.875rem;
  margin-top: var(--spacing-xs);
}

.form-hint {
  color: var(--text-muted);
  font-size: 0.75rem;
  margin-top: var(--spacing-xs);
}

.form-checkbox {
  display: flex;
  align-items: center;
  gap: var(--spacing-sm);
  cursor: pointer;
}

.form-checkbox input[type=checkbox] {
  width: 20px;
  height: 20px;
  border: 2px solid var(--overlay-strong);
  border-radius: var(--radius-sm);
  background: transparent;
  cursor: pointer;
  appearance: none;
}

.form-checkbox input[type=checkbox]:checked {
  background: var(--color-primary);
  border-color: var(--color-primary);
  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='white' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20,6 9,17 4,12'%3E%3C/polyline%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: center;
}

.form-checkbox span {
  font-size: 0.875rem;
  color: var(--text-secondary);
}

/* ============================================
   CARDS
   ============================================ */
/* LOAD-BEARING for React pages despite zero Views usage: the vite-side .card
   (ClientApp/styles/_base.scss) declares no border, so React cards get their
   1px border from THIS rule via the cascade. Do not remove without porting. */
.card {
  background: var(--bg-dark-card);
  border-radius: var(--radius-lg);
  padding: var(--spacing-md);
  border: 1px solid var(--overlay-light);
}

.card--elevated {
  background: var(--bg-dark-elevated);
  box-shadow: 0 10px 15px rgba(0, 0, 0, 0.5);
}

.card--interactive {
  cursor: pointer;
  transition: all 150ms ease;
}

.card--interactive:hover {
  transform: translateY(-2px);
  border-color: var(--overlay-medium);
}

.card__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: var(--spacing-md);
}

.card__title {
  font-size: 1.125rem;
  font-weight: 600;
  color: var(--text-primary);
  margin: 0;
}

.card__body {
  color: var(--text-secondary);
}

.card__footer {
  margin-top: var(--spacing-md);
  padding-top: var(--spacing-md);
  border-top: 1px solid var(--overlay-light);
}

/* ============================================
   AVATARS
   ============================================ */
.avatar {
  width: 56px;
  height: 56px;
  border-radius: var(--radius-full);
  object-fit: cover;
  background: var(--bg-dark-tertiary);
  flex-shrink: 0;
  position: relative;
}

.avatar--xs {
  width: 32px;
  height: 32px;
}

.avatar--sm {
  width: 40px;
  height: 40px;
}

.avatar--md {
  width: 56px;
  height: 56px;
}

.avatar--lg {
  width: 80px;
  height: 80px;
}

.avatar--xl {
  width: 120px;
  height: 120px;
}

.avatar__placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  background: linear-gradient(135deg, var(--color-primary) 0%, var(--color-secondary) 100%);
  color: var(--text-primary);
  font-weight: 600;
}

.avatar__status {
  position: absolute;
  bottom: 2px;
  right: 2px;
  width: 12px;
  height: 12px;
  border-radius: var(--radius-full);
  border: 2px solid var(--bg-dark-card);
}

.avatar__status--online {
  background: var(--color-success);
}

.avatar__status--offline {
  background: var(--text-muted);
}

/* ============================================
   BADGES
   ============================================ */
.badge {
  display: inline-flex;
  align-items: center;
  gap: var(--spacing-xs);
  padding: var(--spacing-xs) var(--spacing-sm);
  border-radius: var(--radius-full);
  font-size: 0.75rem;
  font-weight: 500;
}

.badge--primary {
  background: color-mix(in srgb, var(--color-primary) 20%, transparent);
  color: var(--color-primary);
}

.badge--success {
  background: rgba(76, 175, 80, 0.2);
  color: var(--color-success);
}

.badge--warning {
  background: rgba(255, 152, 0, 0.2);
  color: var(--color-warning);
}

.badge--error {
  background: rgba(244, 67, 54, 0.2);
  color: var(--color-error);
}

.badge--neutral {
  background: var(--overlay-medium);
  color: var(--text-secondary);
}

.notification-badge {
  min-width: 18px;
  height: 18px;
  padding: 0 6px;
  background: var(--color-error);
  color: var(--text-primary);
  font-size: 11px;
  font-weight: 700;
  border-radius: var(--radius-full);
  display: flex;
  align-items: center;
  justify-content: center;
}

/* ============================================
   TABS
   ============================================ */
/* .tabs family removed 2026-07-27 — zero usage in Views; the React tab strip is
   fully styled by ClientApp/styles/_base.scss (this copy only bled a recessed
   container onto it). */

/* ============================================
   PROGRESS
   ============================================ */
.progress {
  height: 8px;
  background: var(--bg-dark-tertiary);
  border-radius: var(--radius-full);
  overflow: hidden;
}

.progress__bar {
  height: 100%;
  background: linear-gradient(135deg, var(--color-primary) 0%, var(--color-secondary) 100%);
  border-radius: var(--radius-full);
  transition: width 250ms ease;
}

.progress--lg {
  height: 12px;
}

/* ============================================
   ALERTS
   Toast-family look: elevated panel + colored left edge, explicit text color.
   Also styles the Razor auth pages' validation summaries (bare .alert).
   ============================================ */
.alert {
  padding: var(--spacing-md);
  border: 1px solid var(--overlay-medium);
  border-left-width: 4px;
  border-radius: var(--radius-md);
  display: flex;
  align-items: flex-start;
  gap: var(--spacing-sm);
  background: var(--bg-dark-elevated);
  color: var(--text-primary);
  font-size: 0.875rem;
  text-align: left;
}

/* asp-validation-summary renders an empty div when the model is valid — hide it */
.alert.validation-summary-valid {
  display: none;
}

/* display:flex above outranks the UA [hidden] rule — restate it so alerts that
   scripts hide via the hidden attribute (e.g. #passkey-error) actually disappear */
.alert[hidden] {
  display: none;
}

.alert ul {
  margin: 0;
  padding: 0;
  list-style: none;
}

.alert--success { border-left-color: var(--color-success); }
.alert--warning { border-left-color: var(--color-warning); }
.alert--error   { border-left-color: var(--color-error); }
.alert--info    { border-left-color: var(--color-info); }

/* .empty-state family removed 2026-07-27 — zero usage in Views; styled by
   ClientApp/styles/_controls.scss for React pages. */

/* ============================================
   PERSON CARD (Discovery)
   ============================================ */
.person-card {
  background: var(--bg-dark-card);
  border-radius: var(--radius-lg);
  padding: var(--spacing-md);
  border: 1px solid var(--overlay-light);
  cursor: pointer;
  transition: all 150ms ease;
}

.person-card:hover {
  transform: translateY(-2px);
  border-color: var(--overlay-medium);
}

.person-card__header {
  display: flex;
  gap: var(--spacing-md);
  margin-bottom: var(--spacing-md);
}

.person-card__avatar {
  width: 80px;
  height: 80px;
  border-radius: var(--radius-full);
  object-fit: cover;
  background: var(--bg-dark-tertiary);
}

.person-card__info {
  flex: 1;
  min-width: 0;
}

.person-card__name {
  font-size: 1.125rem;
  font-weight: 600;
  color: var(--text-primary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.person-card__location {
  font-size: 0.875rem;
  color: var(--text-secondary);
  display: flex;
  align-items: center;
  gap: var(--spacing-xs);
  margin-top: var(--spacing-xs);
}

.person-card__location svg {
  width: 14px;
  height: 14px;
  opacity: 0.6;
}

.person-card__bio {
  font-size: 0.875rem;
  color: var(--text-secondary);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  margin-bottom: var(--spacing-md);
}

.person-card__interests {
  display: flex;
  flex-wrap: wrap;
  gap: var(--spacing-xs);
}

.person-card__interest {
  display: inline-flex;
  align-items: center;
  gap: var(--spacing-xs);
  padding: var(--spacing-xs) var(--spacing-sm);
  border-radius: var(--radius-full);
  font-size: 0.75rem;
  font-weight: 500;
  background: color-mix(in srgb, var(--color-primary) 10%, transparent);
  color: var(--color-primary-light);
}

.person-card__footer {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-top: var(--spacing-md);
  padding-top: var(--spacing-md);
  border-top: 1px solid var(--overlay-light);
}

.person-card__stats {
  display: flex;
  gap: var(--spacing-md);
  font-size: 0.75rem;
  color: var(--text-muted);
}

.person-card__actions {
  display: flex;
  gap: var(--spacing-sm);
}

/* .challenge-card family removed 2026-07-27 — zero usage in Views; the React
   challenge cards are styled by ClientApp/styles/_controls.scss + _challenges.scss
   (this legacy copy bled margins/borders onto them). */

/* ============================================
   CONVERSATION ITEM (Messages)
   ============================================ */
.conversation-item {
  display: flex;
  align-items: center;
  gap: var(--spacing-md);
  padding: var(--spacing-md);
  cursor: pointer;
  transition: background 150ms ease;
  border-bottom: 1px solid var(--overlay-light);
}

.conversation-item:hover {
  background: var(--overlay-subtle);
}

.conversation-item--unread {
  background: color-mix(in srgb, var(--color-primary) 5%, transparent);
}

.conversation-item--unread .conversation-item__preview {
  color: var(--text-primary);
  font-weight: 500;
}

.conversation-item--active {
  background: color-mix(in srgb, var(--color-primary) 10%, transparent);
}

.conversation-item__avatar {
  position: relative;
  flex-shrink: 0;
}

.conversation-item__content {
  flex: 1;
  min-width: 0;
}

.conversation-item__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: var(--spacing-xs);
}

.conversation-item__name {
  font-weight: 500;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.conversation-item__time {
  font-size: 0.75rem;
  color: var(--text-muted);
  flex-shrink: 0;
}

.conversation-item__preview {
  font-size: 0.875rem;
  color: var(--text-secondary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ============================================
   MESSAGE BUBBLE (Chat)
   ============================================ */
/* .message-bubble family removed 2026-07-27 — zero usage in Views; React chat
   bubbles are styled by ClientApp/styles/_messages.scss (chrome lives on
   .message-bubble__body — this copy bled padding onto the flex wrapper). */

/* ============================================
   PROFILE HEADER
   ============================================ */
.profile-header {
  position: relative;
  margin-bottom: var(--spacing-lg);
}

.profile-header__cover {
  height: 120px;
  /* The approved 3-point ramp along the cover diagonal (was an off-system
     --color-accent sweep). All three stops pin per brand style, so it
     collapses cleanly under the single-colour styles. */
  background: linear-gradient(135deg, var(--color-primary) 0%, var(--color-ember-mid) 50%, var(--color-secondary) 100%);
  border-radius: var(--radius-lg);
}

@media (min-width: 768px) {
  .profile-header__cover {
    height: 180px;
  }
}
.profile-header__content {
  display: flex;
  flex-direction: column;
  align-items: center;
  margin-top: -40px;
  text-align: center;
}

@media (min-width: 768px) {
  .profile-header__content {
    flex-direction: row;
    align-items: flex-end;
    margin-top: -40px;
    padding: 0 var(--spacing-lg);
    text-align: left;
  }
}
.profile-header__avatar {
  width: 80px;
  height: 80px;
  border-radius: var(--radius-full);
  border: 4px solid var(--bg-dark);
  object-fit: cover;
  background: var(--bg-dark-tertiary);
}

@media (min-width: 768px) {
  .profile-header__avatar {
    width: 120px;
    height: 120px;
  }
}
.profile-header__info {
  margin-top: var(--spacing-md);
}

@media (min-width: 768px) {
  .profile-header__info {
    margin-top: 0;
    margin-left: var(--spacing-lg);
    flex: 1;
  }
}
.profile-header__name {
  font-size: 1.5rem;
  font-weight: 700;
  margin: 0;
}

.profile-header__handle {
  color: var(--text-secondary);
  margin-top: var(--spacing-xs);
}

.profile-header__actions {
  margin-top: var(--spacing-md);
  display: flex;
  gap: var(--spacing-sm);
}

@media (min-width: 768px) {
  .profile-header__actions {
    margin-top: 0;
  }
}
.profile-stats {
  display: flex;
  justify-content: center;
  gap: var(--spacing-xl);
  padding: var(--spacing-lg);
  background: var(--bg-dark-card);
  border-radius: var(--radius-lg);
  border: 1px solid var(--overlay-light);
}

@media (min-width: 768px) {
  .profile-stats {
    justify-content: flex-start;
  }
}
.profile-stats__item {
  text-align: center;
}

.profile-stats__value {
  font-size: 1.5rem;
  font-weight: 700;
  color: var(--text-primary);
}

.profile-stats__label {
  font-size: 0.875rem;
  color: var(--text-secondary);
  margin-top: var(--spacing-xs);
}

/* ============================================
   UTILITY CLASSES
   ============================================ */
.text-center {
  text-align: center;
}

.text-left {
  text-align: left;
}

.text-right {
  text-align: right;
}

.text-primary {
  color: var(--text-primary) !important;
}

.text-secondary {
  color: var(--text-secondary) !important;
}

.text-muted {
  color: var(--text-muted) !important;
}

.text-accent {
  color: var(--color-primary) !important;
}

.text-success {
  color: var(--color-success) !important;
}

.text-warning {
  color: var(--color-warning) !important;
}

.text-error {
  color: var(--color-error) !important;
}

.text-sm {
  font-size: 0.875rem;
}

/* .text-primary-color removed 2026-07-27 — zero usage in Views; the React copy
   (no !important) in _verification.scss now applies uncontested. */

.d-none {
  display: none !important;
}

.d-block {
  display: block !important;
}

.d-flex {
  display: flex !important;
}

.d-grid {
  display: grid !important;
}

.flex-column {
  flex-direction: column;
}

.justify-center {
  justify-content: center;
}

.justify-between {
  justify-content: space-between;
}

.gap-sm {
  gap: var(--spacing-sm);
}

.gap-md {
  gap: var(--spacing-md);
}

.gap-lg {
  gap: var(--spacing-lg);
}

.w-100 {
  width: 100%;
}

.mt-auto {
  margin-top: auto;
}

.mt-xs {
  margin-top: var(--spacing-xs);
}

.mt-sm {
  margin-top: var(--spacing-sm);
}

.mt-md {
  margin-top: var(--spacing-md);
}

.mt-lg {
  margin-top: var(--spacing-lg);
}

.mb-xs {
  margin-bottom: var(--spacing-xs);
}

.mb-sm {
  margin-bottom: var(--spacing-sm);
}

.mb-md {
  margin-bottom: var(--spacing-md);
}

.mb-lg {
  margin-bottom: var(--spacing-lg);
}

.ml-sm {
  margin-left: var(--spacing-sm);
}

.mr-sm {
  margin-right: var(--spacing-sm);
}

.p-md {
  padding: var(--spacing-md);
}

.p-sm {
  padding: var(--spacing-sm);
}

.rounded {
  border-radius: var(--radius-md);
}

.rounded-full {
  border-radius: var(--radius-full);
}

@media (max-width: 767.98px) {
  .hide-mobile {
    display: none !important;
  }
}
@media (min-width: 768px) {
  .hide-tablet-up {
    display: none !important;
  }
}
@media (min-width: 1024px) {
  .hide-desktop {
    display: none !important;
  }
}
/* ============================================
   GRID LAYOUT
   ============================================ */
.grid {
  display: grid;
  gap: var(--spacing-md);
}

.grid--responsive {
  grid-template-columns: 1fr;
}

@media (min-width: 768px) {
  .grid--responsive {
    grid-template-columns: repeat(2, 1fr);
  }
}
@media (min-width: 1024px) {
  .grid--responsive {
    grid-template-columns: repeat(3, 1fr);
  }
}
/* ============================================
   SECTION
   ============================================ */
/* .section family removed 2026-07-27 — zero usage in Views; the React <Section>
   is styled by ClientApp/styles/_section.scss (margin-bottom ported there). */

/* inbox-tabs and chat-input-group removed 2026-06-18 — confirmed zero usage in
   Views + ClientApp (messaging chat UI is React-driven from styles.scss). */
/* ============================================
   SEARCH BAR
   ============================================ */
.search-bar {
  display: flex;
  gap: var(--spacing-sm);
  margin-bottom: var(--spacing-lg);
}

.search-bar__input {
  flex: 1;
  padding: var(--spacing-md);
  padding-left: 48px;
  background: var(--bg-dark-tertiary);
  border: 1px solid var(--overlay-medium);
  border-radius: var(--radius-md);
  font-family: inherit;
  font-size: 1rem;
  color: var(--text-primary);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 24 24' fill='none' stroke='rgba(255,255,255,0.4)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='11' cy='11' r='8'%3E%3C/circle%3E%3Cline x1='21' y1='21' x2='16.65' y2='16.65'%3E%3C/line%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: 16px center;
}

.search-bar__input::placeholder {
  color: var(--text-muted);
}

.search-bar__input:focus {
  outline: none;
  border-color: var(--color-primary);
}

.search-bar__filter {
  padding: var(--spacing-md);
  background: var(--bg-dark-tertiary);
  border: 1px solid var(--overlay-medium);
  border-radius: var(--radius-md);
  color: var(--text-secondary);
  cursor: pointer;
}

/* Filter chips */
.filter-chips {
  display: flex;
  gap: var(--spacing-sm);
  overflow-x: auto;
  margin-bottom: var(--spacing-lg);
  padding-bottom: var(--spacing-sm);
  scrollbar-width: none; /* hidden swipe strip — opt out of the global thin bar (Firefox) */
}

.filter-chips::-webkit-scrollbar {
  height: 0; /* …and WebKit */
}

.filter-chip {
  padding: var(--spacing-xs) var(--spacing-md);
  background: var(--bg-dark-tertiary);
  color: var(--text-secondary);
  border-radius: var(--radius-full);
  white-space: nowrap;
  font-size: 0.875rem;
  font-weight: 500;
  cursor: pointer;
  border: none;
  transition: all 150ms ease;
}

.filter-chip--active {
  background: color-mix(in srgb, var(--color-primary) 20%, transparent);
  color: var(--color-primary);
}

/* ============================================
   SWIPE VIEW (Dating mode)
   ============================================ */
.swipe-view {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: var(--spacing-md);
  min-height: calc(100vh - var(--navbar-height) - var(--bottom-nav-height));
  min-height: calc(100dvh - var(--navbar-height) - var(--bottom-nav-height));
}

@media (min-width: 1024px) {
  .swipe-view {
    min-height: calc(100vh - var(--navbar-height));
    min-height: calc(100dvh - var(--navbar-height));
  }
}
.swipe-view__cards {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  max-width: 400px;
  position: relative;
}

.swipe-card {
  position: relative;
  width: 100%;
  max-width: 360px;
  aspect-ratio: 3/4;
  border-radius: var(--radius-xl);
  overflow: hidden;
  background: var(--bg-dark-card);
  box-shadow: 0 20px 25px rgba(0, 0, 0, 0.6);
}

.swipe-card__image {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.swipe-card__gradient {
  position: absolute;
  inset: 0;
  background: linear-gradient(to top, rgba(0, 0, 0, 0.9) 0%, rgba(0, 0, 0, 0.5) 30%, transparent 60%);
}

.swipe-card__content {
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  padding: var(--spacing-lg);
}

.swipe-card__name {
  font-size: 1.5rem;
  font-weight: 700;
  margin-bottom: var(--spacing-xs);
}

.swipe-card__info {
  font-size: 1rem;
  color: var(--text-secondary);
  margin-bottom: var(--spacing-sm);
}

.swipe-card__tags {
  display: flex;
  flex-wrap: wrap;
  gap: var(--spacing-xs);
}

.swipe-actions {
  display: flex;
  justify-content: center;
  gap: var(--spacing-lg);
  padding: var(--spacing-lg);
}

.swipe-actions__btn {
  width: 64px;
  height: 64px;
  border-radius: var(--radius-full);
  display: flex;
  align-items: center;
  justify-content: center;
  transition: all 150ms ease;
  border: none;
  cursor: pointer;
}

.swipe-actions__btn--pass {
  background: var(--bg-dark-tertiary);
  color: var(--text-secondary);
}

.swipe-actions__btn--pass:hover {
  background: rgba(244, 67, 54, 0.2);
  color: var(--color-error);
}

.swipe-actions__btn--connect {
  background: linear-gradient(135deg, var(--color-primary) 0%, var(--color-secondary) 100%);
  color: var(--text-primary);
}

.swipe-actions__btn--connect:hover {
  transform: scale(1.1);
  box-shadow: 0 0 20px color-mix(in srgb, var(--color-primary) 30%, transparent);
}

.swipe-actions__btn--super {
  background: var(--bg-dark-tertiary);
  color: var(--color-secondary);
}

.swipe-actions__btn--super:hover {
  background: color-mix(in srgb, var(--color-secondary) 20%, transparent);
}

.swipe-actions__btn svg {
  width: 28px;
  height: 28px;
}

/* ============================================
   AUTH LAYOUT & CARD
   ============================================ */
/* Inside the public-chrome shell the header and pinned footer are flex rows of
   .app, so the auth boxes fill only the scroll area between them — a 100dvh box
   there would force a needless scrollbar. Height chain: .app (100dvh) →
   .main-content--public (flex: 1) → these. */
.main-content--public {
  display: flex;
  flex-direction: column;
  /* .main-content reserves bottom-nav clearance below 1024px, but anonymous pages
     render no bottom nav — the reserve left a 64px dead gap under the in-flow footer. */
  padding-bottom: 0;
}

/* Children of the scrollable flex column keep their natural height. Without this,
   default flex-shrink squishes tall content (e.g. /join's #react-root) to the
   container height — the content still paints (overflow) but the in-flow footer is
   laid out right after the SHRUNK box, i.e. floating mid-page over the content.
   The auth fillers below re-opt into flex sizing at higher specificity. */
.main-content--public > * {
  flex-shrink: 0;
}

.main-content--public > .app-container {
  flex: 1;
  display: flex;
  flex-direction: column;
}

/* Same natural-height guard one level down: .app-container is also a flex column,
   so tall page content (e.g. /join's #react-root) must not shrink to fit it. The
   .auth-layout/.auth-shell rules below re-opt into filling (same specificity tier,
   later in source — keep them below this rule). */
.main-content--public > .app-container > * {
  flex-shrink: 0;
}

.main-content--public .auth-layout,
.main-content--public .auth-shell {
  flex: 1;
  min-height: 0;
}

.auth-layout {
  min-height: 100vh;
  min-height: 100dvh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--spacing-lg);
  background: linear-gradient(135deg, var(--bg-dark) 0%, var(--bg-dark-secondary) 100%);
}

.auth-card {
  width: 100%;
  max-width: 420px;
  background: var(--bg-dark-card);
  border-radius: var(--radius-lg);
  padding: var(--spacing-xl);
  border: 1px solid var(--overlay-light);
}

.auth-card__header {
  text-align: center;
  margin-bottom: var(--spacing-xl);
}

.auth-card__logo {
  display: inline-flex;
  align-items: center;
  gap: var(--spacing-sm);
  text-decoration: none;
  margin-bottom: var(--spacing-lg);
}

.auth-card__logo-icon {
  width: 2rem;
  height: 2rem;
}

.auth-card__logo .brand-word {
  height: 1.4rem; /* cap ≈ 12.4px */
}

.auth-card__title {
  font-size: 1.5rem;
  font-weight: 700;
  color: var(--text-primary);
  margin-bottom: var(--spacing-xs);
}

.auth-card__subtitle {
  color: var(--text-secondary);
  font-size: 0.875rem;
}

.auth-form {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-md);
}

/* .form-row removed 2026-07-27 — zero usage in Views; ClientApp/styles/_verification.scss
   carries the identical grid for React pages. */
.form-group {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-xs);
}

.form-label {
  font-size: 0.875rem;
  font-weight: 500;
  color: var(--text-secondary);
}

.form-input {
  padding: var(--spacing-sm) var(--spacing-md);
  background: var(--bg-dark-tertiary);
  border: 1px solid var(--overlay-medium);
  border-radius: var(--radius-sm);
  color: var(--text-primary);
  font-size: 1rem;
  transition: all 150ms ease;
}

.form-input::placeholder {
  color: var(--text-muted);
}

.form-input:focus {
  outline: none;
  border-color: var(--color-primary);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--color-primary) 10%, transparent);
}

.form-error {
  font-size: 0.75rem;
  color: var(--color-error);
}

.form-hint {
  font-size: 0.75rem;
  color: var(--text-muted);
}

.form-checkbox {
  display: flex;
  align-items: center;
  gap: var(--spacing-sm);
  cursor: pointer;
  font-size: 0.875rem;
  color: var(--text-secondary);
}

.form-checkbox input[type=checkbox] {
  width: 18px;
  height: 18px;
  accent-color: var(--color-primary);
}

.form-terms {
  font-size: 0.75rem;
  color: var(--text-muted);
  text-align: center;
  margin-top: var(--spacing-sm);
}

/* Links take the ramp midpoint + underline (approved §02: flat = ember mid). */
.form-terms a {
  color: var(--color-ember-mid);
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-color: color-mix(in srgb, var(--color-ember-mid) 50%, transparent);
}

.form-terms a:hover {
  text-decoration-color: var(--color-ember-mid);
}

.auth-card__footer {
  margin-top: var(--spacing-lg);
  padding-top: var(--spacing-lg);
  border-top: 1px solid var(--overlay-light);
  text-align: center;
  font-size: 0.875rem;
  color: var(--text-secondary);
}

.auth-link {
  color: var(--color-ember-mid);
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-color: color-mix(in srgb, var(--color-ember-mid) 50%, transparent);
  font-weight: 500;
}

.auth-link:hover {
  text-decoration-color: var(--color-ember-mid);
}

/* Login form modes: emailed sign-in link first; password fields swap in when the
   form carries .auth-form--password (server-set after a failed password attempt,
   otherwise toggled by login-form.js). */
.auth-form__magic {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-sm);
}

.auth-form__password {
  display: none;
}

.auth-form--password .auth-form__magic {
  display: none;
}

.auth-form--password .auth-form__password {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-md);
}

.auth-form__options {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--spacing-md);
}

.auth-divider {
  display: flex;
  align-items: center;
  gap: var(--spacing-md);
  color: var(--text-muted);
  font-size: 0.75rem;
}

.auth-divider::before,
.auth-divider::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--overlay-medium);
}

.auth-toggle {
  background: none;
  border: none;
  padding: 0;
  font-family: inherit;
  font-size: 0.875rem;
  font-weight: 500;
  color: var(--color-primary);
  cursor: pointer;
  align-self: center;
  text-decoration: none; /* also used on <a> (e.g. "Use a different email") */
  text-align: center;
}

.auth-toggle:hover {
  text-decoration: underline;
}

.form-input-icon {
  position: relative;
}

.form-input-icon > svg {
  position: absolute;
  left: var(--spacing-md);
  top: 50%;
  transform: translateY(-50%);
  color: var(--text-muted);
  pointer-events: none;
}

.form-input-icon .form-input {
  width: 100%;
  padding-left: calc(var(--spacing-md) * 2 + 18px);
}

.form-hint--center {
  text-align: center;
  margin: 0;
}

.form-hint--icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
}

/* Passkey CTA: brand-outlined quiet button (login card). */
.btn--passkey {
  background: transparent;
  border: 1px solid color-mix(in srgb, var(--color-primary) 45%, transparent);
  color: var(--text-primary);
}

.btn--passkey:hover:not(:disabled) {
  background: color-mix(in srgb, var(--color-primary) 8%, transparent);
  border-color: var(--color-primary);
}

/* ============================================
   AUTH SHELL (login page)
   Branded split layout on desktop — hero copy left, card right. Chrome (nav +
   footer) comes from the shared public marketing shell (_PublicHeader /
   _SiteFooter via _Layout); the shell renders full-bleed inside it
   (ViewData["PublicFullBleed"]). Below 1024px it collapses to the focused
   centered card.
   ============================================ */
.auth-shell {
  min-height: 100vh;
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  background:
    radial-gradient(55% 45% at 12% 88%, color-mix(in srgb, var(--color-primary) 12%, transparent), transparent 70%),
    linear-gradient(135deg, var(--bg-dark) 0%, var(--bg-dark-secondary) 100%);
}

.auth-shell__content {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--spacing-lg);
}

.auth-hero {
  display: none;
}

@media (min-width: 1024px) {
  .auth-shell__content {
    width: 100%;
    max-width: 1280px;
    margin: 0 auto;
    display: grid;
    grid-template-columns: 1.15fr 1fr;
    gap: var(--spacing-xl);
    align-items: center;
    padding: var(--spacing-xl);
  }

  .auth-hero {
    position: relative;
    align-self: stretch;
    display: flex;
    flex-direction: column;
    justify-content: center;
    padding: var(--spacing-xl);
    overflow: hidden;
    min-height: 480px;
  }

  .auth-hero__flame {
    position: absolute;
    left: -18%;
    top: 50%;
    transform: translateY(-50%);
    width: 36rem;
    height: 36rem;
    color: var(--color-primary);
    opacity: 0.1;
    pointer-events: none;
  }

  .auth-hero__title {
    position: relative;
    font-size: clamp(2.5rem, 4.5vw, 3.75rem);
    font-weight: 700;
    line-height: 1.1;
    color: var(--text-primary);
    margin-bottom: var(--spacing-md);
  }

  .auth-hero__accent {
    color: var(--color-primary);
  }

  .auth-hero__subtitle {
    position: relative;
    font-size: 1.25rem;
    line-height: 1.5;
    color: var(--text-secondary);
    max-width: 32ch;
  }

  /* The shared public header owns branding + the join CTA on desktop — drop the card's copy. */
  .auth-shell .auth-card__footer {
    display: none;
  }

  .auth-shell .auth-card {
    justify-self: center;
    max-width: 460px;
    padding: var(--spacing-xl);
  }
}

/* ============================================
   UTILITIES
   ============================================ */
/* home-dashboard / home-hero / features-grid / feature-card / activity-feed and
   fw-semibold removed 2026-06-18 — confirmed zero usage in Views + ClientApp
   (superseded by the .landing-* set). .mb-lg is still used (registration). */
.mb-lg {
  margin-bottom: var(--spacing-lg);
}

/* ============================================
   SITE FOOTER (in-flow at every width)
   ============================================
   The footer always renders in flow and scrolls with the content — no pinned
   instance at any width. The old desktop-only pinned bar sat as a permanent
   .app flex row, slicing across long marketing scrolls ("footer in the middle
   of the page") and eating 89px of viewport; retired 2026-08-09. Two embeddings
   share this base: public-chrome pages get it via the .site-footer-flow wrapper
   inside .main-content--public (_Layout), self-chromed pages (landing .lp,
   /dating + /Community intros, public challenge shares) embed the bare partial
   at the end of their own markup. */
.site-footer {
  display: flex;
  padding: var(--spacing-md) 0;
  align-items: center;
  background: var(--bg-dark-secondary);
  border-top: 1px solid var(--overlay-light);
  margin-top: auto;
}

.site-footer-flow {
  display: block;
  margin-top: auto; /* short pages (auth shell): sit at the bottom, not mid-screen */
}

.site-footer__inner {
  width: 100%;
  max-width: 1200px;
  margin: 0 auto;
  padding: 0 var(--spacing-lg);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--spacing-md);
}

@media (min-width: 768px) {
  .site-footer__inner {
    flex-direction: row;
    justify-content: space-between;
    align-items: center;
  }
}
.site-footer__links {
  display: flex;
  gap: var(--spacing-lg);
  flex-wrap: wrap; /* narrow widths: links wrap instead of overflowing */
  justify-content: center;
  row-gap: var(--spacing-sm);
}

.site-footer__link {
  font-size: 0.875rem;
  color: var(--text-secondary);
  text-decoration: none;
  transition: color 150ms ease;
}

.site-footer__link:hover {
  color: var(--text-primary);
}

.site-footer__copyright {
  font-size: 0.75rem;
  color: var(--text-muted);
  margin: 0;
}

/* ----- Legal / informational pages (Terms, Privacy, About, Help) ----- */
/* NOTE (split candidate): .legal-page/.contact-form serve only the public
   legal/support pages (~6KB raw, ~1.2KB gzip today). Not worth conditional-
   stylesheet machinery yet — these pages are reachable both anonymous and
   authenticated, so a per-view flag would have to cover every consumer incl.
   error pages. Revisit if this block keeps growing. */
.legal-page {
  max-width: 760px;
  margin: 0 auto;
  padding: var(--spacing-xl) var(--spacing-md) var(--spacing-2xl);
  line-height: 1.65;
}

.legal-page__head {
  margin-bottom: var(--spacing-xl);
}

.legal-page__head h1 {
  margin-bottom: var(--spacing-xs);
}

.legal-page__meta {
  font-size: 0.875rem;
  color: var(--text-tertiary);
  margin: 0;
}

.legal-page h2 {
  font-size: 1.375rem;
  margin-top: var(--spacing-xl);
  padding-top: var(--spacing-md);
  border-top: 1px solid var(--border-subtle);
}

.legal-page h3 {
  font-size: 1.0625rem;
  color: var(--text-primary);
  margin-top: var(--spacing-lg);
  margin-bottom: var(--spacing-xs);
}

.legal-page p,
.legal-page li {
  color: var(--text-secondary);
}

.legal-page ul,
.legal-page ol {
  margin: 0 0 var(--spacing-md);
  padding-left: var(--spacing-lg);
}

.legal-page li {
  margin-bottom: var(--spacing-xs);
}

/* Table of contents card */
.legal-page__toc {
  background: var(--bg-card);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg);
  padding: var(--spacing-md) var(--spacing-lg);
  margin: var(--spacing-xl) 0;
}

.legal-page__toc h2 {
  /* Eyebrow treatment (Type Decision v1.0) — an uppercase marker, not a title */
  font-size: 0.75rem;
  margin: 0 0 var(--spacing-sm);
  padding: 0;
  border-top: none;
  color: var(--text-tertiary);
  text-transform: uppercase;
  letter-spacing: var(--fw-track-eyebrow);
}

.legal-page__toc ol {
  margin: 0;
  columns: 2;
  column-gap: var(--spacing-lg);
}

@media (max-width: 600px) {
  .legal-page__toc ol {
    columns: 1;
  }
}
.legal-page__toc a {
  color: var(--text-secondary);
}

.legal-page__toc a:hover {
  color: var(--color-primary);
}

.legal-page__contact {
  background: var(--bg-card);
  border-radius: var(--radius-md);
  padding: var(--spacing-md);
}

.legal-page__callout {
  background: var(--bg-card);
  border: 1px solid var(--border-subtle);
  border-left: 3px solid var(--color-primary);
  border-radius: var(--radius-md);
  padding: var(--spacing-md) var(--spacing-lg);
  margin: var(--spacing-lg) 0;
  color: var(--text-secondary);
}

.legal-page__callout strong {
  color: var(--text-primary);
}

.legal-page__footnote {
  margin-top: var(--spacing-2xl);
  padding-top: var(--spacing-md);
  border-top: 1px solid var(--border-subtle);
  font-size: 0.875rem;
  color: var(--text-tertiary);
}

/* Closing apply card for informational pages (About) — logged-out visitors only */
.legal-page__cta {
  background: var(--bg-card);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg);
  padding: var(--spacing-xl) var(--spacing-lg);
  margin-top: var(--spacing-2xl);
  text-align: center;
}

.legal-page__cta h2 {
  margin: 0 0 var(--spacing-md);
  padding-top: 0;
  border-top: none;
}

.legal-page__cta-note {
  margin: var(--spacing-sm) 0 0;
  font-size: 0.875rem;
  color: var(--text-tertiary);
}

/* Contact form (Views/Home/Contact.cshtml) — legal-page family */
.contact-form {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-sm);
  margin: var(--spacing-lg) 0 var(--spacing-xl);
}

.contact-form__row {
  display: grid;
  gap: var(--spacing-sm);
}

@media (min-width: 640px) {
  .contact-form__row {
    grid-template-columns: 1fr 1fr;
    column-gap: var(--spacing-md);
  }
}

.contact-form__optional {
  color: var(--text-tertiary);
  font-weight: 400;
}

.contact-form textarea.form-input {
  min-height: 160px;
  resize: vertical;
}

.contact-form .btn {
  align-self: flex-start;
}

/* Honeypot: moved off-canvas rather than display:none so naive bots still "see" it. */
.contact-form__hp {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

/* Offset in-page anchor targets so headings clear the sticky header */
.legal-page section[id],
.legal-page [id] {
  scroll-margin-top: 80px;
}

/* ============================================
   LANDING PAGE STYLES
   ============================================ */
/* Landing page wrapper */
.landing-page {
  background: var(--bg-dark);
  color: var(--text-primary);
}

/* Shared public marketing header (_PublicHeader.cshtml). Pinned dark regardless of
   theme — the whole public funnel (landing → /join → login) shares one nav look.
   Bar/logo metrics mirror the landing's .lp-nav (ClientApp/styles/_landing.scss);
   keep the two in sync. The CTAs are the stock .btn system on purpose — no re-skin,
   no smolder: hover effects that haze the label are unreadable at nav-button size. */
.landing-header {
  display: block;
  position: sticky;
  top: 0;
  z-index: 100;
  background: #0b0a08;
  border-bottom: 1px solid rgba(255, 235, 210, 0.08);
}

.landing-header .header__inner {
  /* Two children, not three (brand + CTAs, no centre nav), so the app header's
     1fr/auto/1fr grid would drop the CTAs into the middle track. Back to the
     plain two-ended bar here. */
  display: flex;
  justify-content: space-between;
  max-width: 1160px;
  height: 64px;
  padding: 0 var(--spacing-lg);
}

.landing-header .header__logo {
  font-size: 1.125rem;
  color: #f5f1ea;
}

.landing-header .header__logo-icon {
  /* Matches the app nav's 32px — same lockup, same 24px floor argument. */
  width: 32px;
  height: 32px;
}

/* Nav CTAs mirror the landing nav's skins (.lp-btn--sm ghost/primary in
   _lp-core.scss) so the whole public funnel shares one nav look. Literals on
   purpose — the funnel is pinned dark (see docs/product/LANDING_CRO.md). */
.landing-header .btn {
  font-size: 0.875rem;
  padding: 0.6em 1.2em;
  border-radius: 6px; /* $lp-radius-sm */
  line-height: 1;
}

.landing-header .btn--outline {
  border: 1px solid rgba(245, 241, 234, 0.22);
  color: #f5f1ea; /* $lp-text-hi */
}

.landing-header .btn--outline:hover:not(:disabled) {
  border-color: rgba(245, 241, 234, 0.45);
  background: rgba(245, 241, 234, 0.06);
  color: #f5f1ea;
}

@media (max-width: 767.98px) {
  /* The app shell hides the wordmark on mobile; the marketing nav keeps it, like .lp-nav. */
  .landing-header .header__logo .brand-word {
    display: inline-block;
  }
}

@media (max-width: 479.98px) {
  /* Below 480px the wordmark plus two nav CTAs overflow the content box —
     icon-only lockup, mirroring .lp-nav's fix (_landing.scss). */
  .landing-header .header__logo .brand-word {
    display: none;
  }
}

/* Hero Section */
.landing-hero {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--spacing-xl);
  padding: var(--spacing-xl) var(--spacing-md);
  min-height: 80vh;
  align-items: center;
  max-width: 1200px;
  margin: 0 auto;
}

@media (min-width: 768px) {
  .landing-hero {
    padding: 60px var(--spacing-lg);
  }
}
@media (min-width: 1024px) {
  .landing-hero {
    grid-template-columns: 1fr 1fr;
    padding: 80px var(--spacing-xl);
    gap: 60px;
  }
}
.landing-hero__badge {
  display: inline-flex;
  align-items: center;
  gap: var(--spacing-sm);
  padding: var(--spacing-xs) var(--spacing-md);
  background: color-mix(in srgb, var(--color-primary) 10%, transparent);
  border: 1px solid color-mix(in srgb, var(--color-primary) 30%, transparent);
  border-radius: var(--radius-full);
  font-size: 0.875rem;
  color: var(--color-primary);
  margin-bottom: var(--spacing-lg);
}

.landing-hero__badge-icon {
  font-size: 1rem;
}

.landing-hero__title {
  font-size: 2.25rem;
  line-height: 1.15;
  margin: 0 0 var(--spacing-lg);
  font-weight: 700;
}

@media (min-width: 768px) {
  .landing-hero__title {
    font-size: 3rem;
  }
}
@media (min-width: 1024px) {
  .landing-hero__title {
    font-size: 3.5rem;
  }
}
.landing-hero__title .highlight {
  background: linear-gradient(135deg, var(--color-primary) 0%, var(--color-secondary) 100%);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
}

.landing-hero__subtitle {
  font-size: 1.125rem;
  color: var(--text-secondary);
  max-width: 500px;
  margin: 0 0 var(--spacing-xl);
  line-height: 1.6;
}

.landing-hero__cta {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-sm);
}

@media (min-width: 480px) {
  .landing-hero__cta {
    flex-direction: row;
    align-items: center;
  }
}
.landing-hero__cta-note {
  font-size: 0.875rem;
  color: var(--text-muted);
  margin: var(--spacing-sm) 0 0;
}

@media (min-width: 480px) {
  .landing-hero__cta-note {
    margin: 0 0 0 var(--spacing-md);
  }
}
.landing-hero__social-proof {
  display: flex;
  align-items: center;
  gap: var(--spacing-md);
  margin-top: var(--spacing-xl);
}

.landing-hero__avatars {
  display: flex;
}

.landing-hero__avatars img {
  width: 36px;
  height: 36px;
  border-radius: var(--radius-full);
  border: 2px solid var(--bg-dark);
  margin-left: -12px;
  object-fit: cover;
  background: var(--bg-dark-tertiary);
}

.landing-hero__avatars img:first-child {
  margin-left: 0;
}

.landing-hero__members-text {
  font-size: 0.875rem;
  color: var(--text-secondary);
  margin: 0;
}

.landing-hero__members-text strong {
  color: var(--text-primary);
}

.landing-hero__visual {
  display: none;
}

@media (min-width: 1024px) {
  .landing-hero__visual {
    display: block;
  }
}
.landing-hero__mockup {
  position: relative;
  background: var(--bg-dark-secondary);
  border-radius: var(--radius-lg);
  padding: var(--spacing-xl);
  border: 1px solid var(--overlay-light);
  aspect-ratio: 4/3;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--text-muted);
  font-size: 0.875rem;
}

.landing-hero__mockup::before {
  content: "";
  position: absolute;
  inset: -20%;
  background: radial-gradient(circle, color-mix(in srgb, var(--color-primary) 12%, transparent) 0%, transparent 70%);
  z-index: -1;
  pointer-events: none;
}

/* Problem Section */
.landing-problem {
  padding: var(--spacing-xl) var(--spacing-md);
  background: var(--bg-dark-secondary);
}

@media (min-width: 768px) {
  .landing-problem {
    padding: 80px var(--spacing-lg);
  }
}
.landing-section__title {
  font-size: 1.75rem;
  font-weight: 700;
  margin: 0 0 var(--spacing-xl);
  text-align: center;
}

@media (min-width: 768px) {
  .landing-section__title {
    font-size: 2.25rem;
  }
}
.landing-problem__grid {
  display: grid;
  gap: var(--spacing-md);
  max-width: 1000px;
  margin: 0 auto var(--spacing-xl);
}

@media (min-width: 768px) {
  .landing-problem__grid {
    grid-template-columns: repeat(3, 1fr);
    gap: var(--spacing-lg);
  }
}
.landing-problem__card {
  background: var(--bg-dark);
  padding: var(--spacing-lg);
  border-radius: var(--radius-lg);
  text-align: center;
  border: 1px solid var(--overlay-light);
}

.landing-problem__icon {
  font-size: 2.5rem;
  margin-bottom: var(--spacing-md);
}

.landing-problem__text {
  font-size: 1rem;
  color: var(--text-secondary);
  font-style: italic;
  margin: 0;
  line-height: 1.6;
}

.landing-problem__transition {
  text-align: center;
  font-size: 1.125rem;
  color: var(--text-primary);
  font-weight: 600;
  margin: 0;
}

/* Social Proof Section */
.landing-social-proof {
  padding: var(--spacing-xl) var(--spacing-md);
  background: var(--bg-dark-secondary);
}

@media (min-width: 768px) {
  .landing-social-proof {
    padding: 100px var(--spacing-lg);
  }
}
.landing-stats {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--spacing-lg);
  max-width: 900px;
  margin: 0 auto 60px;
  text-align: center;
}

@media (min-width: 768px) {
  .landing-stats {
    grid-template-columns: repeat(4, 1fr);
  }
}
.landing-stat__number {
  display: block;
  font-size: 2rem;
  font-weight: 700;
  background: linear-gradient(135deg, var(--color-primary) 0%, var(--color-secondary) 100%);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
}

@media (min-width: 768px) {
  .landing-stat__number {
    font-size: 2.5rem;
  }
}
.landing-stat__label {
  display: block;
  font-size: 0.875rem;
  color: var(--text-secondary);
  margin-top: var(--spacing-xs);
}

.landing-testimonials {
  display: grid;
  gap: var(--spacing-lg);
  max-width: 1200px;
  margin: 0 auto;
}

@media (min-width: 1024px) {
  .landing-testimonials {
    grid-template-columns: repeat(3, 1fr);
  }
}
.landing-testimonial {
  background: var(--bg-dark);
  padding: var(--spacing-lg);
  border-radius: var(--radius-lg);
  border: 1px solid var(--overlay-light);
}

.landing-testimonial__stars {
  color: var(--color-secondary);
  font-size: 0.875rem;
  margin-bottom: var(--spacing-md);
  letter-spacing: 2px;
}

.landing-testimonial__quote {
  font-size: 1rem;
  color: var(--text-primary);
  line-height: 1.7;
  margin: 0 0 var(--spacing-lg) 0;
  font-style: normal;
}

.landing-testimonial__author {
  display: flex;
  align-items: center;
  gap: var(--spacing-md);
}

.landing-testimonial__avatar {
  width: 48px;
  height: 48px;
  border-radius: var(--radius-full);
  background: var(--bg-dark-tertiary);
  flex-shrink: 0;
}

.landing-testimonial__name {
  font-weight: 600;
  color: var(--text-primary);
  margin: 0;
  font-size: 0.875rem;
}

.landing-testimonial__role {
  font-size: 0.75rem;
  color: var(--text-muted);
  margin: var(--spacing-xs) 0 0 0;
}

/* How It Works Section */
.landing-how-it-works {
  padding: var(--spacing-xl) var(--spacing-md);
  max-width: 1000px;
  margin: 0 auto;
}

@media (min-width: 768px) {
  .landing-how-it-works {
    padding: 100px var(--spacing-lg);
  }
}
.landing-steps {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-xl);
  margin-bottom: var(--spacing-xl);
}

@media (min-width: 1024px) {
  .landing-steps {
    flex-direction: row;
    align-items: flex-start;
    justify-content: center;
    gap: 0;
  }
}
.landing-step {
  text-align: center;
  flex: 1;
  max-width: 280px;
  margin: 0 auto;
}

@media (min-width: 1024px) {
  .landing-step {
    margin: 0;
  }
}
.landing-step__number {
  width: 56px;
  height: 56px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: linear-gradient(135deg, var(--color-primary) 0%, var(--color-secondary) 100%);
  border-radius: var(--radius-full);
  font-size: 1.5rem;
  font-weight: 700;
  color: white;
  margin: 0 auto var(--spacing-md);
}

.landing-step__title {
  font-size: 1.125rem;
  font-weight: 600;
  margin: 0 0 var(--spacing-sm);
}

.landing-step__description {
  font-size: 0.875rem;
  color: var(--text-secondary);
  line-height: 1.6;
  margin: 0;
}

.landing-step__connector {
  display: none;
}

@media (min-width: 1024px) {
  .landing-step__connector {
    display: block;
    flex: 0 0 60px;
    height: 2px;
    background: linear-gradient(90deg, var(--color-primary), var(--color-secondary));
    margin-top: 28px;
  }
}
/* FAQ Section */
.landing-faq {
  padding: var(--spacing-xl) var(--spacing-md);
  background: var(--bg-dark-secondary);
}

@media (min-width: 768px) {
  .landing-faq {
    padding: 80px var(--spacing-lg);
  }
}
.landing-faq__list {
  max-width: 700px;
  margin: 0 auto;
}

.landing-faq__item {
  border-bottom: 1px solid var(--overlay-medium);
}

.landing-faq__item[open] .landing-faq__question::after {
  transform: rotate(180deg);
}

.landing-faq__question {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: var(--spacing-lg) 0;
  font-size: 1rem;
  font-weight: 600;
  color: var(--text-primary);
  cursor: pointer;
  list-style: none;
}

.landing-faq__question::-webkit-details-marker {
  display: none;
}

.landing-faq__question::after {
  content: "";
  width: 24px;
  height: 24px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgba(255,255,255,0.5)' stroke-width='2' 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: center;
  transition: transform 200ms ease;
  flex-shrink: 0;
}

.landing-faq__answer {
  padding: 0 0 var(--spacing-lg) 0;
  font-size: 0.875rem;
  color: var(--text-secondary);
  line-height: 1.7;
  margin: 0;
}

/* Landing Footer */
.landing-footer {
  padding: var(--spacing-xl) var(--spacing-md);
  background: var(--bg-dark-secondary);
  border-top: 1px solid var(--overlay-light);
}

@media (min-width: 768px) {
  .landing-footer {
    padding: 60px var(--spacing-lg) var(--spacing-xl);
  }
}
.landing-footer__grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--spacing-xl);
  max-width: 1200px;
  margin: 0 auto var(--spacing-xl);
}

@media (min-width: 768px) {
  .landing-footer__grid {
    grid-template-columns: 2fr 1fr 1fr 1fr;
  }
}
.landing-footer__logo {
  display: flex;
  align-items: center;
  gap: var(--spacing-sm);
  text-decoration: none;
  margin-bottom: var(--spacing-md);
}

.landing-footer__logo img {
  width: 32px;
  height: 32px;
}

.landing-footer__logo span {
  font-size: 1.25rem;
  font-weight: 700;
  background: linear-gradient(135deg, var(--color-primary) 0%, var(--color-secondary) 100%);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
}

.landing-footer__tagline {
  font-size: 0.875rem;
  color: var(--text-muted);
  margin: 0;
}

.landing-footer__links h4 {
  /* 14px is below the 20px display boundary — text face despite the h4 tag */
  font-family: var(--fw-font-text);
  font-size: 0.875rem;
  font-weight: 600;
  color: var(--text-primary);
  margin: 0 0 var(--spacing-md);
}

.landing-footer__links a {
  display: block;
  font-size: 0.875rem;
  color: var(--text-secondary);
  text-decoration: none;
  padding: var(--spacing-xs) 0;
  transition: color 150ms ease;
}

.landing-footer__links a:hover {
  color: var(--color-primary);
}

.landing-footer__bottom {
  max-width: 1200px;
  margin: 0 auto;
  padding-top: var(--spacing-lg);
  border-top: 1px solid var(--overlay-light);
  text-align: center;
}

.landing-footer__bottom p {
  font-size: 0.875rem;
  color: var(--text-muted);
  margin: 0;
}

/* ============================================
   PREVIEW CARD (Hero Mockup for Landing Page)
   ============================================ */
.preview-card {
  background: var(--bg-dark-card);
  border-radius: var(--radius-lg);
  padding: var(--spacing-md);
  border: 1px solid var(--border-subtle);
  max-width: 420px;
  margin: 0 auto;
}

@media (min-width: 1024px) {
  .preview-card {
    margin: 0;
    padding: var(--spacing-lg);
  }
}
.preview-card__header {
  display: flex;
  align-items: center;
  gap: var(--spacing-sm);
  margin-bottom: var(--spacing-md);
}

.preview-card__header-icon {
  font-size: 1.25rem;
}

.preview-card__header-title {
  font-size: 1rem;
  font-weight: 600;
  color: var(--text-primary);
  flex: 1;
}

.preview-card__header-count {
  font-size: 0.875rem;
  color: var(--text-muted);
}

.preview-card__streak {
  display: flex;
  align-items: center;
  gap: var(--spacing-sm);
  padding: var(--spacing-sm) var(--spacing-md);
  background: var(--bg-dark-elevated);
  border-radius: var(--radius-md);
  margin-bottom: var(--spacing-md);
}

.preview-card__streak-icon {
  font-size: 1.25rem;
}

.preview-card__streak-info {
  flex: 1;
  display: flex;
  flex-direction: column;
}

.preview-card__streak-days {
  font-size: 0.875rem;
  font-weight: 600;
  color: var(--text-primary);
}

.preview-card__streak-text {
  font-size: 0.75rem;
  color: var(--text-muted);
}

.preview-card__streak-done {
  font-size: 0.875rem;
  font-weight: 600;
  color: var(--text-primary);
}

/* Preview Habit Items */
.preview-habit {
  background: var(--bg-dark-elevated);
  border-radius: var(--radius-md);
  padding: var(--spacing-md);
  margin-bottom: var(--spacing-sm);
  border: 1px solid var(--overlay-light);
}

.preview-habit:last-child {
  margin-bottom: 0;
}

.preview-habit__check {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  background: var(--color-success);
  border-radius: var(--radius-md);
  color: white;
  float: left;
  margin-right: var(--spacing-sm);
  flex-shrink: 0;
}

.preview-habit__check--empty {
  background: transparent;
  border: 2px solid var(--overlay-strong);
}

.preview-habit__info {
  display: flex;
  flex-direction: column;
  gap: 2px;
  margin-bottom: var(--spacing-sm);
}

.preview-habit__name {
  display: flex;
  align-items: center;
  gap: var(--spacing-xs);
  font-size: 0.875rem;
  font-weight: 600;
  color: var(--text-primary);
}

.preview-habit__icon {
  font-size: 1rem;
}

.preview-habit__goal {
  font-size: 0.75rem;
  color: var(--text-muted);
  margin-left: 36px;
}

.preview-habit__streak-badge {
  float: right;
  padding: 2px var(--spacing-sm);
  background: color-mix(in srgb, var(--color-primary) 15%, transparent);
  border-radius: var(--radius-full);
  font-size: 0.75rem;
  color: var(--color-primary);
}

.preview-habit__progress {
  height: 4px;
  background: var(--overlay-medium);
  border-radius: var(--radius-full);
  overflow: hidden;
  margin-bottom: var(--spacing-sm);
  clear: both;
}

.preview-habit__progress::after {
  content: "";
  display: block;
  height: 100%;
  width: var(--progress, 0%);
  background: linear-gradient(90deg, var(--color-primary), var(--color-primary-dark));
  border-radius: var(--radius-full);
}

.preview-habit__progress--full::after {
  width: 100%;
  background: var(--color-success);
}

.preview-habit__value {
  text-align: center;
  font-size: 0.875rem;
  color: var(--text-muted);
  padding: var(--spacing-xs);
  background: var(--overlay-subtle);
  border-radius: var(--radius-sm);
}

.preview-habit__input-row {
  display: flex;
  gap: var(--spacing-sm);
  clear: both;
}

.preview-habit__input-value {
  flex: 1;
  padding: var(--spacing-xs) var(--spacing-sm);
  background: var(--overlay-subtle);
  border-radius: var(--radius-sm);
  font-size: 0.875rem;
  color: var(--text-secondary);
  text-align: center;
}

.preview-habit__input-btn {
  width: 32px;
  height: 32px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--color-primary);
  border: none;
  border-radius: var(--radius-sm);
  color: white;
  font-size: 1rem;
  font-weight: 600;
  cursor: pointer;
}

.preview-habit__rating {
  display: flex;
  gap: var(--spacing-xs);
  clear: both;
}

.preview-habit__rating-btn {
  flex: 1;
  padding: var(--spacing-xs);
  background: var(--bg-dark-card);
  border-radius: var(--radius-sm);
  font-size: 0.75rem;
  color: var(--text-secondary);
  text-align: center;
  border: 1px solid var(--border-subtle);
}

.preview-habit__rating-btn--selected {
  background: var(--color-primary);
  color: white;
  border-color: var(--color-primary);
}

/* Utility class for landing */
.text-center {
  text-align: center;
}

/* ============================================
   Nav Unread Badge (Messages)
   ============================================ */
.desktop-nav__link {
  position: relative;
}

.nav-badge {
  position: absolute;
  top: 0;
  right: 2px;
  min-width: 16px;
  height: 16px;
  font-size: 10px;
}

.bottom-nav__item .nav-badge {
  top: 2px;
  right: calc(50% - 22px);
}

/* .notification-badge sets display: flex, which would defeat the hidden attribute */
.nav-badge[hidden] {
  display: none;
}
