/*
 * Design tokens, and the map onto Jelly UI.
 *
 * ============================================================================
 * THIS IS THE ONLY FILE IN THE PROJECT THAT MAY CONTAIN A COLOUR LITERAL.
 * ============================================================================
 *
 * Every value below is transcribed from docs/DESIGN.md, which derived it either
 * by sampling /design/concept/20.jpg and 21.jpg, or — for the light mode and the
 * accent presets added later — from Jelly UI's own published token set.
 * Nothing here is a preference and nothing here may be adjusted by eye: change
 * DESIGN.md, then change this file to match. tests/Feature/DesignTokenTest.php
 * fails if a hex appears anywhere else, and fails if a hex here is absent from
 * DESIGN.md.
 *
 * Section numbers below refer to docs/DESIGN.md.
 *
 * ---------------------------------------------------------------------------
 * TWO MODES, ONE MAP
 * ---------------------------------------------------------------------------
 *
 * The map onto Jelly at the foot of this file is written ONCE, against --sp-
 * tokens. Light mode re-points those tokens; it does not add a second map. So
 * there is exactly one place that knows what a Jelly property is called, and
 * adding a mode cannot half-apply.
 *
 * The switch is `data-jelly-mode` on <html> — JELLY'S OWN ROOT ATTRIBUTE, not a
 * second one of ours. Jelly's bundle writes `:root[data-jelly-mode="dark"]` into
 * its own @layer, so one attribute drives both its layer and ours and the two
 * cannot disagree. public/assets/theme.js always sets it explicitly, never
 * leaving it absent: absent means "follow the OS" to Jelly's layer, and this
 * file has no media query to follow it with.
 */

