/* ---------------------------------------------------------------------------
 * The Cedar Gate — design tokens
 *
 * Mirrors the `Theme` variable collection in Figma file URi5xnk7mtMbYku2d8uhys
 * (Light + Dark modes). The mockups are fully bound to these variables, so Dev
 * Mode emits var(--token), not #hex.
 *
 *   Dark  = the member app (black canvas, neutral surfaces, gold accents)
 *   Light = the office CRM (Storybase mechanics: flat white blocks on gray-18,
 *           supporting blocks stay flat; the arrival driver has a soft lift)
 *
 * Both surfaces ship both modes. `data-theme` on <html> picks one.
 *
 * HARD RULE: no component ever writes a hex value. If a color is missing here,
 * add it here.
 *
 * Provenance: every Light value below and the Dark values for page / raised /
 * text-primary / gold / live were read back from the Figma collection directly.
 * The remaining Dark values come from the locked design notes (member scoring
 * palette, TV board, dark-page semi-transparent ramp).
 *
 * The current dark hierarchy was revised on 2026-09-17 at Holden's request.
 * See docs/DARK-HIERARCHY.md for roles and docs/A11Y-AUDIT.md for the Figma
 * follow-up. Run scripts/check-contrast.mjs after changing a surface or ink.
 * ------------------------------------------------------------------------- */

/*
 * The third selector is how a light surface owns the DOCUMENT rather than
 * painting a box on top of a dark one.
 *
 * `<html>` carries data-theme="dark", because three of the four surfaces are
 * dark and that keeps them static. The office used to override the theme on
 * its own wrapper and give that wrapper a background, which looked right and
 * was not: the document underneath stayed dark, so iOS Safari tinted its
 * status bar and toolbar black around a light page. Safari takes its chrome
 * from the page, and the page is <html>, not the div you painted.
 *
 * So a top-level surface wrapper that declares itself light makes the whole
 * document light, and the wrapper then needs no background of its own. The
 * child combinator is deliberate: only the SURFACE counts. A light-themed card
 * nested somewhere inside the dark member app must not flip the document.
 */
