/* ============================================================================
   TGIS — The Growin Stones
   Design tokens. Single source of truth. Do not hardcode values elsewhere.

   ----------------------------------------------------------------------------
   HOW A THEME IS BUILT

   This file used to declare ~40 colours as literal hex. Changing the theme
   therefore meant changing 40 values, which is why for a long time the only
   thing a member could actually change was the accent — and why changing it
   appeared to repaint nothing but the buttons. Everything else was nailed down.

   Nothing here is a literal any more. A colour in this file is DERIVED from
   three inputs, and a theme supplies only those inputs:

     1. THE MODE LADDER  (`--l-*`)   how light each rung of the ramp is.
        Lives on [data-mode]. Two of them exist, dark and light, and they are
        the reason one theme definition can be either without restating itself.

     2. THE THEME SEEDS  (`--h-*`, `--c-*`)  what hue and how much chroma.
        Lives on [data-theme]. A theme is roughly a dozen numbers — which is
        what makes thirty of them maintainable and a thirty-first cheap.

     3. THE INK          (`--ink`)   the colour that overlays are made of.
        White on a dark mode, near-black on a light one. Every hairline, hover
        wash and bevel is a percentage of it, so all of them invert together.

   Both attributes sit on <html>. `data-mode` is not an axis a member chooses
   independently — each theme declares which one it wants and the boot script
   sets both. Splitting them is purely so the two ladders are written once
   rather than thirty times.

   ----------------------------------------------------------------------------
   WHY THE NAMES DID NOT CHANGE

   `--accent` is referenced 294 times across this codebase, `--text-2` 307
   times, `--line` 214. Around seventeen hundred usages in total. Every one of
   those names survives here unchanged and only its RIGHT-HAND SIDE moved, so
   the entire product became themeable without editing the files that consume
   it. Renaming would have been tidier and would have cost a week of churn for
   no behaviour.

   ----------------------------------------------------------------------------
   THE ONE RULE FOR NEW CSS

   Never write `rgba(255,255,255,.06)`. That is a white wash, it is invisible on
   sixteen of the thirty themes, and it is the single mistake this architecture
   exists to prevent. Use the ink ladder — `--ink-0` through `--ink-9` — which is
   the same wash expressed as "a bit of the foreground colour", and therefore
   turns black by itself when the background turns white.
   ========================================================================== */

@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 100 900;
  font-display: block;
  src: url('/assets/fonts/InterVariable.woff2') format('woff2');
}
@font-face {
  font-family: 'Inter';
  font-style: italic;
  font-weight: 100 900;
  font-display: block;
  src: url('/assets/fonts/InterVariable-Italic.woff2') format('woff2');
}
@font-face {
  font-family: 'InterDisplay';
  font-style: normal;
  font-weight: 600;
  font-display: block;
  src: url('/assets/fonts/InterDisplay-SemiBold.woff2') format('woff2');
}
@font-face {
  font-family: 'InterDisplay';
  font-style: normal;
  font-weight: 700;
  font-display: block;
  src: url('/assets/fonts/InterDisplay-Bold.woff2') format('woff2');
}
@font-face {
  font-family: 'InterDisplay';
  font-style: normal;
  font-weight: 900;
  font-display: block;
  src: url('/assets/fonts/InterDisplay-Black.woff2') format('woff2');
}
@font-face {
  font-family: 'Shrikhand';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/assets/fonts/Shrikhand-Regular.ttf') format('truetype');
}


/* ==========================================================================
   1. THE DARK LADDER — also the default
   --------------------------------------------------------------------------
   `:root` carries dark rather than a separate [data-mode="dark"] block, so a
   document that never runs the boot script still renders the way this product
   has always rendered. A missing attribute is the old app, not a white page.

   The lightness values are the OKLCH lightnesses of the six greys this file
   shipped with for two years (#060705, #0b0d0a, #111410, #171b15, #1e231b and
   the text ramp). They are transcribed, not re-invented: the dark theme after
   this rewrite is the same dark theme, to the nearest percent.

   `[data-theme^="dk-"]` is here so a dark theme can be applied to an ELEMENT,
   not only to the document — see the note on §4. Without it a dark preview tile
   sitting inside a light document would inherit the light ladder and render its
   palette on white.
   ========================================================================== */
