:root {
  /* Neutral foundations: four surfaces and three foregrounds per mode.
     `--bg-overlay` is the surface for chrome that *floats* — menus, popovers,
     select, tooltips. It is deliberately not `--bg-elevated`: that one is an
     inline fill — a chip, a count badge, a card that sits flush in the page —
     where a strong lift is the point. A floating surface already has
     the shadow stack doing the separating, so it only needs enough tone to
     read as detached. At `--bg-elevated` a dark menu opened over the sidebar
     stepped 1.25:1 — the loudest tonal edge in the app, against ~1.03:1 for
     every other surface boundary. 0.215 lands that step at 1.12:1.

     Light now has four of these too. It used to have three: `--bg-elevated`
     and `--bg-overlay` were both pure white, 1.00:1 apart, because the page
     underneath them was 0.99 and white has nowhere higher to go. Anything
     needing `--bg-elevated`'s lift in light got none of it, and each consumer
     had to patch around that on its own — `.settings-sync-device__badge`
     stepping *down* into `--todo-row-selected`, `.arg-avatar` growing a
     light-only border (docs/color-palette-audit.md Finding 5).

     The ramp moved down instead, so white is reserved for exactly one tier —
     floating chrome — and nothing in-page is allowed to reach it. That is not
     mainly about stacking more surfaces: under the rule in
     docs/elevation-light-plan.md a panel and the control on it are *allowed*
     to share a fill, told apart by edge and shadow. It is about leaving the
     *interaction* scale somewhere to move. Hover in light is black ink, so on
     a white surface it could only travel downward, and a hovered row in a
     popover ended up darker than the modal the popover floated over — the
     strongest depth cue in the app pointing backwards, on every mouse move. */
  --dark-bg-primary: oklch(0.168 0.006 265);
  --dark-bg-secondary: oklch(0.148 0.006 265);
  /* An inline plate, not a rung on the elevation ladder. A chip, a count
     badge, a dragged row lifting off the list — objects that sit *in* the page
     and need a strong lift to read as objects. No modal, panel or popover uses
     it, and none should: a structural surface takes --bg-overlay if it floats
     and an edge if it does not (docs/elevation-dark-plan.md Step 1 removed the
     last fill-based in-page tier).

     Light cannot keep that separation as cleanly, and this is the one place
     the elevation work did not finish. There `--bg-elevated` is both the plate
     *and* the raised in-page surface, so a chip drawing it on a --surface-panel
     card lands on its own colour, 1.000. Two consumers hit this and both carry
     a light-only patch: .settings-sync-device__badge steps down into
     --todo-row-selected, .arg-avatar grows a border. The fix is a separate
     --surface-plate token that lifts in dark and recesses in light, so the
     plate stops sharing a name with the surface it sits on; see
     docs/elevation-light-plan.md "Consumer audit". */
  --dark-bg-elevated: oklch(0.255 0.006 265);
  /* 0.235, up from 0.215. The note above argues 0.215 down from --bg-elevated
     (0.255) because a menu over the sidebar stepped 1.25:1, the loudest tonal
     edge in the app. That still holds against 0.255; it went one step too far.
     At 0.215 the float tier sat 1.095 over the pane, which a hovered row
     (1.081) all but matched — 1.013 between a floating surface and a pointer
     state, which is nothing. 0.235 lands the tier at 1.150 over the pane and
     1.065 clear of a hovered row: still well under the 1.25 this was reacting
     to, and far enough that the float tier has a tone of its own again.
     See docs/elevation-dark-plan.md Step 3. */
  --dark-bg-overlay: oklch(0.235 0.006 265);
  --dark-text-primary: oklch(0.965 0.003 265);
  --dark-text-secondary: oklch(0.66 0.012 265);
  /* Raised from 0.48. At 0.48 the token cleared 4.5:1 on none of the four
     dark surfaces — 2.94:1 on --bg-primary, down to 2.42:1 on --bg-elevated
     — despite carrying real prose: comment timestamps, table headers,
     settings meta values, search-result paths. 0.632 is the lightness the
     worst ground (--bg-elevated) needs for 4.5:1, so every surface clears
     the floor (4.54:1–5.68:1). It costs the step to --text-secondary: 0.66
     to 0.632 is a real but modest gap (1.12:1 read against each other),
     down from dark's previous much wider margin. See docs/color-palette-audit.md
     Finding 1. */
  --dark-text-tertiary: oklch(0.632 0.012 265);
  /* Lightness only, dropped from 0.76 — chroma and hue are untouched. At 0.76
     this read 9.41:1 against --bg-primary while --dark-danger read 5.07:1, a
     1.9x gap between two colours meaning opposite things, and the gap
     inverted in light (danger read louder there). 0.60 is the L that lands
     success at 5.14:1 — parity with danger, matching the "L ≈ 0.60" option
     in docs/color-palette-audit.md Finding 3 rather than the milder
     half-step also offered there, since keeping the original chroma this
     close to --accent's own lightness (0.67) already tightens their oklab
     distance from 0.097 to 0.078 — a real but modest cost to Finding 8,
     worth taking for a defect this large rather than avoided by picking a
     halfway point that still leaves success shouting over danger. */
  --dark-success: oklch(0.6 0.13 148);
  /* Scarlet — a red carrying just enough orange to stay warm. Hue 31 is where
     #FF2400 lands in OKLCH, and dark sits on the gamut cusp (L 0.64 is where
     sRGB allows the most chroma at this hue, 0.246) because scarlet's depth is
     carried by saturation rather than darkness.

     `-secondary` is the muted sibling used for destructive *text* — menu items,
     badges, the delete button's label. It used to be derived as 62% of danger
     mixed into --text-secondary, which capped this token: mixing a saturated
     red into grey darkens it, so every point of chroma added here pushed
     destructive menu text toward the 4.5:1 floor and dark could not go past
     0.235. Making it its own value unties the two. It is now a *lighter* red
     rather than a greyed one, which is both more saturated and more legible
     (6.05:1 on the menu surface, up from 4.57:1). Keep it distinct from
     --danger: the point is that destructive controls read clearly without
     wearing a full-strength danger fill.

     Moved a quarter of the way toward --danger from its former 0.7/0.19: at
     that value the muted sibling was reading as a coral rather than as a red
     with the alarm turned down, most visibly on the selected Overdue row,
     where it sat beside the accent as a second warm hue instead of a status.
     The midpoint (0.67/0.2175) was tried first and overshot — close enough to
     --danger that the muted sibling stopped reading as muted. Half that step
     lands the tone back in the danger family while keeping the gap that makes
     destructive text legible.

     Every step toward --danger costs contrast, since the secondary moves
     toward the surface in both themes at once: the floor is dark on
     --bg-elevated, 5.44:1 originally, 4.78:1 at the midpoint, 5.10:1 here.
     Going the full distance to --danger would put that floor at 4.18:1, under
     AA — which is the hard limit on how far this token can travel. */
  --dark-danger: oklch(0.64 0.245 31);
  --dark-danger-secondary: oklch(0.685 0.204 31);
  /* Overdue is a standing state, not an event. A to-do can sit in it for a
     week, and the section that collects those to-dos is in the sidebar all
     day — at --danger-secondary's full 0.19 it reads as an alarm going off
     rather than a fact about the list. Same hue and same lightness, chroma
     down a quarter: still unmistakably the danger family, no longer shouting
     from the corner of the eye. Authored rather than mixed toward a grey,
     which would drag the hue toward the neutral ramp's blue.

     The quarter was measured against --danger-secondary at 0.7/0.19, which has
     since taken a step toward --danger above; this token stayed put and is no
     longer a derivation of it.

     It no longer dresses the Overdue section either. That identity is one
     colour now — --danger-secondary, what the sidebar and the pane picker were
     already promoting a selected row to — because this token was reaching only
     the fallback glyph and the section editor's Default swatch, which left the
     editor advertising a red the sidebar never painted. What remains here is
     the mini-calendar's deadline dot, which wants exactly this: the danger
     family with the alarm turned down, on a grid of dots read at a glance.
     Worth folding into the dot's own name if nothing else claims it. */
  --dark-overdue: oklch(0.7 0.1425 31);
  /* Was `color-mix(in oklch, var(--success) 50%, var(--danger))`, which
     resolved to oklch(0.7 0.1875 89.5) — outside sRGB. The browser silently
     clipped it to oklch(0.704 0.145 82.0), discarding a quarter of the
     requested chroma and drifting the hue 7.5° off the request, to a
     different clip point than light's (see below), so the two themes
     disagreed about what warning looked like. Authored directly instead, at
     hue 72 — closer to the sRGB amber cusp than 89.5 (0.150 max chroma
     available vs 0.143) — so both themes now share one hue exactly, the way
     every other status pair already does. 0.148 sits 0.002 under the true
     cusp (0.1499) as a margin against engine-to-engine gamut rounding, the
     same margin --danger takes by *not* leaving one — that token pins to
     the exact cusp deliberately, which is what caused this bug when applied
     to a color-mix() result instead of an authored one. See
     docs/color-palette-audit.md Finding 2. */
  --dark-warning: oklch(0.7 0.148 72);

  /* The pane and modal ground. Was 0.99 — near enough to white that the two
     tiers above it had nowhere to go. */
  --light-bg-primary: oklch(0.95 0.002 265);
  /* The app shell and sidebar, the deepest ground. Was 0.945, itself already
     one correction of an earlier 0.975: light's ramp used to span 2.5 points
     of lightness against dark's 10.7, so no layer boundary registered. The
     whole ramp now spans 8 points (0.92 / 0.95 / 0.985 / 1.0), which is close
     to dark's spread rather than a third of it. */
  --light-bg-secondary: oklch(0.92 0.003 265);
  /* In-page raised: panels, cards, controls. Was pure white; now one step
     under it, which is what leaves room for hover to darken a raised surface
     without dropping it below the ground it rose from. The margin is thin —
     the modal-to-raised step is 1.108 against a 1.094 hover — so do not
     shallow this ramp without re-checking that pair. */
  --light-bg-elevated: oklch(0.985 0.002 265);
  /* Floating chrome only — menus, popovers, select, tooltips. This is the one
     tier that still gets pure white, and the only one allowed to: a floating
     surface has the shadow stack doing the separating, and reserving the top
     of the range for it is what keeps a hovered menu row above the modal
     beneath it instead of below it. Nothing in-page may use this value. */
  --light-bg-overlay: oklch(1 0 0);
  --light-text-primary: oklch(0.2 0.008 265);
  --light-text-secondary: oklch(0.44 0.012 265);
  /* Each time the ground deepens this ink has to follow it down: 0.56 to
     0.548 when the shell went to 0.975, 0.548 to 0.528 at 0.945, and now
     0.528 to 0.49 at 0.92, where 0.528 had fallen to 4.20:1. 0.49 clears the
     floor on all four grounds — 4.94:1 on the shell, 5.41:1 on the pane,
     6.00:1 on raised, 6.26:1 on white. Chroma and hue are untouched, so this
     is the same grey one step down. --text-primary (14.29:1 worst case) and
     --text-secondary (6.13:1) had margin to spare and do not move. */
  --light-text-tertiary: oklch(0.49 0.012 265);
  /* Lightness only, dropped from 0.56 — the separate half of Finding 3.
     0.56 read 4.25:1 on --bg-primary and 4.07:1 on --bg-secondary, below the
     4.5:1 floor whenever this token carries text rather than the 13-14px
     glyphs and fills it happens to be used on today. 0.52 cleared every
     surface then; against the deeper 0.92 shell it reads 4.08:1, so it takes
     the same kind of step the tertiary ink just did. 0.495 restores 4.54:1 on
     the shell, with the same chroma and hue as before. */
  --light-success: oklch(0.495 0.14 148);
  /* On paper the secondary has to go darker than --danger, not lighter, and at
     7.3:1 it was never the token holding saturation back. It takes the same
     quarter step as --dark-danger-secondary, in the same direction relative to
     --danger, so the two themes keep describing one colour: 0.47/0.135, a
     quarter of the way to 0.53/0.2. Light's floor lands on --bg-secondary at
     5.44:1. */
  --light-danger: oklch(0.53 0.2 31);
  --light-danger-secondary: oklch(0.485 0.151 31);
  /* Held at the value it was authored against, when the secondary was
     0.47/0.135 — a quarter off that chroma. See --dark-overdue: both overdue
     tokens still read as the danger family with the alarm down, but they are
     no longer derived from the secondary, and no longer colour the Overdue
     section — only the mini-calendar's deadline dot. */
  --light-overdue: oklch(0.47 0.101 31);
  /* Same fix as --dark-warning, same hue (72) so the two themes read as one
     colour rather than the gold/brown pair the old color-mix() clipped to —
     the derived value cleared AA here (4.60–4.80:1) but only by accident of
     where the gamut mapping happened to land, and it disagreed with dark
     about the hue by 12.8°. 0.114 sits under the cusp (0.1156) for the same
     engine-rounding margin as dark. Lightness has since dropped 0.54 to 0.515
     for the deeper 0.92 shell, where 0.54 read 4.09:1; 0.515 restores 4.54:1
     there. Hue and chroma are the part Finding 2 fixed and are unchanged.
     See docs/color-palette-audit.md Finding 2. */
  --light-warning: oklch(0.515 0.114 72);

  /* Appearance selector palette, dark. These are the authored swatches — the
     51 names in lib/accent-palette.ts, tuned against dark's near-black
     grounds, which is what they were picked on. Light gets its own set of
     lightnesses below; hue and chroma are shared, so a name means one colour
     in both themes, at the lightness its ground can carry. */
  --palette-cinnabar: oklch(0.6 0.16 25);
  --palette-rose-quartz: oklch(0.79 0.09 10);
  --palette-persimmon: oklch(0.68 0.16 45);
  --palette-apricot-cream: oklch(0.83 0.1 55);
  --palette-citrine: oklch(0.83 0.14 95);
  --palette-acid-lime: oklch(0.83 0.16 125);
  --palette-olive-grove: oklch(0.55 0.08 110);
  --palette-pine-needle: oklch(0.45 0.08 155);
  --palette-aurora: oklch(0.74 0.13 180);
  --palette-cerulean: oklch(0.68 0.13 230);
  --palette-pewter: oklch(0.62 0.03 240);
  --palette-sapphire: oklch(0.55 0.17 255);
  --palette-periwinkle: oklch(0.72 0.12 270);
  --palette-amethyst: oklch(0.61 0.18 305);
  --palette-twilight: oklch(0.45 0.1 285);
  --palette-magenta-bloom: oklch(0.62 0.18 340);
  --palette-lilac-mist: oklch(0.81 0.1 325);
}