:root,
[data-theme="light"],
html:has(> body > [data-theme="light"]) {
  /* dropdown — the AnimatedDropdown panel surface (ported from Storybase) */
  --dropdown-bg: radial-gradient(
    farthest-corner at center,
    rgba(239, 244, 246, 0.9) 0%,
    rgba(255, 255, 255, 0.9) 70%
  );
  --dropdown-border: rgba(32, 29, 24, 0.08);
  --dropdown-shadow: 0 0 10px rgba(0, 0, 0, 0.1);

  /*
   * tooltip — the Tooltip surface: a field's help note and a blocked button's
   * reason. Ink with white words in light, because the office's blocks are
   * flat white and a white tooltip on one of them disappears.
   */
  --tooltip-bg: #201d18;
  --tooltip-text: #ffffff;
  --tooltip-border: rgba(32, 29, 24, 0);
  --tooltip-shadow: 0 6px 20px rgba(32, 29, 24, 0.22);

  /*
   * peek — the inspector that floats beside a calendar entry, like Apple
   * Calendar's event card. Frosted on purpose: the month stays readable
   * underneath, which is the point of not navigating away.
   */
  --peek-bg: rgba(255, 255, 255, 0.78);
  --peek-border: rgba(32, 29, 24, 0.1);
  --peek-shadow: 0 8px 28px rgba(32, 29, 24, 0.16), 0 0 1px rgba(32, 29, 24, 0.08);

  /* Scoring result, distinct from an error or an unanswered bird. */
  --shot-hit: #4f6842;
  --shot-hit-hover: #445a39;
  --shot-hit-press: #3b4d31;
  --shot-hit-edge: #4f6842;
  --text-on-shot-hit: #ffffff;
  --shot-miss: #8f4938;
  --shot-miss-hover: #824232;
  --shot-miss-press: #753b2d;
  --shot-miss-edge: #8f4938;
  --text-on-shot-miss: #ffffff;

  --weight-display: 400;
  --weight-heading: 400;
  --weight-action: 400;

  /* surfaces */
  --surface-page: #f5f7f8;
  --surface-raised: #ffffff;
  --surface-card: #ffffff;
  --surface-card-alt: #f8f9fa;
  --surface-inset: #f5f7f8;
  --surface-warm-black: #0e0906;

  /* Light-theme ink is measured on resting and hovered surfaces.
     Dark uses its own opaque neutral text ramp below. */
  --text-primary: #201d18;
  --text-secondary: #201d18bf; /* ink @ 75% — 7.1:1 on the page grey */
  --text-tertiary: #201d18a6; /* ink @ 65% — 5.1:1 on the page grey, 5.0:1 hovered */
  --text-on-gold: #201d18; /* ink on gold, both modes. See the button alias note. */
  --text-on-dark: #ffffff; /* white labels on dark pills, both modes */
  --text-on-danger: #ffffff; /* the label on a red FILL, which is not a gold one */

  /* brand */
  --brand-gold: #c19931;
  --brand-gold-strong: #7d6320; /* small gold text + icons on light — 4.7:1 on its own chip tint */
  --brand-gold-tint: #c1993126; /* gold @ 15% */
  --brand-gold-edge: #c199318c; /* gold @ 55% — the outline on a card that wants action */
  /* The gold FILL hovered and held down: brand gold under 8% and 14% of the
     ink that sits on it, so the label keeps 5.5:1 and 5.0:1. The same in both
     themes, like the gold itself, and here rather than in the fixed block so
     check:contrast can measure the label on them. */
  --brand-gold-hover: #b48f2f;
  --brand-gold-press: #aa882e;

  /* lines + fills */
  --border-hairline: #201d181a; /* ink @ 10% */
  /* A CONTROL's edge, not a divider's. An outline button at hairline weight is
     barely there; the Figma frames draw it at 25% and they are right. */
  --border-control: #201d1840; /* ink @ 25% */
  --fill-subtle: #201d1814; /* ink @ 8% — chip and tag tints */

  /* interactions — see the hover and press vocabulary in globals.css */
  --hover-on-card: #f8f9fa; /* a whisper off white, for anything ON a card */
  --hover-on-page: #eceff1; /* a step off the page grey, for anything on it */
  /* A press is one step PAST the hover, in the same direction, so holding a
     control down reads as the hover deepening rather than as something new.
     It lasts as long as a finger is down, so check:contrast holds the copy on
     it to AA rather than to the 5:1 margin a resting ground gets. */
  --press-on-card: #f1f3f5;
  --press-on-page: #e4e8eb;
  --fill-subtle-press: #201d181f; /* ink @ 12%, a ghost control or chip held down */

  /* state */
  --state-live: #4f6842;
  --state-danger: #ae3931;
  --accent-olive: #5d6136;

  /* Tints of the two state colours, for the onboarding matrix and anywhere
     else a whole cell has to carry a status. Weak on purpose: fourteen rows of
     saturated red reads as a fire drill, and the point of the board is that
     three of those rows matter and eleven do not.

     A TINT IS BUILT FROM ITS OWN STATE COLOUR, not from a near neighbour. The
     text in a tinted cell is that state colour, so the two move together and
     the gap between them is the thing being protected. A tint left behind
     when its colour changed is how a cell ends up saying "stalled" in a red
     you cannot read on a red. */
  --state-live-tint: #4f68421f; /* green @ 12% */
  --state-danger-tint: #ae393126; /* red @ 15% */
}