:root, [data-theme^="dk-"] {
  color-scheme: dark;

  /* Surface ladder: back to front. In dark mode elevation means "lighter".
     These are the measured OKLCH lightnesses of #060705 #0b0d0a #111410
     #171b15 #1e231b — the greys this file shipped with — not new numbers.
     OKLCH lightness is perceptual and runs far higher than the sRGB byte
     suggests: #060705 is L 12.6%, not the ~5% an eyeballed guess produces.
     Guessing here is what crushed every surface to black on the first pass. */
  --l-bg-0: 12.58%;   /* app canvas          #060705 */
  --l-bg-1: 15.55%;   /* primary surface     #0b0d0a */
  --l-bg-2: 18.64%;   /* raised cards        #111410 */
  --l-bg-3: 21.56%;   /* overlays, menus     #171b15 */
  --l-bg-4: 24.82%;   /* hover on raised     #1e231b */

  /* Text ladder, likewise measured — from the ramp app.css rebuilt and then
     re-declared in a second `:root` further down the cascade, which is where
     these four actually came from until now:

       text-0  #e9e9e6  233   names, body copy
       text-1  #b6b6b2  182   secondary copy, quoted text
       text-2  #8e8e8a  142   meta — handles, counters, inactive tabs
       text-3  #7c7c78  124   faint — timestamps, denominators, units

     Primary-to-meta is 233→142 and the third step is a step you can see. Not
     pure white at the top: a 255 glyph on a near-black surface haloes, which is
     the same call X makes with #E7E9EA. */
  --l-text-0: 93.32%;
  --l-text-1: 77.51%;
  --l-text-2: 64.55%;
  --l-text-3: 58.51%;
  --l-raised-lift: 5.4%;   /* faint ink brightens on a card; see --text-3-raised */

  /* The colour every hairline, wash and bevel is a percentage of. */
  --ink: #ffffff;
  --ink-contrast: #000000;

  /* The ink ladder. Ten rungs replacing the thirty-four ad-hoc alphas that were
     scattered through the stylesheets — .045 and .05 and .055 sitting side by
     side was noise, never intent.

     Rungs 0–4 are washes (hover, zebra, chip fill). Rungs 5–8 are strokes, and
     they are pitched deliberately high: app.css had re-levelled the hairlines
     until the rule token cleared 45/255 on the column, because at the old
     weight the feed collapsed into one unbroken block of text. 5, 7 and 8 land
     exactly on the three weights that fix bought — .11, .19 and .28 — so
     folding those overrides in here loses nothing.

     0 and 9 are the two ends the first draft was missing, and they exist
     because snapping the real stylesheets to the ladder measured the cost of
     not having them:

       9  the strongest fills in the product — `.bmsearch` at .35, an active
          bookmark chip at .32 — had nowhere to go but .28, an 18/255 drop.
          That is a redesign, not a rounding.
       0  the faintest tint in the product — the unread-notification wash at
          .012 — had nowhere to go but .03. Only 4/255, but 2.5x, and on a tint
          that quiet the ratio is what the eye reads, not the difference.

     Every other value in the product lands within 8/255 of a rung, which is
     what makes the other 160 sites a snap rather than a change. */
  --ink-0: color-mix(in srgb, var(--ink) 1.5%, transparent);
  --ink-1: color-mix(in srgb, var(--ink) 3.0%, transparent);
  --ink-2: color-mix(in srgb, var(--ink) 4.5%, transparent);
  --ink-3: color-mix(in srgb, var(--ink) 6.0%, transparent);
  --ink-4: color-mix(in srgb, var(--ink) 8.0%, transparent);
  --ink-5: color-mix(in srgb, var(--ink) 11.0%, transparent);   /* line-soft   */
  --ink-6: color-mix(in srgb, var(--ink) 14.0%, transparent);
  --ink-7: color-mix(in srgb, var(--ink) 19.0%, transparent);   /* line        */
  --ink-8: color-mix(in srgb, var(--ink) 28.0%, transparent);   /* line-strong */
  --ink-9: color-mix(in srgb, var(--ink) 35.0%, transparent);

  /* Shadows are cast by absence of light, so in dark mode they are nearly
     opaque black and in light mode they are a soft tinted grey. */
  --shadow-h: 150;
  --shadow-c: 0.02;
  --shadow-l: 0%;
  --shadow-a1: 0.40;
  --shadow-a2: 0.45;
  --shadow-a3: 0.55;
  --shadow-a4: 0.66;

  /* How much of the theme's hue is allowed into the greys. Dark surfaces carry
     tint gracefully; light ones go dirty fast, so the light ladder scales this
     down rather than every theme having to know about it. */
  --c-scale-surf: 1;
  --c-scale-text: 1;

  /* Tint deepens with elevation. The old greys did this — #060705 sits at
     chroma .006 and #1e231b at .017 — and it is why the room reads as lit from
     within rather than as five flat greys. Per-rung multipliers keep that
     relationship while leaving a theme exactly one number (`--c-surf`) to turn
     the whole effect up or down. A monochrome theme sets `--c-surf: 0` and the
     ladder collapses to true neutral without needing to know these exist. */
  --k-bg-0: 0.80;
  --k-bg-1: 0.90;
  --k-bg-2: 1.15;
  --k-bg-3: 1.60;
  --k-bg-4: 2.05;

  /* Glass: a translucent pane of the primary surface. */
  --glass-a: 0.62;
  --glass-strong-a: 0.86;
  --scrim-a: 0.72;
}


