/* ============================================================================
   Best Guests — design tokens
   ============================================================================

   The colour, type, radius, shadow and glass values from the Best Guests
   landing page, ported for app use. The landing page's DESIGN.md is the source
   of truth for the brand; docs/DESIGN.md in this repo records what changed on
   the way in and why.

   Three rules from DESIGN.md that this file encodes and you should not undo:

   1. The brand IS the six-stop gradient. Any ambient colour must use the whole
      ramp in order along 135deg with near-uniform alphas. Truncating it to two
      or three stops collapses the identity back to "generic purple".
   2. Depth comes from light, not lines. A bright inner top highlight plus a
      long soft shadow — not a grey border.
   3. Shadows are always multi-layer (tight contact + wide ambient) and always
      rgba(20,20,26,...) — the ink, never black.

   Carried over unchanged from the landing page: palette, gradient, type scale
   ratios, radii, --ease, shadow recipes, glass Tiers 1 and 3.

   Deliberately NOT carried over: glass Tier 2 (--mock-*). It exists purely to
   make mockups read as floating illustrations; real UI shouldn't float.
   DESIGN.md says explicitly that it should not ship to the dashboard.
   ========================================================================= */

/* ---------- THE THEME SWITCH ----------------------------------------------
   The accent, the button ramp, the accent-tinted shadows, the brand mark and the
   WEIGHT of the status pills all live in a theme file. Everything else — the
   six-stop brand gradient, the neutrals, type, radii, density, and the semantic
   hues themselves — is shared and stays below.

   The pills are the one entry in that list that needs explaining. What a state
   MEANS is not the brand's decision, so the three hues are shared and every
   theme spends the same ones; how loudly they are spoken is a decision about the
   palette they sit in, so each theme supplies its own --status-*-bg /
   --status-*-ink pair. Violet-through-gold wears them tinted, a mono blue wears
   them filled.

   To change the app's whole look, change this ONE line:

       themes/blue-bowtie.css    royal blue accent + the bowtie    (active)
       themes/purple-crown.css   violet accent + the gold crown

   Nothing else moves. No HTML changes, no find-and-replace: every consumer of
   these values reads a token, which is what makes the swap a one-liner.

   Two rules for this file and the theme files:
     * A token is defined in exactly one of them, never both. They are merged by
       the cascade, and a duplicate here would silently beat the theme's copy.
     * If a value contains the accent — including inside an rgba() in a shadow —
       it belongs in the theme, not here.

   The theme is no longer @imported here. An @import is invisible to the browser
   until this file has been fetched AND parsed, so the theme was a second,
   serialised round trip on the render-blocking path. Every page now links
   /css/theme.css instead, which downloads in parallel and still lands ahead of
   these tokens, as the cascade requires.

   It is still ONE line to change: ACTIVE_THEME in web/server.js is what that
   stable path resolves to. --------------------------------------------------*/

