/*
 * tones.css
 * Single source of truth for tone semantics.
 *
 * Every tone sets two variables:
 *   --bg-mix      The tone hue
 *   --on-bg-mix   The contrast color for text on that hue
 *
 * Every component (btn, pill, badge, card, table, dialog, field…) reads
 * these two vars. Adding a new tone = one line here, zero component edits.
 *
 * ── Why these are registered as non-inheriting ──────────────────────
 *
 * Custom properties inherit by default, and `var(--bg-mix, fallback)`
 * only reaches its fallback when the property is unset on the element
 * *and* on every ancestor. Unregistered, a tone therefore bleeds into
 * every descendant that reads it:
 *
 *   <div class="alert danger">
 *     <button class="btn">Undo</button>   ← renders red-on-red
 *     <span class="pill">3</span>         ← renders red, not muted
 *   </div>
 *
 * `inherits: false` scopes a tone to the element that declares it, so
 * descendants fall back to their own defaults. `syntax: "*"` with no
 * initial-value leaves the property guaranteed-invalid when unset, which
 * is precisely what makes those var() fallbacks fire.
 *
 * The contract this creates: a rule that reads --bg-mix must sit on the
 * same element that carries the tone class. Where a descendant needs the
 * value — the tinted <td> in tables.css, the tinted header in dialogs.css
 * — the toned element derives the result into a normal (inheriting)
 * property and passes that down instead.
 *
 * Browsers without @property support (pre-Firefox 128) fall back to the
 * old inheriting behavior: leaky, but not broken.
 */

@property --bg-mix {
  syntax:   "*";
  inherits: false;
}

@property --on-bg-mix {
  syntax:   "*";
  inherits: false;
}

/*
 * A tone is one variable. The contrast color is no longer declared here:
 * every tone used to assert `--on-bg-mix: white` (warning excepted)
 * regardless of how light the hue actually was, which failed WCAG AA on
 * 15 of the 35 tone x theme combinations — worst 1.99:1, on the primary
 * button of a real client theme.
 *
 * chip.css now derives it from the fill's luminance, which is AA for any
 * hue. --on-bg-mix survives as the override: set it on a tone or inside a
 * theme to pin a specific text color, e.g. to put a soft slate on warning
 * instead of the computed black:
 *
 *   .theme-x .warning { --on-bg-mix: #1f2937; }
 */
.primary   { --bg-mix: var(--color-primary);   }
.secondary { --bg-mix: var(--color-secondary); }
.muted     { --bg-mix: var(--color-muted);     }
.info      { --bg-mix: var(--color-info);      }
.success   { --bg-mix: var(--color-success);   }
.warning   { --bg-mix: var(--color-warning);   }
.danger    { --bg-mix: var(--color-danger);    }

/*
 * ── The tint ramp ───────────────────────────────────────────────────
 *
 * Three named steps, each the tone mixed into one of the three neutral
 * tokens. This is the only place the percentages live — surface.css reads
 * them rather than restating them, so a toned Card, a toned Alert and an
 * app's own `<div class="danger">` are tinted by one set of numbers.
 *
 *   --tint-surface   10%  into --surface   the fill of a toned block
 *   --tint-rule      30%  into --rule      its border
 *   --tint-ink       55%  into --ink       its text
 *
 * The names say which token each one tints, so there is nothing to look
 * up: --tint-ink is --ink pulled toward the tone.
 *
 * They mix toward --surface and --ink, not toward white and black, so one
 * set of percentages is correct in light and dark alike. A fixed
 * "lighten 90%" is a light-theme assumption wearing a neutral name, which
 * is why these are not named for a lighten scale.
 *
 * ── Why the universal selector ──────────────────────────────────────
 *
 * Listing the seven tone classes again would make adding an eighth tone two
 * edits in this file instead of one. There is no selector for "any element
 * with --bg-mix set", so the derivation is declared everywhere and the
 * cascade decides: --bg-mix is registered `inherits: false` with no
 * initial-value, so on an untoned element it is guaranteed-invalid, which
 * makes each color-mix() below invalid at computed-value time and leaves
 * the token unset. Its var() fallbacks then fire.
 *
 * The same mechanism is why there is no leak: every element computes these
 * from its own --bg-mix, so an untoned child inside a danger Card gets
 * nothing rather than its parent's red. Registering them `inherits: false`
 * pins that even where this rule is overridden.
 */
/*
 * ── The tone as text ────────────────────────────────────────────────
 *
 * --tone-ink is the tone made legible AS TEXT on this theme's surface:
 * hue and chroma untouched, lightness clamped into --tone-l-min/max
 * (tokens.css). A tone is tuned as a fill behind white text and mostly
 * fails the other job — measured on `.btn.outlined`, 34 of the 72
 * tone x theme pairs were under AA, worst 1.19:1; through the window the
 * worst is 6.02:1.
 *
 * Clamped rather than blended toward --ink, which is the same ruling
 * code.css already runs on: the clamp is a no-op wherever the tone
 * already reads, so a well-tuned theme keeps looking like itself. The
 * 55% blend --tint-ink applies was measured against it here and does not
 * hold — sunset's warning lands at 4.05:1.
 *
 * The window cannot be derived, only declared: relative color syntax
 * exposes the channels of the tone, never those of the surface it will
 * land on, so a dark theme states the inverted pair.
 *
 * Declared on * for the same reason the tint ramp below is, and it is
 * guaranteed-invalid on an untoned element, so `var(--tone-ink, X)` is
 * how a component says what it looks like with no tone. Registered
 * `inherits: false`, so a descendant — a ::after spinner included —
 * computes its own or gets nothing; a component that needs to pass the
 * value down derives it into a normal property first.
 */
@property --tone-ink {
  syntax:   "*";
  inherits: false;
}

@property --tint-surface {
  syntax:   "*";
  inherits: false;
}

@property --tint-rule {
  syntax:   "*";
  inherits: false;
}

@property --tint-ink {
  syntax:   "*";
  inherits: false;
}

*,
*::before,
*::after {
  --tone-ink: oklch(from var(--bg-mix)
                clamp(var(--tone-l-min), l, var(--tone-l-max)) c h);

  --tint-surface: color-mix(in srgb, var(--bg-mix) 10%, var(--surface));
  --tint-rule:    color-mix(in srgb, var(--bg-mix) 30%, var(--surface));
  /*
   * The tint ramp's text rung goes through the SAME legibility window
   * --tone-ink does, and for the same reason: 55% toward --ink is a blend
   * tuned for a tone, and a tone that starts very light lands short of it.
   * Measured across 10 themes x 7 tones, one pair failed — sunset's warning
   * at 3.86:1 — and the window puts it at 6.77:1.
   *
   * The clamp is a no-op wherever the blend already reads, which is 53 of
   * the 70 pairs; the 17 it moves all move UP, none regresses, and no
   * theme's tint ramp is flattened toward the ink to fix one hue. It is
   * the 2026-08-08 ruling (clamped, not blended) reaching the last place
   * in the package that had not applied it.
   */
  --tint-ink:     oklch(from color-mix(in srgb, var(--bg-mix) 55%, var(--ink))
                    clamp(var(--tone-l-min), l, var(--tone-l-max)) c h);
}