/* ==========================================================================
   2. THE LIGHT LADDER
   --------------------------------------------------------------------------
   Not a mirror of the dark one, because elevation does not mirror. In dark
   mode a raised card is LIGHTER than the canvas and a hover is lighter still.
   In light mode a raised card is lighter than the canvas — it goes to white —
   but a hover has nowhere brighter to go, so it must DARKEN instead. That is
   why `--l-bg-4` is below `--l-bg-2` here and above it above: the ladder is a
   set of named jobs, not a monotonic ramp.

   THE `lt-` PREFIX. Every light theme's id starts with `lt-` and every dark
   theme's with `dk-`, and this selector matches on that prefix. It means a
   theme carries its own mode: setting `data-theme` is enough, and the mode can
   never disagree with the theme because nothing has to remember to set it.
   The alternative — a 30-entry id→mode map in the boot script — is a list that
   would be wrong the first time someone adds a theme and forgets to update it.
   `[data-mode]` still works on its own, for the OS preference and for anyone
   who wants to force one.
   ========================================================================== */
[data-mode="light"], [data-theme^="lt-"] {
  color-scheme: light;

  --l-bg-0: 96.4%;   /* canvas: a shade off white, so white cards read as cards */
  --l-bg-1: 99.0%;
  --l-bg-2: 100%;    /* raised cards are paper */
  --l-bg-3: 100%;    /* menus and inputs are paper too, held by a hairline */
  --l-bg-4: 94.6%;   /* hover DARKENS — the one rung that is not a mirror */

  --l-text-0: 21%;
  --l-text-1: 38%;
  --l-text-2: 49%;
  --l-text-3: 55%;
  --l-raised-lift: -5.4%;   /* on paper the card is BRIGHTER, so faint ink darkens */

  --ink: #0a0c08;
  --ink-contrast: #ffffff;

  /* Black on white bites harder than white on black at the same alpha — the
     eye reads a dark line on a bright field far more readily than the reverse.
     Every rung is pulled down so the two modes feel equally quiet. */
  --ink-0: color-mix(in srgb, var(--ink) 1.2%, transparent);
  --ink-1: color-mix(in srgb, var(--ink) 2.4%, transparent);
  --ink-2: color-mix(in srgb, var(--ink) 3.6%, transparent);
  --ink-3: color-mix(in srgb, var(--ink) 5.0%, transparent);
  --ink-4: color-mix(in srgb, var(--ink) 6.5%, transparent);
  --ink-5: color-mix(in srgb, var(--ink) 9.0%, transparent);
  --ink-6: color-mix(in srgb, var(--ink) 11.5%, transparent);
  --ink-7: color-mix(in srgb, var(--ink) 15.0%, transparent);
  --ink-8: color-mix(in srgb, var(--ink) 22.0%, transparent);
  --ink-9: color-mix(in srgb, var(--ink) 27.5%, transparent);

  --shadow-l: 45%;
  --shadow-c: 0.03;
  --shadow-a1: 0.07;
  --shadow-a2: 0.09;
  --shadow-a3: 0.13;
  --shadow-a4: 0.18;

  --c-scale-surf: 0.42;
  --c-scale-text: 0.55;

  --glass-a: 0.72;
  --glass-strong-a: 0.90;
  --scrim-a: 0.42;
}


/* ==========================================================================
   3. THE DEFAULT SEEDS
   --------------------------------------------------------------------------
   What `[data-theme]` overrides. These reproduce the original chartreuse
   grow-room: a faint chlorophyll cast in the greys, lime as tone 1, HPS amber
   as tone 2, LED violet as tone 3.

   A theme sets as few or as many of these as it has colours. A one-colour
   monochrome theme sets `--c-1: 0` and stops; a five-colour theme sets all
   five. The slots a theme leaves unset are filled by the cycling blocks in
   themes.css §7, not here — these are the DEFAULTS, so a theme that set only
   `--h-1` and relied on falling back would silently get amber for tone 2.
   ========================================================================== */