:root {
  /* ---- ground and surface ----------------------------------- DESIGN.md §4 */
  --sp-void:            #050614;  /* sampled  page ground                     */
  --sp-surface:         #0B0D1A;  /* sampled  cards, sheets, app bar          */
  --sp-slate:           #1C1A33;  /* sampled  raised inside a card           */

  /* ---- ink ---------------------------------------------------------------- */
  --sp-text:            #FFFFFF;  /* sampled  20.jpg "Charging", "Mode"      */
  --sp-text-muted:      #9B9CA1;  /* sampled  20.jpg "81 rue St-Chales"      */
  --sp-ink:             #050614;  /* the ink used on luminous fills          */

  /* ---- borders ------------------------------------------------------------ */
  --sp-border:          #33395D;  /* sampled  1.73:1 — decorative ONLY       */
  --sp-border-strong:   #73757C;  /* sampled  4.20:1 — may carry meaning     */

  /* ---- accent ------------------------------------------------------------- */
  --sp-indigo:          #5458F6;  /* sampled  filled controls                */
  --sp-indigo-lift:     #7578FA;  /* sampled  accent-as-text, focus ring     */
  --sp-cyan:            #57C8F4;  /* sampled  readout, gradient start        */
  --sp-aqua:            #8CE3DE;  /* sampled  gradient end                  */

  /* ---- availability ------------------------------------------------------- */
  --sp-state-available: #05C997;
  --sp-state-partial:   #F6B704;
  --sp-state-blocked:   #E71B7B;
  /* unset has no token: it is the absence of a fill — §3 */

  /*
   * The edge on a marked calendar cell. Transparent here, because in dark mode
   * the fill itself clears 3:1 against the card (9.03 / 10.76 / 4.45 — §7) and
   * an outline would be drawing a boundary that is already there.
   *
   * IN LIGHT MODE IT IS NOT TRANSPARENT, and §3 has the numbers: the same three
   * fills come back at 2.14:1, 1.80:1 and 4.34:1 against a white card, so two
   * of the three fail the 3:1 a boundary carrying information needs. The
   * boundary moves to a hairline that clears it and the fill keeps carrying the
   * hue. One token, so the calendar's stylesheet does not learn about modes.
   */
  --sp-cell-edge: transparent;

  /*
   * Two colours above, repeated as "r, g, b" channel triples.
   *
   * Not new colours: the same #050614 and #57C8F4, in the only form a colour
   * function accepts when the channels arrive through a custom property. A
   * translucent value cannot be built from a hex token — rgb(var(--sp-ink))
   * is not a thing — so anything that needs an alpha needs the channels.
   *
   * §9 already writes the overlay shadow as rgb(5 6 20 / 0.72) and §10 writes
   * the wash from rgb(87 200 244 / …), so both triples are DESIGN.md's rather
   * than this file's. Comma form because Jelly builds its own shadows as
   * rgba(var(--jelly-color-shadow), 0.34) and that only parses with commas.
   *
   * --sp-ink-rgb tracks --sp-ink and NOT --sp-void, which matters now that
   * there are two modes: a scrim and a drop shadow stay dark on a light page,
   * so they are built from the ink rather than from the ground. It was called
   * --sp-void-rgb while those were the same colour, and the rename is the whole
   * of that change.
   */
  --sp-ink-rgb:         5, 6, 20;
  --sp-cyan-rgb:        87, 200, 244;
  /*
   * There was a third, added in phase 8 because §10's group heatmap was emerald
   * over the card surface in five alpha steps and an alpha needs channels.
   * Stage 3 removed the ladder with the model it painted, and the channels went
   * with it — the group view now draws the state colours themselves, at full
   * strength, and no alpha of any of them is taken anywhere.
   */

  /*
   * The overlay scrim — §9, "a scrim of the ink at 0.72". Written out here
   * because a translucent value cannot be built from a hex token, and because
   * Jelly's own drawer backdrop is color-mix(… foreground-default 42%,
   * transparent), which on the dark palette is a white veil over a near-black
   * page. calendar.css and shell.css point ::part(backdrop) at this.
   */
  --sp-scrim: rgba(var(--sp-ink-rgb), 0.72);

  /* ---- spacing, 4px base -------------------------------------- §8, ratio  */
  --sp-space-1: 4px;
  --sp-space-2: 8px;
  --sp-space-3: 12px;
  --sp-space-4: 16px;   /* card padding, grid gap, card radius: one value    */
  --sp-space-6: 24px;
  --sp-space-8: 32px;

  /* ---- radius ------------------------------------------------------------- */
  --sp-radius-card:    16px;  /* ratio     cards, sheets, controls           */
  --sp-radius-cell:    12px;  /* ratio     calendar cells                    */
  --sp-radius-pill:   999px;  /* sampled   badges, chips, avatars            */

  /* ---- control sizing ----------------------------------------------------- */
  --sp-control-height: 48px;  /* ratio     46.7px measured, rounded to 4     */
  --sp-chip-height:    44px;  /* ratio + floor                               */
  --sp-touch-min:      44px;  /* floor                                       */
  --sp-appbar-height:  56px;  /* UI-LAYOUT.md, not the image                 */

  /* ---- type --------------------------------------------------------- §6   */
  --sp-font-text: -apple-system, BlinkMacSystemFont, "Segoe UI Variable Text",
                  "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --sp-font-display: var(--sp-font-text);
  --sp-font-mono: ui-monospace, "Cascadia Mono", "Segoe UI Mono", Menlo, monospace;

  /* ---- motion ------------------------------------------------------- §10  */
  --sp-ease-settle: cubic-bezier(0.25, 0.46, 0.45, 0.94);
  --sp-dur-instant:  0ms;
  --sp-dur-tap:    120ms;
  --sp-dur-settle: 350ms;   /* PRIOR-ART.md, already tuned — do not re-feel  */

  /* ---- signature ---------------------------------------------------- §10  */
  --sp-wash-max: 0.20;      /* hard ceiling — computed, not a taste          */
}

/*
 * ===========================================================================
 * LIGHT MODE — DESIGN.md §11
 * ===========================================================================
 *
 * Every value below is JELLY UI'S OWN, transcribed from the vendored bundle at
 * public/assets/vendor/jelly/jelly.js and converted from oklch to sRGB. None of
 * it was sampled, because both concept images are dark and neither contains a
 * light surface — and none of it was invented either, which is the constraint
 * §11 was written under. Jelly's light set is WCAG AA on its own ground and
 * §7's second table re-checks every pairing on ours.
 *
 * WHAT DOES NOT CHANGE, and each has a reason:
 *
 *   the availability hues   they are the data, and §3's shape markers are what
 *                           make them legible; the EDGE changes instead
 *   --sp-ink                it is the ink on those fills, which did not change
 *   --sp-ink-rgb            the scrim and the drop shadow stay dark on a light
 *                           page — that is what a scrim is
 *   cyan and aqua           readout colours; §10's light wash is re-checked
 *   every measurement       spacing, radius, type and motion are not a mode
 */