/* Public headers retain the home's dark brand treatment, including when the
   content below them follows the light appearance. */
:root,
.nav {
  --palette-argon: oklch(0.67 0.15 160);
  --palette-seafoam: oklch(0.83 0.1 180);
}

/* Appearance selector palette, light.
 *
 * `--accent` is the only user-chosen colour in the app and it was the last
 * token here that did not vary by theme — one value asked to work on a 0.148
 * shell and a 0.92 one. It cannot. Measured against light's shell, 45 of the
 * 51 swatches fell under 4.5:1, and the pale end was not close: champagne
 * read 1.18:1, buttercream 1.16:1 — accent text that is, in practice, blank
 * page. The fixes already in globals.css are downstream of this and say so in
 * their own comments: `--focus-accent` clamps the accent's lightness before
 * using it as focus ink, and `--accent-foreground` picks its ink from the
 * accent's lightness rather than the theme's (docs/color-palette-audit.md
 * Finding 9). Both exist because the fill underneath them was wrong for the
 * ground; this is that fill, made right.
 *
 * Lightness only. Hue and chroma are untouched — the same rule the rest of
 * this file's light/dark pairs follow, and the reason a swatch keeps its
 * identity across the flip: light `argon` is the deeper green the light
 * wordmark already uses, not a different colour wearing the name.
 *
 * The map is `L_light = 0.50 - (0.87 - L_dark) * 0.4`: the whole authored
 * range (0.42-0.87) compressed into 0.32-0.50 rather than clamped to a
 * ceiling. Clamping was tried first and collapses the palette — hibiscus,
 * cinnabar, dusty-rose and rose-quartz all pile onto the cap, and the last
 * two, which share a chroma and sit 5 degrees apart, become the same colour.
 * The compression keeps every pair in the order it was authored in.
 *
 * The two numbers: 0.50 is the highest lightness that still clears 4.5:1 on
 * the 0.92 shell across all 51 hues (the tightest is 0.495 around H 160), so
 * the palest swatches land at 4.66-4.78:1 — AA, with the margin that reads as
 * thin because it is. Do not raise it, and re-check every swatch if the shell
 * ever deepens again. 0.4 is then what a 0.32 floor implies; it also keeps
 * the darkest swatches light enough that `--accent-foreground`'s white ink
 * clears 4.5:1 on every fill (worst 5.33:1), which the authored set failed at
 * both ends.
 *
 * Chroma is written as authored even where the darker lightness puts it
 * outside sRGB — the engine gamut-maps by reducing chroma, which is the same
 * value we would otherwise hard-code, and the contrast figures above are
 * measured after that mapping. The light wordmark's `oklch(0.52 0.15 160)`
 * is written the same way.
 *
 * Dark is unchanged. Five of its swatches have the mirror-image problem
 * (burgundy 2.18:1, aubergine 2.21:1, twilight 2.57:1, pine-needle 2.76:1,
 * stormcloud 3.31:1 against the dark shell) and want the same treatment from
 * the other end; that is a visible change to accents people are already
 * looking at, so it is its own pass, not a side effect of this one. */