:root, [data-theme] {
  --h-surf: 132;   --c-surf: 0.008;
  --h-text: 106;   --c-text: 0.0045;

  /* The five tones are the measured OKLCH of the five colours this product
     already used, so the default theme is the old theme rather than a
     lookalike. Every one of the numbers below came out of a converter. */
  --h-1: 130.2;  --c-1: 0.2165;  --l-1: 91.75%;  /* chartreuse  #b2ff4d — the signal */
  --h-2: 88.1;   --c-2: 0.1520;  --l-2: 87.41%;  /* HPS amber   #ffcf4d — warmth, cure */
  --h-3: 300.4;  --c-3: 0.1663;  --l-3: 72.95%;  /* LED violet  #b98cff — night, spectra */
  --h-4: 154.6;  --c-4: 0.1722;  --l-4: 80.24%;  /* chlorophyll #4ade8b */
  --h-5: 339.8;  --c-5: 0.2139;  --l-5: 74.34%;  /* anthocyanin #ff6bd6 */

  /* Semantic hues. Deliberately NOT theme-seeded by default: a red that stops
     meaning "destructive" because the theme is blue has cost the member the
     one thing colour was carrying. A theme may still override them, and the
     monochrome themes below do exactly that on purpose — but they have to say
     so rather than get it by accident. */
  --h-danger: 18.6;   --c-danger: 0.1942;  --l-danger: 69.74%;  /* #ff5f6d */
  --h-warn:   88.1;   --c-warn:   0.1520;  --l-warn:   87.41%;  /* #ffcf4d */
  --h-ok:     154.6;  --c-ok:     0.1722;  --l-ok:     80.24%;  /* #4ade8b */
  --h-info:   244.7;  --c-info:   0.1382;  --l-info:   75.01%;  /* #58b6ff */
  --h-live:   12.6;   --c-live:   0.2292;  --l-live:   66.30%;  /* #ff3b6b */

  /* Font set. A theme may point these at a different stack; the sets live in
     §8 and are all locally available or system-resident, so switching one
     costs no download. */
  --font-ui:    var(--fs-ui, 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif);
  --font-dis:   var(--fs-dis, 'InterDisplay', 'Inter', system-ui, sans-serif);
  --font-brand: var(--fs-brand, 'Shrikhand', 'InterDisplay', serif);
  --font-mono:  var(--fs-mono, ui-monospace, 'SF Mono', SFMono-Regular, Menlo, monospace);
}


/* ==========================================================================
   4. THE DERIVATION
   --------------------------------------------------------------------------
   Everything the rest of the product actually names. Declaration order is
   irrelevant here — a custom property resolves against whatever its references
   compute to on the same element, not against what was written above it — so
   this block reads top-down as a description rather than as a sequence.

   IT RUNS ON ANY ELEMENT CARRYING `data-theme`, not just on the document.
   Custom properties inherit, so a plain <div data-theme="lt-rose"> inside a
   dark document would otherwise inherit the ALREADY-RESOLVED `--bg-0` from
   <html> and change nothing but the seeds — which is a confusing failure,
   because the seeds are right and the colours are the old ones. Re-deriving on
   the element makes the subtree genuinely that theme. The settings gallery is
   built on this: every tile is a real miniature of the theme it offers, not a
   row of swatches that has to be kept in step with the CSS by hand.

   The selector is `[data-theme]`, not `*` — the derivation only runs where
   someone asked for it.
   ========================================================================== */