[data-theme="dark"] {
  /* Scoring result, distinct from an error or an unanswered bird. */
  --shot-hit: #4f6842;
  --shot-hit-hover: #445a39;
  --shot-hit-press: #3b4d31;
  --shot-hit-edge: #93b189;
  --text-on-shot-hit: #ffffff;
  --shot-miss: #8f4938;
  --shot-miss-hover: #824232;
  --shot-miss-press: #753b2d;
  --shot-miss-edge: #e6a28c;
  --text-on-shot-miss: #ffffff;

  --weight-display: 500;
  --weight-heading: 700;
  --weight-action: 700;

  /* Neutral charcoal panels above a true-black canvas. Gold carries the
     warmth; surfaces carry hierarchy. See docs/DARK-HIERARCHY.md. */
  --dropdown-bg: radial-gradient(
    farthest-corner at center,
    rgba(41, 41, 41, 0.97) 0%,
    rgba(46, 46, 46, 0.97) 70%
  );
  --dropdown-border: rgba(255, 255, 255, 0.14);
  --dropdown-shadow: 0 0 24px rgba(0, 0, 0, 0.55);

  /* One step above the raised panel, edged, so it reads on a dialog too. */
  --tooltip-bg: #3a3a3a;
  --tooltip-text: #f5f5f5;
  --tooltip-border: rgba(255, 255, 255, 0.14);
  --tooltip-shadow: 0 8px 24px rgba(0, 0, 0, 0.55);

  --peek-bg: rgba(41, 41, 41, 0.96);
  --peek-border: rgba(255, 255, 255, 0.14);
  --peek-shadow: 0 12px 40px rgba(0, 0, 0, 0.5), 0 0 1px rgba(255, 255, 255, 0.08);

  /* The brighter step is reserved for foreground panels and featured content.
     Fields recede into their panel; hover is one further neutral step. */
  --surface-page: #000000;
  --surface-raised: #292929;
  --surface-card: #202020;
  --surface-card-alt: #171717;
  --surface-inset: #101010;
  --surface-warm-black: #000000;

  /* Opaque text keeps the reading hierarchy stable across surfaces. Even the
     quiet tier clears 5:1 on the brightest hover ground. */
  --text-primary: #f5f5f5;
  --text-secondary: #c4c4c4;
  --text-tertiary: #a3a3a3;
  --text-on-gold: #201d18; /* ink on gold. See the button alias note. */
  --text-on-dark: #ffffff;
  --text-on-danger: #201d18; /* the label on a red FILL, and dark's red is a light one */

  /* brand */
  --brand-gold: #c19931;
  --brand-gold-strong: #d0aa48; /* readable small gold labels; primary fill stays brand gold */
  --brand-gold-tint: #c1993126;
  --brand-gold-edge: #c199318c;
  --brand-gold-hover: #b48f2f;
  --brand-gold-press: #aa882e;

  /* lines + fills */
  --border-hairline: #ffffff1f; /* white @ 12% */
  --border-control: #ffffff59; /* white @ 35% — visible outline controls on charcoal */
  --fill-subtle: #ffffff1a; /* white @ 10% */

  /* interactions. In dark the lift is invisible, so these carry the hover on
     their own and are therefore a LIGHTER step rather than a darker one.
     hover-on-card sits above the raised step, so a hovered card is always the
     brightest thing on the screen no matter what it was resting at. */
  --hover-on-card: #303030;
  --hover-on-page: #171717;
  /* Lighter again, for the same reason. #363636 is as far as it can go: one
     step more and gold copy on a held card drops under AA. */
  --press-on-card: #363636;
  --press-on-page: #1f1f1f;
  --fill-subtle-press: #ffffff24; /* white @ 14% */

  /* state */
  --state-live: #93b189;
  --state-danger: #ea7f76;
  --accent-olive: #5d6136;

  /* State tints stay quiet enough for their labels to clear AA on raised
     charcoal. The label supplies the color; the wash only groups it. */
  --state-live-tint: #93b1891f; /* green @ 12% */
  --state-danger-tint: #ea7f761a; /* red @ 10% */
}

/*
 * Without :has(), a light surface cannot reach the document, so it goes back
 * to painting its own box. That is the old behaviour and it looks the way the
 * office looked before this rule existed: correct, with the wrong colour
 * behind the browser chrome. The alternative is dark ink on a dark page, which
 * is not a degraded experience, it is an unreadable one.
 */
@supports not selector(html:has(*)) {
  body > [data-theme] {
    background: var(--surface-page);
  }
}