html.light {
  --palette-cinnabar: oklch(0.392 0.16 25);
  --palette-rose-quartz: oklch(0.468 0.09 10);
  --palette-persimmon: oklch(0.424 0.16 45);
  --palette-apricot-cream: oklch(0.484 0.1 55);
  --palette-citrine: oklch(0.484 0.14 95);
  --palette-acid-lime: oklch(0.484 0.16 125);
  --palette-olive-grove: oklch(0.372 0.08 110);
  --palette-argon: oklch(0.42 0.15 160);
  --palette-pine-needle: oklch(0.332 0.08 155);
  --palette-aurora: oklch(0.448 0.13 180);
  --palette-seafoam: oklch(0.484 0.1 180);
  --palette-cerulean: oklch(0.424 0.13 230);
  --palette-pewter: oklch(0.4 0.03 240);
  --palette-sapphire: oklch(0.372 0.17 255);
  --palette-periwinkle: oklch(0.44 0.12 270);
  --palette-amethyst: oklch(0.384 0.18 305);
  --palette-twilight: oklch(0.332 0.1 285);
  --palette-magenta-bloom: oklch(0.4 0.18 340);
  --palette-lilac-mist: oklch(0.476 0.1 325);
}

/* The wordmark is drawn as lit slits. On black the gaps read as unlit and the
   letterforms fuse into a solid mark; on paper the same gaps read as page, so
   it fragments into pale slivers at 2.7:1 against the background. Light mode
   swaps in a variant with a deeper green (4.8:1), slightly wider bars and its
   own ground behind them. It lives here rather than in a page stylesheet
   because every surface — the app and all the marketing pages — imports this
   file, so both modes stay on one asset pair. */
html.light img[src$="argon-wordmark-thick.svg"] {
  content: url("/argon-wordmark-thick-light.svg");
}

/* The solid mark carries no slits, so it needs no structural variant — only an
   ink flip. At nav and footer sizes it is set in --text-primary rather than the
   brand green, which held only 2.6:1 as a 16px-tall mark; the green now appears
   at hero size, where it has the mass to carry it. */
html.light img[src$="argon-wordmark-solid.svg"] {
  content: url("/argon-wordmark-solid-light.svg");
}