:root, [data-theme] {
  /* ---------- Surfaces ------------------------------------------------- */
  --void:         #000000;
  --bg-0:         oklch(var(--l-bg-0) calc(var(--c-surf) * var(--c-scale-surf) * var(--k-bg-0)) var(--h-surf));
  --bg-1:         oklch(var(--l-bg-1) calc(var(--c-surf) * var(--c-scale-surf) * var(--k-bg-1)) var(--h-surf));
  --bg-2:         oklch(var(--l-bg-2) calc(var(--c-surf) * var(--c-scale-surf) * var(--k-bg-2)) var(--h-surf));
  --bg-3:         oklch(var(--l-bg-3) calc(var(--c-surf) * var(--c-scale-surf) * var(--k-bg-3)) var(--h-surf));
  --bg-4:         oklch(var(--l-bg-4) calc(var(--c-surf) * var(--c-scale-surf) * var(--k-bg-4)) var(--h-surf));

  --scrim:        oklch(from var(--bg-0) l c h / var(--scrim-a));
  --glass:        oklch(from var(--bg-1) l c h / var(--glass-a));
  --glass-strong: oklch(from var(--bg-1) l c h / var(--glass-strong-a));

  /* The five surfaces the shell names by job rather than by depth. These lived
     in app.css as hex until now, which is precisely why a theme change left the
     rail, the column and the sticky header untouched — the three largest areas
     on screen. They are the ladder plus a nudge, so they move with the theme
     and keep the exact separation the layout was tuned against.

     Inputs are RECESSED, not raised: that is the thing that makes a field read
     as a field rather than as one more card. */
  --surface-room:   var(--bg-0);                                 /* rail + aside: the room  */
  --surface-column: oklch(from var(--bg-1) calc(l + 0.02) c h);  /* the lit reading plane   */
  --surface-header: var(--bg-2);                                 /* sticky chrome           */
  --surface-inset:  var(--bg-3);                                 /* recessed wells          */
  --surface-raised: oklch(from var(--bg-3) calc(l + 0.016) c h); /* fields                  */

  --raise:  0 1px 0 var(--ink-3) inset, 0 8px 22px var(--shadow-3);
  --recess: inset 0 1px 2px var(--shadow-3);

  /* ---------- Hairlines & strokes -------------------------------------- */
  --line:         var(--ink-7);
  --line-strong:  var(--ink-8);
  --line-soft:    var(--ink-5);
  --line-accent:  color-mix(in srgb, var(--accent) 28%, transparent);
  --bevel:        inset 0 1px 0 var(--ink-3);

  /* ---------- Text ------------------------------------------------------ */
  /* The ramp is near-neutral by design. Every grey used to carry a green cast,
     and by the time the accent arrived there was no true neutral left for it to
     read against, so the lime stopped meaning "interactive". Colour in this
     product is a signal; the text is the page. */
  --text-0:       oklch(var(--l-text-0) calc(var(--c-text) * var(--c-scale-text)) var(--h-text));
  --text-1:       oklch(var(--l-text-1) calc(var(--c-text) * var(--c-scale-text)) var(--h-text));
  --text-2:       oklch(var(--l-text-2) calc(var(--c-text) * var(--c-scale-text)) var(--h-text));
  --text-3:       oklch(var(--l-text-3) calc(var(--c-text) * var(--c-scale-text)) var(--h-text));
  --text-inverse: var(--ink-contrast);

  /* The faint grey is tuned against the canvas. On a raised surface the ground
     comes up under it and the same ink loses about half a point of contrast, so
     cards re-point `--text-3` at this instead of the whole product compromising
     on one value. Contrast is a relationship, not a colour — which is also why
     this is a step along the ladder rather than a hex: on a light theme the
     ground comes up the other way and the step has to go down. */
  --text-3-raised: oklch(calc(var(--l-text-3) + var(--l-raised-lift)) calc(var(--c-text) * var(--c-scale-text)) var(--h-text));

  /* ---------- The palette ----------------------------------------------
     Five tones, each with the same six-part interface. A theme that only has
     one colour points tones 2..5 at tone 1 (see §7 of themes.css) — the slots
     always exist so no component has to ask how many colours it may use.

     `-ink` is the text that goes ON a fill of that tone, and it is computed,
     not chosen: `clamp(0, (l - 0.58) * -1000, 1)` collapses to 0 above 58%
     lightness and to 1 below it, which is black text on a bright tone and white
     text on a deep one. It costs no JavaScript and it stays correct for a
     colour a member invented five minutes ago, which a hand-picked value could
     not.

     WHY 0.58 AND NOT 0.62. Black and white ink give equal contrast on a fill
     whose relative luminance is 0.179 — the crossover — and either side of it
     the correct choice always clears 4.58:1. So a failure here is never the
     colour's fault, it is the threshold picking the wrong ink. 0.62 was an
     eyeball; it put saturated yellow-greens on the wrong side, because a
     high-chroma chartreuse at 60% OKLCH lightness is far brighter in luminance
     terms than a neutral grey at the same number. 0.58 was measured across all
     thirty-six themes' hundred and eighty tones and is the value at which none
     of them fails. */
  --tone-1:       oklch(var(--l-1) var(--c-1) var(--h-1));
  --tone-1-hover: oklch(from var(--tone-1) calc(l + 0.06) c h);
  --tone-1-press: oklch(from var(--tone-1) calc(l - 0.07) c h);
  --tone-1-dim:   color-mix(in srgb, var(--tone-1) 14%, transparent);
  --tone-1-glow:  color-mix(in srgb, var(--tone-1) 34%, transparent);
  --tone-1-ink:   oklch(from var(--tone-1) clamp(0, (l - 0.58) * -1000, 1) 0 0);

  --tone-2:       oklch(var(--l-2) var(--c-2) var(--h-2));
  --tone-2-hover: oklch(from var(--tone-2) calc(l + 0.06) c h);
  --tone-2-press: oklch(from var(--tone-2) calc(l - 0.07) c h);
  --tone-2-dim:   color-mix(in srgb, var(--tone-2) 14%, transparent);
  --tone-2-glow:  color-mix(in srgb, var(--tone-2) 34%, transparent);
  --tone-2-ink:   oklch(from var(--tone-2) clamp(0, (l - 0.58) * -1000, 1) 0 0);

  --tone-3:       oklch(var(--l-3) var(--c-3) var(--h-3));
  --tone-3-hover: oklch(from var(--tone-3) calc(l + 0.06) c h);
  --tone-3-press: oklch(from var(--tone-3) calc(l - 0.07) c h);
  --tone-3-dim:   color-mix(in srgb, var(--tone-3) 14%, transparent);
  --tone-3-glow:  color-mix(in srgb, var(--tone-3) 34%, transparent);
  --tone-3-ink:   oklch(from var(--tone-3) clamp(0, (l - 0.58) * -1000, 1) 0 0);

  --tone-4:       oklch(var(--l-4) var(--c-4) var(--h-4));
  --tone-4-hover: oklch(from var(--tone-4) calc(l + 0.06) c h);
  --tone-4-press: oklch(from var(--tone-4) calc(l - 0.07) c h);
  --tone-4-dim:   color-mix(in srgb, var(--tone-4) 14%, transparent);
  --tone-4-glow:  color-mix(in srgb, var(--tone-4) 34%, transparent);
  --tone-4-ink:   oklch(from var(--tone-4) clamp(0, (l - 0.58) * -1000, 1) 0 0);

  --tone-5:       oklch(var(--l-5) var(--c-5) var(--h-5));
  --tone-5-hover: oklch(from var(--tone-5) calc(l + 0.06) c h);
  --tone-5-press: oklch(from var(--tone-5) calc(l - 0.07) c h);
  --tone-5-dim:   color-mix(in srgb, var(--tone-5) 14%, transparent);
  --tone-5-glow:  color-mix(in srgb, var(--tone-5) 34%, transparent);
  --tone-5-ink:   oklch(from var(--tone-5) clamp(0, (l - 0.58) * -1000, 1) 0 0);

  /* ---------- Accent = tone 1 ------------------------------------------
     The accent is not a sixth colour, it is the name the product calls tone 1
     by. Keeping it as an alias is what lets a member override the accent alone
     without dislodging the theme around it. */
  --accent:       var(--tone-1);
  --accent-hover: var(--tone-1-hover);
  --accent-press: var(--tone-1-press);
  --accent-ink:   var(--tone-1-ink);
  --accent-dim:   var(--tone-1-dim);
  --accent-glow:  var(--tone-1-glow);

  /* ---------- Semantic -------------------------------------------------- */
  --danger:       oklch(var(--l-danger) var(--c-danger) var(--h-danger));
  --danger-dim:   color-mix(in srgb, var(--danger) 14%, transparent);
  --danger-ink:   oklch(from var(--danger) clamp(0, (l - 0.58) * -1000, 1) 0 0);
  --warn:         oklch(var(--l-warn) var(--c-warn) var(--h-warn));
  --warn-dim:     color-mix(in srgb, var(--warn) 14%, transparent);
  --ok:           oklch(var(--l-ok) var(--c-ok) var(--h-ok));
  --ok-dim:       color-mix(in srgb, var(--ok) 14%, transparent);
  --info:         oklch(var(--l-info) var(--c-info) var(--h-info));
  --info-dim:     color-mix(in srgb, var(--info) 14%, transparent);
  --live:         oklch(var(--l-live) var(--c-live) var(--h-live));

  /* ---------- Legacy ramp names ----------------------------------------
     Seventy-seven call sites still name a pigment rather than a role —
     `--lime-600` where they meant "the accent, pressed", `--amber-400` where
     they meant "tone 2". They are aliased onto the palette rather than deleted
     so that this rewrite changes no other file, and they are steps of the
     THEME's tones now, which is why an amber chip goes blue on a blue theme
     instead of staying stubbornly amber. Prefer `--tone-N-*` in new code. */
  --lime-100:  oklch(from var(--tone-1) calc(l + 0.09) calc(c * 0.30) h);
  --lime-200:  oklch(from var(--tone-1) calc(l + 0.06) calc(c * 0.55) h);
  --lime-300:  var(--tone-1-hover);
  --lime-400:  var(--tone-1);
  --lime-500:  var(--tone-1-press);
  --lime-600:  oklch(from var(--tone-1) calc(l - 0.16) c h);
  --lime-700:  oklch(from var(--tone-1) calc(l - 0.30) calc(c * 0.90) h);
  --lime-900:  oklch(from var(--tone-1) calc(l - 0.62) calc(c * 0.55) h);
  --amber-400: var(--tone-2);
  --amber-600: oklch(from var(--tone-2) calc(l - 0.18) c h);
  --amber-dim: var(--tone-2-dim);
  --violet-400: var(--tone-3);
  --violet-600: oklch(from var(--tone-3) calc(l - 0.18) c h);
  --violet-dim: var(--tone-3-dim);

  /* ---------- Elevation ------------------------------------------------- */
  --shadow-1: oklch(var(--shadow-l) var(--shadow-c) var(--shadow-h) / var(--shadow-a1));
  --shadow-2: oklch(var(--shadow-l) var(--shadow-c) var(--shadow-h) / var(--shadow-a2));
  --shadow-3: oklch(var(--shadow-l) var(--shadow-c) var(--shadow-h) / var(--shadow-a3));
  --shadow-4: oklch(var(--shadow-l) var(--shadow-c) var(--shadow-h) / var(--shadow-a4));

  --e-1: 0 1px 2px var(--shadow-1);
  --e-2: 0 2px 8px var(--shadow-2), 0 1px 2px var(--shadow-1);
  --e-3: 0 8px 28px var(--shadow-3), 0 2px 6px var(--shadow-1);
  --e-4: 0 24px 64px var(--shadow-4), 0 6px 16px var(--shadow-2);
  --e-glow: 0 0 0 1px var(--line-accent), 0 8px 32px var(--accent-glow);
}