/* ---------------------------------------------------------------------------
 * Button aliases. These exist so a button never reaches for a raw surface or
 * text token. `--button-primary-label` is the one that earns its keep: one gold
 * button definition serves both the office CRM and the member app.
 *
 * The label is the warm INK on gold, in both modes, and it is the same ink in
 * both so the office and the member app still read as one button. It was white
 * until 2026-09-15, which is the live site's treatment and looked right, and
 * white on #c19931 measures 2.67:1. There is no size at which that is legible
 * text: it fails the large-text floor of 3:1 as well, so shipping it meant the
 * label on the most-tapped control in the product was decoration. Ink on the
 * same gold is 6.3:1 and the gold itself did not have to move, which is what
 * decided it — #c19931 is the brand, the label was never the brand.
 *
 * `--text-on-danger` exists for the same reason and is NOT a copy of this one.
 * A red fill is not a gold one: light's red takes a white label and dark's red
 * is light enough that it takes the ink. The assistant's talking button used
 * `--text-on-gold` on a red ground, which happened to work while that token was
 * white and would have gone to 2.8:1 the moment it stopped being.
 *
 * Both tokens exist in both theme blocks rather than moving to the
 * theme-independent list below, because the light/dark parity check reads those
 * two blocks and a token present in one and missing from the other is exactly
 * what it is there to catch.
 * ------------------------------------------------------------------------- */
:root {
  --button-primary-bg: var(--brand-gold);
  --button-primary-label: var(--text-on-gold);
  --button-ghost-label: var(--text-primary);
  --button-outline-border: var(--border-control);
  --button-outline-label: var(--text-primary);
  /* SINGLE SOURCE OF TRUTH: actions are pills except desktop office controls.
     The radius is resolved on each control, after its size is known. */
  --button-height-xs: 30px;
  --button-height-sm: 36px;
  --button-height-md: 44px;
  --button-target-size: clamp(144px, 25svh, 210px);
  --button-height-lg: 56px;
  --button-touch-height: 44px;
  --button-radius-ratio: 0.5;
  --button-office-radius-ratio: calc(1 / 3);

  /*
   * THE FOCUS RING, and there is one of it.
   *
   * It points at `--brand-gold-strong` rather than `--brand-gold` because a
   * focus ring is a non-text graphic and has to clear 3:1 against what it is
   * drawn on. Brand gold on the office's white blocks is 2.7:1, so the ring
   * that told a keyboard user where they were stood was, on the surface where
   * people actually use a keyboard, the hardest one to see. In dark the stronger
   * reading gold keeps this visible on raised charcoal too.
   *
   * Every focusable thing gets this from one rule in globals.css. Nothing
   * should be reaching for it at a call site.
   */
  --focus-ring: var(--brand-gold-strong);
  --focus-ring-width: 2px;
  --focus-ring-offset: 2px;
}

/* ---------------------------------------------------------------------------
 * Colors that do NOT change with the theme.
 *
 * These sit on something that is the same in both modes: gold, or a
 * photograph. Declaring them once here rather than twice in the light and dark
 * blocks is not a shortcut, it is the honest place for them, and it keeps
 * `pnpm check:theme` able to insist that everything in those two blocks is
 * defined in BOTH. A token that appears in one and not the other is a colour
 * that silently falls back to the wrong mode.
 * ------------------------------------------------------------------------- */