:root[data-jelly-mode="light"] {
  /*
   * Ground and card are the same white, exactly as Jelly ships them, and that
   * is not an oversight: §9 says elevation in this project is radius, spacing
   * and a hairline rather than lightness, and the dark palette already puts
   * ground and card 1.06:1 apart. The hairline does the work in both modes.
   */
  --sp-void:            #FFFFFF;  /* Jelly background.default                */
  --sp-surface:         #FFFFFF;  /* Jelly background.surface                */
  --sp-slate:           #FBFCFD;  /* Jelly background.muted, the field fill  */

  --sp-text:            #1F2430;  /* Jelly foreground.default — 15.52:1      */
  --sp-text-muted:      #5D6474;  /* Jelly foreground.muted   —  5.93:1      */

  --sp-border:          #ECECEF;  /* Jelly border.default, 1.18:1 decorative */
  --sp-border-strong:   #5A5A5A;  /* Jelly neutral-emphasis, 6.90:1          */

  /* §3: two of the three fills fall under 3:1 on white, so the cell gets an
     edge that does not. */
  --sp-cell-edge: var(--sp-border-strong);

  /*
   * §10: the wash is cyan over the ground, so a white ground needs more of it
   * to read at all. Muted text composited on it is the limiting pairing, and
   * it measures 4.69:1 at 0.36 and 4.46:1 at 0.44 — so the ceiling sits between
   * those and 0.32 is what ships, with the same margin the dark 0.20 keeps
   * under its own 0.22 failure point.
   */
  --sp-wash-max: 0.32;
}

/*
 * prefers-reduced-motion: the 350ms filmstrip slide collapses to an instant
 * change — §10. Done by retargeting the token rather than by writing a second
 * rule per animated property, so nothing can animate past the preference by
 * being written later.
 *
 * The wash is a static gradient and therefore stays: it is contrast-safe at
 * rest and nothing about it moves.
 */
@media (prefers-reduced-motion: reduce) {
  :root {
    --sp-dur-settle: var(--sp-dur-instant);
    --sp-dur-tap:    var(--sp-dur-instant);
  }
}

/*
 * ===========================================================================
 * DERIVED VALUES — after both modes, because they read what the mode set
 * ===========================================================================
 */
:root {
  /*
   * The luminous wash: the app's signature element, and the one thing §10 is
   * emphatic did not have to be invented — 20.jpg lights its selected row and
   * its charge bar exactly this way. Inherited in three places and nowhere
   * else: the hero card, the active screen in the filmstrip nav, and quantity
   * readouts.
   *
   * Defined here rather than in shell.css because it is a design decision, and
   * because §10 hands it over as this gradient. Both ceilings are computed
   * rather than chosen, and neither is a knob to turn up when the hero looks
   * flat.
   */
  --sp-wash: linear-gradient(
    100deg,
    rgb(var(--sp-cyan-rgb), var(--sp-wash-max)) 0%,
    rgb(var(--sp-cyan-rgb), 0.06) 45%,
    transparent 72%
  );

  /*
   * THE GROUP HEATMAP'S LADDER WAS HERE, AND STAGE 3 REMOVED IT.
   *
   * It was --sp-heat-1 through -5: emerald over the card surface at 0.12, 0.24,
   * 0.36, 0.48 and 0.60, a luminance ramp whose composites §10 still tabulates.
   * Group view painted one of the five per day, by how many people were free.
   *
   * The batch's fix 13 replaced that visual model — one square per member who
   * answered, four to a cell and small circles past four — so nothing paints the
   * ladder any more. The tokens went with it rather than staying as five names
   * that resolve to a colour and mean nothing, which is the state a token file
   * is least useful in. §10 keeps the measurements, marked superseded, because
   * they are the evidence for the 0.60 cap if a density ramp is ever wanted
   * again.
   */
}