/* ==========================================================================
   5. INVARIANTS
   --------------------------------------------------------------------------
   Type scale, rhythm, radius, motion, layout. A theme may adjust the radius
   and the font stack — those are in §6 and §8 — but the 4px rhythm and the z
   order are structure, not style, and no theme gets a vote.
   ========================================================================== */
:root {
  /* Fluid, but locked to a 4px rhythm at the common sizes */
  --t-11: 11px;  --lh-11: 14px;
  --t-12: 12px;  --lh-12: 16px;
  --t-13: 13px;  --lh-13: 18px;
  --t-14: 14px;  --lh-14: 20px;
  --t-15: 15px;  --lh-15: 22px;   /* body / post text */
  --t-17: 17px;  --lh-17: 24px;
  --t-20: 20px;  --lh-20: 26px;
  --t-24: 24px;  --lh-24: 30px;
  --t-31: 31px;  --lh-31: 36px;
  --t-40: 40px;  --lh-40: 44px;
  --t-56: 56px;  --lh-56: 56px;

  --tracking-tight: -0.014em;
  --tracking-display: -0.028em;
  --tracking-wide: 0.06em;

  --s-1: 2px;  --s-2: 4px;  --s-3: 6px;  --s-4: 8px;  --s-5: 12px;
  --s-6: 16px; --s-7: 20px; --s-8: 24px; --s-9: 32px; --s-10: 40px;
  --s-11: 48px; --s-12: 64px; --s-13: 80px; --s-14: 112px;

  --ease-out:  cubic-bezier(.16, 1, .3, 1);        /* entrances, expands */
  --ease-in-out: cubic-bezier(.65, 0, .35, 1);     /* moves, morphs */
  --ease-spring: linear(0,.0063 1.51%,.025 3.11%,.099 6.4%,.2107 9.94%,.5417 18.99%,.6829 23.28%,.796 27.66%,.8888 32.29%,.9245 34.72%,.9542 37.32%,.9779 40.16%,.9958 43.34%,1.0081 46.94%,1.0157 51.29%,1.0176 58.24%,1.0027 79.32%,1);
  --d-1: 90ms; --d-2: 140ms; --d-3: 220ms; --d-4: 340ms; --d-5: 520ms; --d-6: 820ms;

  --rail-w: 88px;          /* collapsed icon rail */
  --rail-w-open: 268px;    /* expanded nav */
  --col-main: 640px;
  --col-aside: 372px;
  --gutter: 28px;
  --header-h: 56px;
  --z-gl: 0; --z-content: 10; --z-sticky: 100; --z-nav: 200;
  --z-overlay: 800; --z-modal: 900; --z-toast: 1000;
}