:root {
  /* Resume glass: invariant dark material, selected from footer option 2. */
  --resume-glass-base: #191710;
  --resume-glass-fill: #191710e0;
  --resume-glass-hover: #211e16e0;
  --resume-glass-press: #121009e8;
  --resume-glass-gloss: #e8d6a212;
  --resume-glass-reflection: #e8d6a217;
  --resume-glass-reflection-tail: #e8d6a20d;
  /* Rounded highlights with long, transparent tails; never a triangular peak. */
  --resume-glint-peak: #e8d6a22e;
  --resume-glint-rim-peak: #e8d6a299;
  --resume-glint-fill: linear-gradient(105deg,
    color-mix(in srgb, var(--resume-glint-peak) 0%, transparent) 0%,
    color-mix(in srgb, var(--resume-glint-peak) 0%, transparent) 10%,
    color-mix(in srgb, var(--resume-glint-peak) 3%, transparent) 20%,
    color-mix(in srgb, var(--resume-glint-peak) 20%, transparent) 30%,
    color-mix(in srgb, var(--resume-glint-peak) 66%, transparent) 40%,
    color-mix(in srgb, var(--resume-glint-peak) 91%, transparent) 45%,
    color-mix(in srgb, var(--resume-glint-peak) 100%, transparent) 50%,
    color-mix(in srgb, var(--resume-glint-peak) 91%, transparent) 55%,
    color-mix(in srgb, var(--resume-glint-peak) 66%, transparent) 60%,
    color-mix(in srgb, var(--resume-glint-peak) 20%, transparent) 70%,
    color-mix(in srgb, var(--resume-glint-peak) 3%, transparent) 80%,
    color-mix(in srgb, var(--resume-glint-peak) 0%, transparent) 90%,
    color-mix(in srgb, var(--resume-glint-peak) 0%, transparent) 100%
  );
  --resume-glint-rim: linear-gradient(105deg,
    color-mix(in srgb, var(--resume-glint-rim-peak) 0%, transparent) 0%,
    color-mix(in srgb, var(--resume-glint-rim-peak) 0%, transparent) 10%,
    color-mix(in srgb, var(--resume-glint-rim-peak) 3%, transparent) 20%,
    color-mix(in srgb, var(--resume-glint-rim-peak) 20%, transparent) 30%,
    color-mix(in srgb, var(--resume-glint-rim-peak) 66%, transparent) 40%,
    color-mix(in srgb, var(--resume-glint-rim-peak) 91%, transparent) 45%,
    color-mix(in srgb, var(--resume-glint-rim-peak) 100%, transparent) 50%,
    color-mix(in srgb, var(--resume-glint-rim-peak) 91%, transparent) 55%,
    color-mix(in srgb, var(--resume-glint-rim-peak) 66%, transparent) 60%,
    color-mix(in srgb, var(--resume-glint-rim-peak) 20%, transparent) 70%,
    color-mix(in srgb, var(--resume-glint-rim-peak) 3%, transparent) 80%,
    color-mix(in srgb, var(--resume-glint-rim-peak) 0%, transparent) 90%,
    color-mix(in srgb, var(--resume-glint-rim-peak) 0%, transparent) 100%
  );
  --resume-glass-rim: linear-gradient(115deg, #f2dea7, #a98128 35%, #c19931 67%, #f3e4ba);
  --resume-shadow: 0 0 0 1px rgb(0 0 0 / 0.8), 0 -4px 48px 14px rgb(0 0 0 / 0.88), 0 18px 56px 12px rgb(0 0 0 / 0.75);

  /* The SCORE disc lifted out of the member tab bar (Figma A05, score-action
     22:122): gold lit from above, with a warm glow under it. Gold in both
     themes, so it is declared once here. */
  --score-disc-top: #e9c975;
  --score-disc-bottom: #ad841a;
  --score-disc-fill: linear-gradient(180deg, var(--score-disc-top), var(--score-disc-bottom));
  --score-disc-glow: 0 4px 9px rgb(193 153 49 / 0.38);

  /* The boot splash sheen. White and low alpha in both themes.

     Three stops rather than one so the highlight can be given a ROUNDED peak.
     A gradient that runs straight up to a single bright stop and straight back
     down has a corner at the top, and that corner is visible as a hard line
     down the middle of the glint. `mid` sits close to the peak so the profile
     flattens as it approaches it; `faint` carries the long tail out to nothing. */
  --sheen: #ffffff2e; /* white 18% - the peak, softened from 30% */
  --sheen-mid: #ffffff26; /* white 15% */
  --sheen-faint: #ffffff14; /* white 8% */

  /* The boot splash's ground, which depends on where the page is running.

     In a browser it is the page's own black. Inside the native app it is the
     launch screen's grey instead, from the rule just below this block, which
     applies once the head script in lib/native-shell-mark.ts has marked the
     document as the app. Holden, 2026-09-18. */
  --surface-splash: var(--surface-page);

  /* The traveling boot-splash highlight.
     Transparent ends sit well inside the box so a diagonal does not hard-cut
     at a vertical edge; the mid stops flatten the peak so it is not a line. */
  --sheen-sweep: linear-gradient(
    100deg,
    transparent 0%,
    transparent 20%,
    var(--sheen-faint) 32%,
    var(--sheen-mid) 42%,
    var(--sheen) 50%,
    var(--sheen-mid) 58%,
    var(--sheen-faint) 68%,
    transparent 80%,
    transparent 100%
  );

  /* The lift under a hovered CARD and the event arrival driver.
     Tight and two-layer: a contact shadow plus a short ambient one. Small on
     purpose, so it reads as a nudge rather than a floating block and does not
     bleed onto the card below it in a dense list.
     Slate-tinted and theme-independent, which means it is near-invisible in
     dark mode by design: there the surface shift carries the hover instead. */
  --shadow-lift: 0 2px 4px -1px rgba(15, 23, 42, 0.06), 0 8px 24px -8px rgba(15, 23, 42, 0.13);

  /* The ground under text laid over a PHOTOGRAPH: gallery scrims, event card
     gradients, the lightbox. Neutral black, and the same in both themes,
     because the photograph does not change when the app does. Alpha comes from
     the Tailwind modifier at the call site (`from-scrim/85`), so there is one
     colour here rather than six near-identical ones. */
  --scrim: #000000;

  /* INK ON PAPER. The printed directory page, and only that.
     The same in both themes because paper does not have a dark mode, and a
     page that followed the theme would come out of the printer as a solid
     black sheet the first time somebody printed it from the CRM at night.
     Warm rather than neutral, matching the club's own ink. */
  --paper: #ffffff;
  --paper-ink: #201d18;
  --paper-ink-soft: #4a443a;
  --paper-ink-faint: #6b6459;
  --paper-ink-quiet: #8b8577;
  --paper-rule: #d6d1c7;
  --paper-gold: #8a6d23;
}

/*
 * THE SPLASH INSIDE THE NATIVE APP, and nowhere else.
 *
 * A cold open in the app starts on the launch screen, which the binary paints
 * #121212 on iOS and Android, and hands over to the web splash underneath it.
 * With the web splash on the page's black the whole screen dropped a shade at
 * that moment, which read as a flash, and a launch screen can only change with
 * a store release. So in the app the web splash takes the launch screen's
 * colour, and the app's own black arrives as the splash lifts. The native
 * launch assets carry the same value (the storyboard, the Splash image, the
 * splash plugin, and cedarSplash on Android); change them together or not at
 * all.
 *
 * `data-shell="native"` is set on <html> by lib/native-shell-mark.ts before the
 * first paint. A browser never gets it, so a browser keeps the black above.
 */
:root[data-shell="native"] {
  --surface-splash: #121212;
}

/* ---------------------------------------------------------------------------
 * Non-color tokens
 * ------------------------------------------------------------------------- */
:root {
  /* radius — the live site's buttons are 10px, cards run 12-20 */
  --radius-sm: 8px;
  --radius-md: 10px;
  --radius-lg: 12px;
  --radius-xl: 16px;
  --radius-2xl: 20px;
  --radius-pill: 999px;

  /* type — Cinzel display (uppercase, wide tracking), Lato body, and the hand
     a waiver signature is written in */
  --font-display: var(--font-cinzel), Georgia, serif;
  --font-body: var(--font-lato), system-ui, sans-serif;
  --font-signature: var(--font-hand), cursive;
  --tracking-display: 0.125em;
  --tracking-body: 0.6px;

  /* motion — every press is subtle, premium, and instant */
  --ease-out: cubic-bezier(0.22, 1, 0.36, 1);
  --duration-press: 120ms;
  --duration-view: 260ms;
  /* A hover or a press changes SHADE, never size. The shade eases in over
     --duration-shade and a finger going down quickens it to --duration-press.
     The curve (`--ease-smooth`) lives in globals.css @theme so it is also a
     Tailwind utility. */
  --duration-shade: 200ms;

  /* the assistant's stronger elevation; the arrival driver uses shadow-lift */
  --shadow-assistant: 0 8px 16px rgb(0 0 0 / 0.2);

  /* safe areas — every element against a screen edge uses these.
     In-flow content still sits under the iOS status bar; flow does NOT
     push it below OS chrome. */
  --safe-area-top: env(safe-area-inset-top, 0px);
  --safe-area-bottom: env(safe-area-inset-bottom, 0px);
}