/*
 * ===========================================================================
 * ACCENT PRESETS — DESIGN.md §5.1, and they answer §14 question 6
 * ===========================================================================
 *
 * An accent is a PAIR, not a colour, and our own indigo has always been one:
 * #5458F6 fills a control at 3.78:1 against the card, and #7578FA is what the
 * accent looks like when it is TEXT, because the fill value is only 3.78:1 and
 * text needs 4.5:1 (§7, finding 1).
 *
 * Jelly's four semantic tones behave identically and the numbers say so. As a
 * fill they clear 3:1 in both modes (4.00–4.23 on our dark card, 4.56–4.83 on
 * white) and take a white label at 4.56–4.83:1. As TEXT on the dark card they
 * come back at 4.00–4.23:1 and fail — so each needs a lift, exactly as indigo
 * does.
 *
 * THE LIFTS ARE DERIVED BY A RULE JELLY PUBLISHES, not chosen by eye. Jelly
 * ships azure twice: oklch(0.5601 0.1577 249.8) as the light accent and
 * oklch(0.7112 0.1559 250.96) as the dark one — the same hue and chroma, plus
 * 0.1511 of lightness. Applying that one delta to the other three tones is the
 * whole derivation, and applying it to azure itself reproduces Jelly's own dark
 * accent to within two units per channel (#48A7FF against their #4DA6FF),
 * which is the evidence that it is the rule they used.
 *
 * WHICH HALF IS THE TEXT DEPENDS ON THE MODE, and it does for indigo too:
 *
 *              as text on #0B0D1A    as text on #FFFFFF
 *   indigo          3.78 — fail          5.12 — pass
 *   indigo-lift     5.36 — pass          3.61 — fail
 *   mint            4.22 — fail          4.57 — pass
 *   mint-lift       7.63 — pass          2.53 — fail
 *
 * So --sp-accent-text is the LIFT in dark and the BASE in light, one rule for
 * all five presets, and light mode re-points it once below rather than every
 * preset carrying two values.
 *
 * The preset is a per-member account setting. It arrives as data-sp-accent on
 * <html>, written by the same boot script that writes the mode.
 */
:root {
  --sp-accent:      var(--sp-indigo);
  --sp-accent-lift: var(--sp-indigo-lift);

  /*
   * The label on any accent fill, in either mode. Fixed rather than tracking
   * --sp-text, which is #1F2430 in light mode and would fail on every one of
   * these fills. White measures 5.12:1 on indigo and 4.56–4.83:1 on the four
   * tones, so one value serves all five presets and both modes — which is the
   * property §14 question 6 could not find while our own rose was in the set.
   */
  --sp-on-accent: #FFFFFF;
}

/*
 * Jelly's own four, as fills. Same values in both modes — Jelly does not vary
 * them either, and §7's second table checks them against both grounds.
 *
 * INDIGO IS WRITTEN OUT TOO, although the :root block above already resolves to
 * it. Stage 2 made the attribute always present — the server renders the member's
 * stored choice, and "indigo" is a choice somebody can make rather than the
 * absence of one — so a preset with no rule of its own would be the one value in
 * the set that worked by falling through. It restates the pair rather than
 * inventing a value, and tests/Feature/Shell/AccentTest.php is what keeps these
 * five rules, the five cases of App\Kernel\Shell\Accent and the migration's ENUM
 * in step.
 */