/* ==========================================================================
   6. SHAPE — the radius scale, as a customisable axis
   --------------------------------------------------------------------------
   One multiplier drives the whole scale so the relationship between a chip and
   a modal survives being made square or made round.

   `--r-pill` USED TO BE EXEMPT FROM THAT AXIS, AND THAT WAS THE BUG.

   The reasoning for the exemption was that a pill at a 0 multiplier is a
   rectangle, i.e. a different component rather than a squarer version of the
   same one. True as far as it goes, but it quietly opted the loudest controls in
   the product out of the member's choice. `Button()` in web/lib/ui.js takes
   `pill = true` by DEFAULT, so EVERY button read the exempt token. Choosing
   "square" produced a sign-in screen — measured on production — with 0px fields,
   0px provider buttons and a 999px primary CTA sitting on top of them. On the
   feed it was worse: a square "Post" shoulder to shoulder with three fully round
   composer chips. The setting is asked to change the theme completely, not the
   buttons only, and a token nothing could reach was the reason it didn't.

   So the pill joins the axis. What it does NOT do is join the MULTIPLIER: 999 ×
   0.55 is 549, which on a 40px control is still a full semicircle, so "soft"
   would have been indistinguishable from "round" — a rounding error where a
   visible third answer was wanted. The pill gets a ladder of its own instead,
   and 12px at "soft" is a squircle you can actually see.

   Round and pill stay at 999px, exactly as they render today, so this changes
   what "square" and "soft" mean and not what the product looks like out of the
   box.

   Everything reading this token follows, including avatars, the FAB and the
   small round indicators. That is deliberate and it is the point: a square
   avatar on a square theme is coherent, whereas one circle left behind in a
   square layout reads as a bug. Nothing here depends on being a circle to stay
   legible — the dots are 5–7px, and a 5px square is still a dot. */
:root { --r-scale: 1; }
[data-shape="square"] { --r-scale: 0; }
[data-shape="soft"]   { --r-scale: 0.55; }
[data-shape="round"]  { --r-scale: 1; }
[data-shape="pill"]   { --r-scale: 1.9; }