:root {
  /* ---------- Core palette ---------- */
  --bg: #FFFFFF;
  --bg-alt: #F4F4F5;
  --ink: #14141A;
  --muted: #6E6E78;
  /* Hairlines. LIGHTER than they were (#E7E7EA), because separation on this app
     comes from shadow and a step in value — a line only has to say "these are two
     things", and at the old weight a page of tiles read as a wireframe of itself.
     Anything that needs to be seen rather than felt uses --line-firm. */
  --line: #F1F1F5;
  --line-firm: #E9E9EE;
  --card: #FFFFFF;
  /* Placeholder grey for skeleton blocks. A step darker than --line, which is a
     hairline colour and disappears as a filled shape. Deliberately neutral and
     NOT accent-tinted: a skeleton stands for absent content, so it should read
     as inert rather than as a brand element. */
  --skeleton: #E9E9ED;

  /* ---------- Accent ----------
     THEME-OWNED. --accent, --accent-deep, --accent-soft, --accent-tint,
     --on-accent and the six derived --accent-* values are supplied by the
     imported theme file. Don't define them here — see the switch above. */

  /* ---------- The brand gradient ----------
     Violet -> light violet -> pink -> dusty rose -> peach -> gold, at 135deg.

     Shared by every theme, and the reason the theme files are palettes rather
     than rebrands: this ramp IS the identity. A theme changes the accent the
     product acts through; it does not get to change this.

     The button ramp (--grad-btn) is theme-owned — it is a compressed version of
     whatever the accent is, which differs per theme. */
  --grad: linear-gradient(135deg, #7A4AD0 0%, #A47CDA 22%, #C28FBE 42%, #D69AA0 62%, #EDAE82 82%, #F7C169 100%);

  /* The six ramp stops, individually addressable. Ambient washes and (later)
     categorical chart series must be built from these and nothing else. */
  --g1: #7A4AD0;
  --g2: #A47CDA;
  --g3: #C28FBE;
  --g4: #D69AA0;
  --g5: #EDAE82;
  --g6: #F7C169;

  /* ---------- Type ---------- */
  --font: "Instrument Sans", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;

  /* PARKED, and nothing reads it yet. The wordmark is meant to be bold Visby
     CF — a commercial Connary Fagen face that is neither licensed nor present
     here. When it is: drop the web files in /assets/fonts/, add the @font-face,
     put this in front, and apply it to .rail-brand and .auth-mark. Until then
     the wordmark stays in --font, which is honest about what is installed. */
  --font-display: var(--font);

  /* ---------- Radii ----------
     Nothing is a capsule. Radius tracks element HEIGHT, not importance — a 48px
     button at 14px and a 26px chip at 8px read as the same amount of rounding.
     Nested corners are concentric: a control inset N px inside a rounded parent
     takes (parent radius - N).

     TIGHTENED, roughly a third off every step. The old scale (panel 16, button
     and field 14, tile 11) was soft enough that a page of nested containers read
     as a stack of lozenges — the roundness became the loudest thing about the
     layout, which is not something a layout should be saying. At these values a
     corner still reads as deliberate and stops competing with the content. */
  /* An ✕ as a mask, for the one place a glyph cannot be an inline <svg>: a
     ::-webkit-search-cancel-button is a pseudo-element and can hold no markup.
     Stroke-linecap round and 2.4 weight to match the toolbar icons it sits
     beside. The stroke colour inside is irrelevant — a mask uses only alpha, so
     the visible colour comes from whatever `background-color` the rule sets. */
  --icon-x: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.4' stroke-linecap='round'%3E%3Cline x1='6' y1='6' x2='18' y2='18'/%3E%3Cline x1='18' y1='6' x2='6' y2='18'/%3E%3C/svg%3E");

  --r-xs: 5px;
  --r-sm: 6px;
  --r-chip: 6px;
  --r-tile: 8px;
  --r-btn: 8px;
  --r-md: 8px;
  --r-panel: 10px;
  --r-lg: 12px;
  --r-squircle: 22%;   /* icon chips only — scales with the box */

  /* ---------- Shadows ----------
     Note the large negative spreads: they keep the wide ambient layer from
     muddying the area directly beneath a surface. */
  --shadow-card: 0 1px 2px rgba(20,20,26,.04), 0 12px 28px -10px rgba(20,20,26,.12), 0 48px 90px -40px rgba(20,20,26,.20);
  --shadow-card-hover: 0 1px 2px rgba(20,20,26,.05), 0 18px 36px -12px rgba(20,20,26,.16), 0 60px 100px -40px rgba(20,20,26,.24);
  --shadow-icon: inset 0 1px 0 rgba(255,255,255,.85), inset 0 -1px 1px rgba(20,20,26,.05), 0 1px 2px rgba(20,20,26,.06), 0 20px 36px -14px rgba(20,20,26,.20);

  /* Dashboard-scale shadow. The marketing --shadow-card throws a 90px ambient
     layer, which at app density overlaps the next panel and reads as haze.
     Same two-layer recipe, proportionally tighter. */
  --shadow-panel: 0 1px 2px rgba(20,20,26,.04), 0 8px 20px -8px rgba(20,20,26,.10), 0 24px 48px -28px rgba(20,20,26,.16);

  /* THEME-OWNED: --shadow-btn and the primary button's --shadow-cta pair. A
     coloured button's shadow is tinted with its own hue — that is what makes it
     look lit rather than pasted on — so it moves with the accent.

     The destructive set below does NOT move: red is red in every theme. */

  /* The destructive button's lift. Same shape as --shadow-cta but in the danger
     hue — a red control throwing a neutral grey shadow reads as two materials.
     Shallower than the CTA's: this is a button you should be able to see
     clearly, not one being sold to you. */
  --shadow-danger: 0 2px 6px -2px rgba(190,30,30,.35), 0 10px 22px -12px rgba(150,20,20,.30);
  --shadow-danger-hover: 0 3px 8px -2px rgba(190,30,30,.45), 0 16px 30px -14px rgba(150,20,20,.40);

  /* The destructive button's glass treatment: --grad-btn and --shadow-cta,
     transposed into red. The ramp starts at --danger-solid and only darkens —
     it does NOT lighten toward a brighter red first, the way the blue ramp
     lightens toward --accent. White on #DC2626 is 4.8:1, which is the floor;
     a lighter stop above it would drop the label below AA somewhere along the
     gradient, and a label that only fails on part of a button is worse than
     one that fails all over, because it looks fine in a screenshot. */
  --grad-danger: linear-gradient(135deg, #DC2626 0%, #C41C1C 52%, #911616 100%);
  --shadow-cta-danger: inset 0 1.5px 0 rgba(255,255,255,.42), inset 0 -8px 16px -8px rgba(90,10,10,.5), 0 8px 20px -8px rgba(220,38,38,.5), 0 22px 44px -20px rgba(140,20,20,.42);
  --shadow-cta-danger-hover: inset 0 1.5px 0 rgba(255,255,255,.52), inset 0 -8px 16px -8px rgba(90,10,10,.55), 0 14px 30px -10px rgba(220,38,38,.6), 0 34px 62px -22px rgba(140,20,20,.5);

  --shadow-panel-hover: 0 1px 2px rgba(20,20,26,.05), 0 12px 26px -10px rgba(20,20,26,.14), 0 32px 60px -30px rgba(20,20,26,.20);

  /* ---------- Glass Tier 1 — page glass ----------
     Content surfaces: panels, cards, modals, the rail. Most opaque tier; these
     hold body copy and must stay legible over any background. */
  --glass-blur: blur(22px) saturate(180%);
  --glass-blur-sm: blur(12px) saturate(160%);
  --glass-fill: linear-gradient(150deg, rgba(255,255,255,.82) 0%, rgba(255,255,255,.46) 52%, rgba(244,238,255,.62) 100%);
  --glass-fill-soft: linear-gradient(150deg, rgba(255,255,255,.72) 0%, rgba(245,240,255,.44) 100%);
  --glass-edge: 1px solid rgba(255,255,255,.7);
  /* THEME-OWNED: --glass-shadow and --glass-shadow-hover. Their inner bottom
     layer is a trace of the accent, so the pair travels with the theme even
     though the fills above them don't. */

  /* ---------- Glass Tier 3 — icon glass ----------
     Squircle icon chips and icon buttons. Neutral white, NO purple tint: the
     gradient's warm end at 155deg lands bottom-right and reads as a smudge on
     small surfaces. Ambient colour belongs in the background, not on objects. */
  --glass-icon-fill: linear-gradient(155deg, rgba(255,255,255,.95) 0%, rgba(246,246,250,.58) 100%);
  --glass-icon-shadow: inset 0 2px 2px rgba(255,255,255,.95), inset 0 -8px 16px -10px rgba(20,20,26,.06), 0 8px 20px -10px rgba(20,20,26,.10), 0 22px 44px -22px rgba(20,20,26,.16);

  /* ---------- Motion ----------
     The house curve: fast out, long settle. Everything except linear loops. */
  --ease: cubic-bezier(.22,.9,.28,1);

  /* ======================================================================
     App-only additions
     ----------------------------------------------------------------------
     Everything above is the landing page's system. Everything below is new —
     DESIGN.md section 10 lists these as "needs designing, no precedent here".
     ===================================================================== */

  /* ---------- Density ----------
     Marketing values stepped down roughly one notch, per DESIGN.md: section
     padding 112px -> 32-48px, card padding 26-32px -> 16-20px, body 16 -> 15px.
     Type scale RATIOS and radii are unchanged; only spacing compresses. */
  --rail-w: 264px;

  /* The rail's glyph slot. Every row in the rail — crown, nav icon, avatar —
     reserves a leading box of exactly this width and centres its mark inside
     it, then starts its label at the slot's trailing edge. 34px because that is
     the widest mark (the avatar), so the widest one needs no negative space and
     nothing is ever clipped.

     Two things fall out of this, and both matter:
       - Every row is inset the same 10px from the rail's content edge, so the
         avatar is padded exactly like the nav icons rather than sitting flush
         against the divider above it.
       - Every mark's centre lands on one vertical axis, which is also the
         collapsed rail's centre line — so collapsing moves nothing.

     Collapsed width is derived from it, never chosen by eye:
       1px border + 16px rail padding + 10px row inset
       + 34px slot
       + 10px row inset + 16px rail padding + 1px border  =  88px
     Change the rail padding, the row inset or the slot and this must be
     recomputed — see the glyph-slot note in app.css. */
  --rail-slot: 34px;
  --rail-row-inset: 10px;
  --rail-w-collapsed: 88px;
  /* Horizontal: equal to the rail's own 14px inset, so viewport-to-rail,
     rail-to-content and content-to-viewport are all the same gap. The column
     used to carry 40px here, which made its margins visibly wider than the
     rail's and wasted width the dashboard could use.

     Vertical is NOT 14, and the difference is deliberate. It's derived: the
     rail's brand row centres at 14 (margin) + 22 (rail padding) + 21 (half the
     42px row) = 57px from the top of the viewport. The page header is a 44px
     icon chip, so its centre sits at padding + 22. Solving for 57 gives 35.
     At 14 the page title floated well above the crown beside it and the header
     looked pinned to the top edge; at 35 the two first rows line up. */
  --main-pad-x: 14px;
  --main-pad-y: 35px;
  --panel-pad: 20px;
  --gap: 20px;
  --gap-sm: 12px;
  --text-body: 15px;

  /* ---------- Semantic colours ----------
     The brand palette has none. These are desaturated a step from their web
     defaults, and each one's hue is kept clear of BOTH accent bands — the
     violet 260-290 and the blue 210-230 — so a state never reads as accent
     under either theme. Contrast of every -ink value on its own -tint is
     >= 4.5:1.

     Shared, not theme-owned, and deliberately so: these say "this succeeded" /
     "this failed", which is not a thing the brand colour gets a vote on. They
     survived the blue reskin and the return from it unchanged. */
  --ok: #14763A;
  --ok-tint: #E7F6ED;
  --warn: #8A6100;        /* the yellow darkened until it reads AS TEXT (5.6:1) */
  --warn-tint: #FEF6D8;
  --danger: #C31D1D;
  --danger-tint: #FCE9E9;
  --info: var(--accent-deep);
  --info-tint: var(--accent-tint);

  /* ---------- Filled status pills ----------
     Traditional red / yellow / green, solid.

     The INK IS NOT THE SAME ON ALL THREE, and that is forced rather than
     stylistic. A bright yellow reflects almost as much light as white paper:
     #FACC15 with white text lands at 1.53:1, so the label disappears. Holding
     white ink everywhere means darkening the yellow to #A16207 — which was
     tried, and traded the yellow away to buy consistency nobody could see.

     So each pill takes whichever ink actually reads on it:

       green  #15803D + white = 5.0:1
       yellow #FACC15 + ink   = 12.0:1
       red    #DC2626 + white = 4.8:1

     If a future state needs a fourth colour, run it against BOTH inks and pick
     the winner rather than assuming white. */
  --ok-solid: #15803D;      --ok-on-solid: #FFFFFF;
  --warn-solid: #FACC15;    --warn-on-solid: #14141A;
  --danger-solid: #DC2626;  --danger-on-solid: #FFFFFF;

  /* ---------- Creator tiers ----------
     A tier says how big someone's following is, so the scale is one of WEIGHT
     rather than of hue: ink, mid grey, then the canvas step. Darkest is biggest,
     which means a column of them sorts itself visually before a word is read —
     and it costs the page no colour at all, on a table that already carries a
     figure in every other column.

     Neutral rather than semantic on purpose: green/amber/red are reserved for
     state, and a red NANO would read as an error on a row where nothing is wrong.

     Contrast measured, not assumed: 18.4, 5.0 and 4.6:1. Shared rather than
     theme-owned, because a tier means the same thing whichever accent is loaded.

     A fourth tier (MEGA) has nowhere darker to go, so it would shift the whole
     scale down a step rather than add one. */
  --tier-macro-bg: #14141A;  --tier-macro-ink: #FFFFFF;
  --tier-micro-bg: #E7E7EB;  --tier-micro-ink: #2E2E36;
  --tier-nano-bg: #F4F4F5;   --tier-nano-ink: #6E6E78;

  /* ---------- Surfaces ----------
     The app runs on flat white. There WAS a six-stop brand wash behind the
     shell; it has been removed. On a page where nothing scrolls behind
     anything, an ambient gradient is decoration that every surface then has to
     be read against — and it was the reason surfaces looked like faintly
     different whites depending on where they sat.

     What separates a container from the page is shadow, a hairline, and a
     small step in value: the canvas is a shade off white so that white panels
     sitting on it have something to sit ON. Pure white on pure white left the
     shadow doing all the work, which reads as soft rather than as separate.

     Consequence to know: backdrop-filter is a no-op over flat colour. Any
     remaining glass element gets its edge from --line, not from blur. */
  --canvas: #F4F4F6;
}