:root[data-sp-accent="indigo"] { --sp-accent: var(--sp-indigo); --sp-accent-lift: var(--sp-indigo-lift); }
:root[data-sp-accent="rose"]  { --sp-accent: #DC2626; --sp-accent-lift: #FF6256; }
:root[data-sp-accent="amber"] { --sp-accent: #BC5A00; --sp-accent-lift: #F08945; }
:root[data-sp-accent="azure"] { --sp-accent: #0077CC; --sp-accent-lift: #48A7FF; }
:root[data-sp-accent="mint"]  { --sp-accent: #178746; --sp-accent-lift: #52B672; }

/*
 * The half of the pair that is text, chosen by mode. After the preset rules, so
 * that it reads whichever pair the preset installed.
 */
:root                          { --sp-accent-text: var(--sp-accent-lift); }
:root[data-jelly-mode="light"] { --sp-accent-text: var(--sp-accent); }

/*
 * ---------------------------------------------------------------------------
 * The map onto Jelly UI — DESIGN.md §5
 * ---------------------------------------------------------------------------
 *
 * THE SELECTOR IS LOAD-BEARING. It targets `jelly-theme`, not `:root`, and
 * getting that wrong is silent.
 *
 * <jelly-theme> does not put its tokens on :root. It writes a
 * `:host { --jelly-color-*: … }` rule into its own shadow root, so those values
 * are set DIRECTLY on the <jelly-theme> element. A directly-set property beats
 * an inherited one, so `:root { --jelly-color-background-surface: … }` would be
 * overridden for every element inside the app while looking correct in the
 * stylesheet — and because Jelly's own defaults are WCAG AA compliant, the
 * result would look fine too. That is the exact failure docs/DESIGN.md §5 and
 * the phase 6 handoff both warned about.
 *
 * Targeting the host element works because normal declarations from the outer
 * tree beat an inner tree's :host rule (CSS Scoping). It also beats Jelly's
 * global defaults, which ship inside `@layer jelly` and therefore lose to any
 * unlayered declaration regardless of source order.
 *
 * Verified in a real browser with getComputedStyle, not assumed — see
 * .claude/state/logs/phase-7.md.
 *
 * ---------------------------------------------------------------------------
 * AND THE SELECTOR IS A LIST, because some components leave the theme — phase 8
 * ---------------------------------------------------------------------------
 *
 * <jelly-drawer> moves itself to <body> when it opens — a portal, so that its
 * backdrop and its focus trap are not trapped inside whatever container it was
 * written in. That takes it OUT of <jelly-theme>, and everything above stops
 * applying: it inherits nothing from the provider it is no longer inside, and
 * falls back to Jelly's own defaults, which are the LIGHT set.
 *
 * Found in a browser at 390 × 844 and not visible in the code: the detail sheet
 * opened as a white card with a blue button in the middle of a dark app.
 *
 * docs/DESIGN.md §5 said jelly-popover, jelly-menu, jelly-toaster and
 * jelly-tooltip "belong in that list the day one of them is first rendered".
 * THE APP BAR RENDERS jelly-popover, so that day is today — and all four are
 * added at once rather than one at a time, because the failure is silent in
 * light mode and merely wrong in dark, and finding it four times would be
 * finding it three times too often.
 */
jelly-theme,
jelly-drawer,
jelly-dialog,
jelly-menu,
jelly-popover,
jelly-toaster,
jelly-tooltip {
  /* ---- global surfaces ---------------------------------------------------- */
  --jelly-color-background-default: var(--sp-void);     /* §9 ground          */
  --jelly-color-background-surface: var(--sp-surface);  /* §5                 */
  --jelly-color-background-muted:   var(--sp-slate);    /* §5 resting field   */
  --jelly-color-background-neutral: var(--sp-slate);    /* §5 disabled fill   */
  --jelly-color-background-accent:  var(--sp-accent);   /* §5.1 the preset    */

  /* ---- global ink --------------------------------------------------------- */
  --jelly-color-foreground-default: var(--sp-text);        /* §5              */
  --jelly-color-foreground-muted:   var(--sp-text-muted);  /* §5              */

  /*
   * White on every accent this app can be set to — 5.12:1 on indigo and
   * 4.56–4.83:1 on Jelly's four tones, in both modes. §5.1 and §7.
   */
  --jelly-color-foreground-on-accent:  var(--sp-on-accent);
  /* Ink on slate is 16.87:1 dark and 15.11:1 light. Needed because -neutral
     is remapped above. */
  --jelly-color-foreground-on-neutral: var(--sp-text);

  /*
   * ---- Jelly's four semantic tones — DESIGN.md §14 question 6, ANSWERED ----
   *
   * Open since phase 7 and deliberately left open by phases 8, 9 and 10, each
   * of which avoided rendering jelly-alert, jelly-badge and jelly-toaster
   * rather than half-mapping the set. The app bar's unread badge renders one,
   * so it is answered here.
   *
   * The reason it was hard: Jelly pairs all four emphasis fills with ONE label
   * colour, and OUR palette had no ink clearing 4.5:1 on both indigo (needs
   * white, 5.12:1) and our rose #E71B7B (needs ink, 4.64:1; white is 4.34:1).
   *
   * The answer is that our rose was never a candidate. #E71B7B is an
   * AVAILABILITY fill — it means "upptagen", it is read off a calendar cell
   * with a dark numeral on it, and §3 owns it. Jelly's rose is a different
   * thing for a different job, and its own value takes white at 4.83:1. So the
   * four tones map to JELLY'S OWN values, keep Jelly's own single white label,
   * and the availability hues stay out of the set entirely. Nothing is split
   * and nothing is invented.
   *
   * As fills they clear the 3:1 a meaningful fill needs on both grounds:
   * 4.00–4.23:1 on the dark card, 4.56–4.83:1 on white.
   */
  --jelly-color-background-rose:  var(--sp-tone-rose);
  --jelly-color-background-amber: var(--sp-tone-amber);
  --jelly-color-background-azure: var(--sp-tone-azure);
  --jelly-color-background-mint:  var(--sp-tone-mint);
  --jelly-color-foreground-on-emphasis: var(--sp-on-accent);

  /* ---- borders and the focus ring ---------------------------------------- */
  --jelly-color-border-default: var(--sp-border);       /* §9 hairline        */
  --jelly-color-border-focus:   var(--sp-accent-text);  /* §5.1               */

  /*
   * --jelly-ring-color is overridden because Jelly builds it as
   * color-mix(… var(--jelly-ring) 50%, transparent). At 50% over our card
   * surface the ring composites to 2.19:1 — under the 3:1 a focus indicator
   * needs. §7 computed 5.36:1 for the SOLID lifted indigo, so the ring is set
   * solid. Jelly's default is AA on Jelly's own surfaces; it is not AA on ours.
   *
   * It follows --sp-accent-text rather than naming indigo, so a member who
   * chose a preset gets a focus ring in it. Every one of the ten combinations
   * clears 3:1 — the worst is rose-lift on the dark card at 6.56:1 and indigo
   * on white at 5.12:1 — because the token is already the half of each pair
   * that was chosen for being legible as ink.
   *
   * Found by checking contrast after the override, which is the order §5 asks
   * for and the reason it asks for it: the halved ring is perfectly visible,
   * just not compliant.
   */
  --jelly-ring-color: var(--sp-accent-text);

  /* Geometry from §5: a 2px ring with a 2px gap. Jelly defaults to 4.5px / 0. */
  --jelly-ring-width: 2px;
  --jelly-ring-gap:   2px;

  /* ---- elevation ---------------------------------------------------------- */
  /*
   * §9: elevation in this palette is not lightness. Void, Surface and Slate sit
   * within 1.2:1 of each other and a black shadow on a near-black ground is
   * invisible, so a raised shadow is replaced by a hairline and a drop shadow is
   * reserved for overlays, where there is a scrim to cast onto. Light mode
   * inherits the reasoning rather than the excuse — Jelly's own light surfaces
   * are 1.00:1 apart, so the hairline is doing even more of the work there.
   */
  --jelly-color-shadow:   var(--sp-ink-rgb);
  --jelly-shadow-raised:  0 0 0 1px var(--sp-border);
  --jelly-shadow-overlay: 0 8px 32px rgba(var(--sp-ink-rgb), 0.72);

  /* ---- type --------------------------------------------------------------- */
  --jelly-font-display: var(--sp-font-display);
  --jelly-font-text:    var(--sp-font-text);
  --jelly-font-mono:    var(--sp-font-mono);

  /* ---- geometry ----------------------------------------------------------- */
  --jelly-radius:        var(--sp-radius-card);
  --jelly-card-radius:   var(--sp-radius-card);

  --jelly-button-radius: var(--sp-radius-card);
  --jelly-input-radius:  var(--sp-radius-card);
  --jelly-button-height: var(--sp-control-height);
  --jelly-chip-radius:   var(--sp-radius-pill);
  --jelly-badge-radius:  var(--sp-radius-pill);
}

/*
 * The four tone literals, declared after the map that consumes them so that the
 * "no --jelly- declaration before the provider rule" check in
 * tests/Feature/DesignTokenTest.php still reads a clean file. Custom properties
 * do not care about order — they resolve at use.
 *
 * Jelly's own, transcribed from the vendored bundle and converted from oklch.
 * §5.1 has the derivation of the lifted halves; the lifts live on the preset
 * rules above because only an accent ever uses one.
 */
:root {
  --sp-tone-rose:  #DC2626;
  --sp-tone-amber: #BC5A00;
  --sp-tone-azure: #0077CC;
  --sp-tone-mint:  #178746;
}

/*
 * ---------------------------------------------------------------------------
 * The category palette — phase 14.2, docs/DESIGN.md §5.2 and §7
 * ---------------------------------------------------------------------------
 *
 * Eight colours for Inventory's per-member categories, added under an edit to
 * DESIGN.md the user approved on 2026-07-28. They are NOT adopted from Jelly the
 * way the four tones above are — Jelly ships four and this needs eight — so they
 * are derived by a stated rule instead: Jelly's own tone band, at hue 27.33 plus
 * multiples of 45, lightness 0.55, chroma 0.15, gamut-clamped. The same
 * conversion reproduces all four Jelly tones to the byte, which is the evidence
 * the rule is what produced these rather than a preference.
 *
 * 0.55 IS A FLOOR, NOT A TASTE. It is the highest lightness at which all eight
 * clear 4.5:1 with white on them; at 0.555 the jade is 4.49:1. Going lower costs
 * contrast at the other end, where the plum is 3.68:1 against the dark card.
 * Both tightest values are in DESIGN.md §7's sixth table.
 *
 * ONE VALUE FOR BOTH MODES, and there is no light-mode block re-pointing them.
 * A category's colour is what identifies it, so re-tinting per mode would make
 * one category two colours — the reasoning §11 already gives for the three
 * availability hues. Every value clears 3:1 against BOTH cards, which is what
 * makes one value legal in both places.
 *
 * The label on one of these is --sp-on-accent, the same fixed white §5.1 gives
 * every fill in this application. It is not a ninth token and must not become
 * one: --sp-text is #1F2430 in light mode and fails on all eight.
 *
 * App\Modules\Inventory\CategoryColour is the enum whose cases are these names,
 * and inventory.css is the only stylesheet that reads them. Nothing stores a
 * colour VALUE anywhere — the database stores the name.
 */
:root {
  --sp-cat-brick:  #B9463E;
  --sp-cat-ochre:  #9B6402;
  --sp-cat-olive:  #6D7A01;
  --sp-cat-jade:   #00875C;
  --sp-cat-teal:   #02808E;
  --sp-cat-denim:  #1E73C6;
  --sp-cat-violet: #7D5ABD;
  --sp-cat-plum:   #AA4889;
}

/*
 * ---------------------------------------------------------------------------
 * Card padding — phase 9, and it is §5's finding a third time
 * ---------------------------------------------------------------------------
 *
 * §8 is emphatic that padding, gap and radius are ONE value in this design —
 * "the single most useful thing in either image" — and that value is 16px.
 * Jelly's card defaults to 22px block and 24px inline, so a dashboard grid cell
 * offered 123.2px of label box where §6 measured every Swedish card label
 * against 139.2px. TILLGÄNGLIGHET at 117.31px still fitted, with 5.9px to spare
 * instead of 21.9px — passing, on a margin nobody chose.
 *
 * IT HAS TO BE SET ON jelly-card, NOT ON THE PROVIDER. <jelly-card> writes those
 * two defaults into its own :host rule, which sets them DIRECTLY on the element,
 * and a directly-set property beats an inherited one — the same mechanism that
 * makes a :root override of the colour tokens silently do nothing. Setting the
 * value on jelly-theme was measured in a browser and changed the card by exactly
 * zero pixels.
 *
 * After the jelly-theme rule rather than before it, because
 * tests/Feature/DesignTokenTest.php asserts that no --jelly- declaration appears
 * ahead of the provider — which is what keeps the :root mistake out.
 */
jelly-card {
  --jelly-card-padding-block:  var(--sp-space-4);
  --jelly-card-padding-inline: var(--sp-space-4);
}

/*
 * The switch, raised to the touch floor — phase 10.
 *
 * Jelly's default track is 62 x 34, and the clickable region inside its shadow
 * root is exactly that tall. docs/DESIGN.md §13 and CLAUDE.md §6 both put the
 * minimum touch target at 44 x 44px and call it non-negotiable, so the default is
 * 10px short. Measured in a real browser at 390px, not inferred: the inner label
 * came back at 224.16 x 34.
 *
 * Both axes are raised so the control stays a pill rather than becoming a square:
 * 62/34 is 1.82:1 and 76/44 is 1.73:1. Scaling the component to meet the floor,
 * not redesigning it.
 *
 * On the COMPONENT, not on jelly-theme. A --jelly-<component>-* knob is written
 * into the component's own :host rule, which sets it directly on the element, and
 * a directly-set property beats an inherited one — so an override on the provider
 * is silently defeated. docs/DESIGN.md §5; found the hard way in phase 9 with
 * jelly-card's padding, and the third time the same mechanism has bitten.
 */
jelly-switch {
  --jelly-switch-width:  76px;
  --jelly-switch-height: 44px;
}

/*
 * STILL NOT MAPPED, and now deliberately rather than for want of an answer:
 * --jelly-color-background-white / -neutral-emphasis and their paired
 * foregrounds. Those are the "white" and "graphite" variants, which nothing in
 * this app renders — the four tones above were mapped because the app bar's
 * unread badge needed one, and the rule §14 question 6 settles is that a tone
 * gets mapped by the screen that first needs it, with its numbers written down.
 */