:root {
  --r-xs: calc(6px  * var(--r-scale));
  --r-sm: calc(10px * var(--r-scale));
  --r-md: calc(14px * var(--r-scale));
  --r-lg: calc(20px * var(--r-scale));
  --r-xl: calc(28px * var(--r-scale));
  /* Its own ladder rather than the multiplier — see above. */
  --r-pill: 999px;
}
[data-shape="square"] { --r-pill: 0px; }
[data-shape="soft"]   { --r-pill: 12px; }


/* ==========================================================================
   7. SURFACE TREATMENT — how a card is drawn
   --------------------------------------------------------------------------
   Four ways to say "this is a distinct object": fill it, outline it, lift it,
   or frost it. These set the primitives that `.card` and its equivalents read
   in app.css; nothing here paints anything on its own.
   ========================================================================== */
:root {
  --card-bg:     var(--bg-2);
  --card-line:   1px solid var(--line);
  --card-shadow: var(--e-1);
  --card-bevel:  var(--bevel);
}
[data-card="flat"] {
  --card-bg: var(--bg-2);
  --card-line: 1px solid transparent;
  --card-shadow: none;
  --card-bevel: none;
}
[data-card="outline"] {
  --card-bg: transparent;
  --card-line: 1px solid var(--line-strong);
  --card-shadow: none;
  --card-bevel: none;
}
[data-card="elevated"] {
  --card-bg: var(--bg-2);
  --card-line: 1px solid transparent;
  --card-shadow: var(--e-3);
  --card-bevel: var(--bevel);
}
[data-card="glass"] {
  --card-bg: var(--glass);
  --card-line: 1px solid var(--line);
  --card-shadow: var(--e-2);
  --card-bevel: var(--bevel);
}


/* ==========================================================================
   8. FONT SETS
   --------------------------------------------------------------------------
   Every one of these is either shipped in /assets/fonts or already on the
   machine. Nothing here costs a download, which is the constraint that decided
   the list: a theme that changes the typeface should cost nothing at all.
   ========================================================================== */
[data-font="inter"] {
  --fs-ui:  'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
  --fs-dis: 'InterDisplay', 'Inter', system-ui, sans-serif;
}
[data-font="system"] {
  --fs-ui:  -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, system-ui, sans-serif;
  --fs-dis: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, system-ui, sans-serif;
}
[data-font="serif"] {
  --fs-ui:  'Iowan Old Style', 'Palatino Linotype', Palatino, Georgia, 'Times New Roman', serif;
  --fs-dis: 'Iowan Old Style', Palatino, Georgia, serif;
  --fs-brand: 'Iowan Old Style', Palatino, Georgia, serif;
}
[data-font="mono"] {
  --fs-ui:  ui-monospace, 'SF Mono', SFMono-Regular, Menlo, Consolas, monospace;
  --fs-dis: ui-monospace, 'SF Mono', SFMono-Regular, Menlo, Consolas, monospace;
  --fs-brand: ui-monospace, 'SF Mono', Menlo, monospace;
}
[data-font="rounded"] {
  --fs-ui:  ui-rounded, 'SF Pro Rounded', 'Hiragino Maru Gothic ProN', 'Inter', system-ui, sans-serif;
  --fs-dis: ui-rounded, 'SF Pro Rounded', 'InterDisplay', system-ui, sans-serif;
}


/* ==========================================================================
   9. ACCESSIBILITY OVERRIDES
   --------------------------------------------------------------------------
   These work on any of the thirty themes now, because they move the ladder
   rather than naming a colour. `prefers-contrast: more` used to hardcode five
   greys and therefore silently reimposed the dark theme on anyone who asked
   for contrast.
   ========================================================================== */
@media (prefers-contrast: more) {
  /* Same rungs and same numbers as `html.more-contrast` in settings.css — the
     OS preference and the in-app toggle are two switches on one dimmer, and if
     they disagreed, turning the toggle off on a machine that asks for contrast
     would visibly change the page. */
  :root, [data-theme^="dk-"] {
    --ink-5: color-mix(in srgb, var(--ink) 15%, transparent);
    --ink-7: color-mix(in srgb, var(--ink) 26%, transparent);
    --ink-8: color-mix(in srgb, var(--ink) 38%, transparent);
    --l-text-1: 89%;
    --l-text-2: 74.5%;
    --l-text-3: 66.5%;
  }
  [data-mode="light"], [data-theme^="lt-"] {
    --l-text-1: 27%;
    --l-text-2: 37%;
    --l-text-3: 44%;
  }
}

@media (prefers-reduced-motion: reduce) {
  :root { --d-1: 1ms; --d-2: 1ms; --d-3: 1ms; --d-4: 1ms; --d-5: 1ms; --d-6: 1ms; }
}
