/* The fork's design tokens — THE ONE source of truth for the analyst
 * plane's identity, replacing the identical (and in several
 * cases silently forked) :root block that the fork's own pages each carried.
 * Where pages disagreed, the value here is the majority (--warn 10:2,
 * --warn-bg 10:2) or the coherent family pick on a tie (--ok/--ok-bg/
 * --good unified on the lighter green family) — the forks were drift,
 * not design, and a single file is what stops the next one.
 * Crosscut-derived language; never the legacy skin.
 *
 * Every ONLINE fork page links this file and consumes these values rather
 * than redeclaring them (billing/list, q2help/home + triage, insights/*,
 * study_writer.css via @import). One category keeps a DELIBERATE,
 * documented mirror of these values instead of linking, and it is
 * intentional, not drift:
 *   - insights/report.html and billing/invoice.html — OFFLINE client
 *     deliverables opened from an email attachment, which must self-contain
 *     (report.html is pinned by test_the_html_is_self_contained). Change
 *     values here first, then copy into those inline blocks.
 * The indigo PansophIQ vendored-token adapter was retired: it was a second,
 * off-identity token system, not this navy one.
 *
 * `q2_analyst.css` is no longer on that list (2026-09-16, the look program).
 * The analyst chrome's 34 `--q2a-*` tokens used to MIRROR the palette in
 * their own :root block, and that block is now a block of ALIASES of the
 * names below — `--q2a-acc: var(--acc)` — so the navy the analyst plane
 * paints is this file's, with no second copy to drift. The dark half of
 * that palette lives here too, in the same three-selector shape the chrome
 * uses (bare :root, the OS media query, the [data-theme] stamp), and the
 * migration is a PROVEN no-op rather than an asserted one: `scripts/ci/
 * analyst_palette.py` resolves every token in all three theme contexts and
 * diffs them against the pre-migration palette (see docs/VERIFICATION_LOG.md).
 *
 * WHY THE DARK HALF IS `--x-dark` AND NOT A REDEFINITION OF `--x`.
 * The obvious spelling — a media-query block that redefines --acc, --bg,
 * --ink … on `:root` — repaints every consumer of these names, and two of
 * them are light-only ON PURPOSE: `study_writer.css` (which paints white
 * panels and dark ink with its own literals) and `q2_landing.css` (whose
 * dark block supplies its own values). A global dark redefinition would hand
 * study_writer.html a #0c141e page ground under white cards and #e9eff6
 * text on them — a broken page where there is a working one today. So the
 * light values keep the plain names and the dark half takes a `-dark` name,
 * and only a theme-aware consumer (the chrome, via its aliases) picks
 * between them. Both halves still live HERE, which is the point; when those
 * two sheets become theme-aware the halves can collapse onto one name. */
@import url("/static/css/respondent_type_scale.css");

:root {
    /* These :root defaults are read OUTSIDE the analyst shell -- by the
     * respondent plane's sheets (the interview's headings, focus rings and
     * option accents) and the client deliverable. Inside the shell
     * `q2_shell.css` re-points every one at the Theme Studio's roles, so an
     * analyst never sees these. Outside it they follow the respondent
     * palette's own accent, so the value lives in ONE place that palette
     * owns (PR #522 retires it there) and no analyst look leaks into an
     * interview. */
    --acc: var(--qr-acc);
    --acc-strong: var(--qr-acc-deep);
    --acc-soft: #e8eff8;
    --ink: #24334d;
    --mut: #4f5e73;
    --bg: #f7f9fc;
    --card: #ffffff;
    --panel-2: #f1f5fa;       /* inset / code-field / rail-adjacent ground */
    --ln: #e3e8ef;
    --rule: #eef2f5;
    --warn: #92400e;
    --warn-bg: #fdf0e3;
    --warn-line: #eccf8f;
    --ok: #1f6c3f;
    --ok-bg: #eaf6ef;
    --ok-ring: #8fd6b3;
    --good: #1d7a44;
    --good-bg: #e7f4ec;
    --bad: #a13d3d;
    --bad-bg: #fbecec;        /* the quiet destructive hover ground */
    --measured: #0b5c4b;
    --measured-bg: #e3f3ee;
    --gap: #92400e;
    --gap-bg: #fdf0e3;
    --focus: #b9cde4;         /* the focus ring, named */
    --off: #c2cad5;           /* an OFF switch track; the scrollbar thumb */
    /* The app bar's band, as two tokens rather than a literal navy: it is
     * the same mass as the rail and it has to move when somebody chooses
     * their own colours (insights/appearance.py derives both ends). */
    --band-1: var(--qr-acc);
    --band-2: #16213e;
    /* The primary action's gradient and the active rail item's, and the ONE
     * label colour that reads on each. `color: #fff` was a literal here and
     * it was a bug: in the dark theme the accent is a LIGHT blue and white
     * on it measures about 2:1. Both ends are tokens because a label has to
     * clear the WORST end of a gradient, not the first one. */
    --acc-grad-1: #245586;
    --acc-grad-2: var(--qr-acc);
    --on-acc: #ffffff;
    --on-rail: #ffffff;
    /* The navy rail is kept DARK in BOTH themes (owner direction, 2026-09:
     * the practice's other applications lead with a navy rail). These are
     * NOT redefined in the dark block -- the rail is the one deliberate dark
     * mass, continuous with the app bar -- so they carry no `-dark` twin. */
    --rail-1: #14314f;
    --rail-2: #0e2138;
    --rail-ink: #c6d4e4;
    /* Raised from #8aa2bd / #6a83a1 (2026-09). The section label measured
     * 3.0:1 on the rail, and an 11px uppercase label is NORMAL text to WCAG,
     * not large text -- so 3.0 is a fail. Found by pointing the palette
     * guardrail (insights/appearance.py) at the platform's OWN colours,
     * which is the only reason it was found at all. */
    --rail-mut: #a0b3c9;
    --rail-faint: #869bb2;
    --rail-line: rgba(255, 255, 255, 0.08);
    --rail-active-1: #245586;
    --rail-active-2: var(--qr-acc);
    /* ---- The alpha scale -----------------------------------------------
     * The translucent half of the palette: the steps a rule paints a
     * shadow, a scrim or a wash with, so that no sheet types an `rgba()`
     * of its own. A step is named for its WEIGHT — `--shade-NN` darkens
     * whatever is behind it, `--wash-NN` lightens it, and NN is the alpha
     * as a percentage — so a sheet picks the step it means, and the name
     * is the check that the value still says what it is.
     *
     * The steps ARE the values the respondent plane already painted
     * (`style.css`, `base.css`, `modern_design.css`), moved here
     * UNCHANGED, so re-pointing a declaration at a step is a no-op at
     * computed-value time. That property is what
     * `research/test_respondent_frame.py` pins, per step, and it is why
     * the values below are not rounded: they are what the sheets drew.
     * They are not a tidy ramp either — 0.3/0.4/0.41/0.5 sit within five
     * points of each other, exactly the spread the legacy sheets invented
     * — and collapsing them onto a coarser ramp is a design pass over
     * every rule that uses them, not a re-point. That pass is what
     * `clinical.css`'s 46 `rgba()` values (eight bases, twenty-one alphas)
     * are owed; this scale is the vocabulary it will move onto.
     *
     * ONE step is a deliberate exception to "moved here UNCHANGED":
     * `--shade-40` carried an `rgb()` channel-triple spelling of the
     * retired mid blue this file bans (owner, 2026-09-26: banned outright;
     * see `check_shell_tokens.py`, which now refuses that spelling too). It
     * is retuned to the current `--qr-acc` navy (`#153b68`) at the same 40%
     * weight — a declared, in-PR baseline move
     * (`scripts/ci/respondent_paint_baseline.py`), not a value-preserving
     * migration.
     *
     * No `-dark` twins, and that is a measurement rather than an
     * omission: the respondent plane is light-only on purpose
     * (`modern_design.css` stamps `color-scheme: only light`), and not
     * one of the three sheets references a `-dark` name. A dark half of
     * this scale would be a value nothing reads.
     *
     * The last two are opaque — `rgba()` written at alpha 1, which is a
     * colour with no alpha in it at all — so they are named for what they
     * are rather than given a step number the name would lie about. */
    --shade-10: rgba(16, 32, 48, 0.1);
    --shade-30: rgba(0, 0, 0, 0.3);
    --shade-40: rgba(21, 59, 104, 0.4);
    --shade-41: rgba(1, 1, 1, 0.41);
    --shade-50: rgba(0, 0, 0, 0.5);
    --shade-75: rgba(0, 0, 0, 0.75);
    --wash-22: rgba(255, 255, 255, 0.22);
    --wash-90: rgba(232, 232, 232, 0.9);
    --halo-grey: rgba(176, 176, 176, 1);
    --halo-acc: rgba(100, 149, 209, 1);
    /* The respondent TASK plane: the two sheets a respondent actually
     * completes — the swipe/card task and the products-shopping task.
     * They held 95 colour literals between them, and a literal is a value
     * that cannot move when somebody chooses their own colours; these are
     * those values, named. What is here is the feedback vocabulary a
     * respondent reads as MEANING (a like is green, a dislike is red, an
     * info affordance is blue), the neutral chrome every task control
     * shares, and the grounds and the veil they sit on.
     *
     * The names are keyed to the SHEET that owns them rather than to a
     * platform-wide semantic, because these are that task plane's own
     * colours and nothing else consumes them — and where two sheets
     * happened to agree on a value (black, #aaaaaa) they keep one name
     * each, the same way --warn and --gap already hold #92400e without
     * collapsing into one token.
     *
     * They carry no `-dark` twin. Both pages are light-only documents —
     * swipe_task.html extends no base and links no shell — so a dark half
     * would be a palette nothing can reach. The light values are what a
     * respondent sees either way, which is also why the migration is a
     * no-op rather than a repaint: every name below holds exactly the
     * literal it replaced.
     *
     * `--swipe-veil` and `--shop-shadow` keep their alpha rather than
     * being flattened onto a solid, and `--on-fill` is separate from
     * `--on-acc` on purpose: --on-acc tracks the accent gradient, and the
     * task buttons are not the accent. */
    --on-fill: #ffffff;              /* ink on a coloured fill that is NOT the accent */
    /* swipe_task.css */
    --swipe-like: var(--qr-best-line);     /* the swipe bar's Best button: the Best role colour */
    --swipe-like-strong: var(--qr-best-line); /* the Best affordance ON a card */
    --swipe-select: var(--qr-sel);   /* the final round's selected-card ring (a selection, not a verdict): the neutral selection colour */
    --swipe-like-stamp: #476024;     /* the classic like badge's border and ink */
    --swipe-dislike: var(--qr-worst-line); /* the swipe bar's Worst button: the Worst role colour */
    --swipe-dislike-strong: var(--qr-worst-line); /* the Worst affordance ON a card */
    --swipe-dislike-stamp: #7d2123;  /* the classic dislike badge's border and ink */
    --swipe-info: #888888;           /* the swipe bar's info button */
    --swipe-info-strong: #4183f9;    /* the info affordance ON a card */
    --swipe-control: #656666;        /* a grey control's fill and its border */
    --swipe-control-soft: #aaaaaa;   /* the muted step of the same chrome */
    --swipe-dark: #000000;           /* the landscape overlay's plate */
    --swipe-chip: #cccccc;           /* the plate behind the question overlay's text */
    --swipe-veil: rgba(99, 99, 99, 0.5); /* the question overlay's veil */
    /* products_shopping.css */
    --shop-ink: #4471b7;             /* the header's ink and the logo's fill */
    --shop-ink-2: #4d4f5c;           /* the search field's and the clear link's ink */
    --shop-ink-hover: #3a6b9c;       /* the cart menu's hover ink */
    --shop-accent: #ff7f50;          /* the filter button, the checked box, the filter close */
    --shop-danger: #ff6760;          /* the delete affordance's fill */
    --shop-danger-soft: #f790a1;     /* its hover */
    --shop-line: #e2e2e2;            /* the search box's border */
    --shop-line-2: #dddddd;          /* a dropdown rule, and light ink on the navy button */
    --shop-row-line: #e7e6ef;        /* a cart row's rule */
    --shop-shadow: rgba(0, 0, 0, 0.25); /* the header's cast */
    --shop-dark: #000000;            /* the cart's and the empty item's ground */
    --shop-control-soft: #aaaaaa;    /* the checkbox's hover fill */
}

/* The dark half of the palette above, under `-dark` names (see the header
 * for why they are not a redefinition). Same two selectors as the chrome's
 * own dark block, and the same order: an OS that prefers dark moves the
 * palette unless the person stamped light, and an explicit dark stamp wins
 * over a light OS. Only the tokens the light-first consumers above must
 * keep are here -- the rail has no dark twin. */
@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) {
        --acc-dark: #6aa4de;
        --acc-strong-dark: #8bb8e6;
        --acc-soft-dark: #17293e;
        --acc-grad-1-dark: #8bb8e6;
        --acc-grad-2-dark: #6aa4de;
        --on-acc-dark: #0d1620;
        --ink-dark: #e9eff6;
        --mut-dark: #9aa8b8;
        --bg-dark: #0c141e;
        --card-dark: #151f2c;
        --panel-2-dark: #111a26;
        --ln-dark: #26333f;
        --rule-dark: #1e2a37;
        --focus-dark: #35526e;
        --ok-dark: #3fc088;
        --ok-bg-dark: #12271c;
        --ok-ring-dark: #235238;
        --warn-dark: #d9a748;
        --warn-bg-dark: #2a2210;
        --warn-line-dark: #4a3c1c;
        --bad-dark: #e06a6a;
        --bad-bg-dark: #2b1616;
        --off-dark: #3d4a58;
        --band-1-dark: #16324f;
        --band-2-dark: #0c1626;
    }
}

:root[data-theme="dark"] {
    --acc-dark: #6aa4de;
    --acc-strong-dark: #8bb8e6;
    --acc-soft-dark: #17293e;
    --acc-grad-1-dark: #8bb8e6;
    --acc-grad-2-dark: #6aa4de;
    --on-acc-dark: #0d1620;
    --ink-dark: #e9eff6;
    --mut-dark: #9aa8b8;
    --bg-dark: #0c141e;
    --card-dark: #151f2c;
    --panel-2-dark: #111a26;
    --ln-dark: #26333f;
    --rule-dark: #1e2a37;
    --focus-dark: #35526e;
    --ok-dark: #3fc088;
    --ok-bg-dark: #12271c;
    --ok-ring-dark: #235238;
    --warn-dark: #d9a748;
    --warn-bg-dark: #2a2210;
    --warn-line-dark: #4a3c1c;
    --bad-dark: #e06a6a;
    --bad-bg-dark: #2b1616;
    --off-dark: #3d4a58;
    --band-1-dark: #16324f;
    --band-2-dark: #0c1626;
}

/* ---- The clinical kit's palette -------------------------------------
 * `clinical.css` is one of the three sheets the `Study.style` <select>
 * offers and it `@import`s `modern_design.css`, so the two are read by the
 * same respondent page. It carried 120 colour literals; every one that is a
 * DECLARATION VALUE and not the kit's own palette table is here now, by
 * name, holding exactly the literal it replaced -- so a re-point is a no-op
 * at computed-value time, which is the only thing this section claims.
 *
 * The names are keyed to the SHEET that owns them (`--k-` for the clinical
 * kit) rather than to a platform-wide semantic, because these are that
 * kit's own colours: `--k-good` is the clinical status green, which is not
 * `--ok`, and collapsing them would be a repaint rather than a tokenisation.
 * An alpha step is named for the BASE it shades and the WEIGHT it shades it
 * by, because that is the whole of what such a value means: `--k-ink-07` is
 * the clinical ink at 7 per cent.
 *
 * The two LINE values are shared with `modern_design.css`, the other choice
 * in that same <select>, so they are not kit names. That sheet's own comment
 * said a line scale in the token file is what retires its `--q2-ln` /
 * `--q2-card-ln` aliases, and this is that scale: `--row-line` (the
 * respondent frame's row rule, #e6ecf5) and `--card-line` (the card edge,
 * #d6deeb, the value the kit itself calls `--q-hairline`). They are declared
 * HERE and not read from the kit's table in `clinical.css`, because
 * `modern_design.css` is itself one of the three `Study.style` choices, and
 * on that choice `clinical.css` is not loaded at all.
 *
 * Two palettes deliberately did NOT move in:
 *
 *   * `#bbf7d0` and `#fecaca`, the legacy highlighting palette's two light
 *     halves. Each is ALSO spelled inside an attribute selector
 *     (`[data-color="#bbf7d0"]`), and an attribute selector compares literal
 *     text, so a `var()` cannot stand there. Keeping the declarations
 *     literal too means the value has ONE spelling in the sheet and the
 *     selector and the paint cannot drift apart.
 *   * the kit's own `:root` table in `clinical.css` itself. Those are the
 *     kit's palette VALUES -- what a study theme overrides, name by name --
 *     and every rule in the sheet reads them through `--q-*`, which is what
 *     keeps that override seam working.
 *
 * These are the LIGHT values and they carry no `-dark` twin: `clinical.css`
 * declares no dark block and no `color-scheme`, so a dark half would be a
 * palette nothing could reach. Nothing in the dark blocks above redefines
 * any name below. */
:root {
    --card-line: #d6deeb;
    --k-bad-line: #f0c8c8;
    --k-chip: #e6effa;
    --k-control-line: #c3ccd6;
    --k-dial: #0e1b36;
    --k-frost-ink: #cfe0f5;
    --k-good-line: #c6e8d4;
    --k-hl-green-ink: #166534;
    --k-hl-green-line: #86efac;
    --k-hl-green-on: #16a34a;
    --k-hl-red-ink: #991b1b;
    --k-hl-red-line: #fca5a5;
    --k-hl-red-on: #dc2626;
    --k-least-wash: #fdf7f7;
    --k-live-ink: #0f766e;
    --k-live-wash: #d9f2ee;
    --k-most-wash: #f6fbf8;
    --k-on-fill: #ffffff;
    --k-pick-line: #b6c0cb;
    --k-plate: #111827;
    --k-rule-line: #c7d7ec;
    --k-sel-line: #c9d2dc;
    --k-well-1: #fafbfd;
    --k-well-2: #f2f5f9;
    --row-line: #e6ecf5;
    --k-black-25: rgba(0, 0, 0, 0.25);
    --k-dial-55: rgba(14, 27, 54, 0.55);
    --k-dial-62: rgba(14, 27, 54, 0.62);
    --k-plate-50: rgba(17, 24, 39, 0.5);
    --k-navy-28: rgba(22, 59, 106, 0.28);
    --k-navy-30: rgba(22, 59, 106, 0.3);
    --k-navy-34: rgba(22, 59, 106, 0.34);
    --k-navy-38: rgba(22, 59, 106, 0.38);
    --k-navy-40: rgba(22, 59, 106, 0.4);
    --k-navy-45: rgba(22, 59, 106, 0.45);
    --k-navy-50: rgba(22, 59, 106, 0.5);
    --k-navy-55: rgba(22, 59, 106, 0.55);
    --k-navy-60: rgba(22, 59, 106, 0.6);
    --k-ink-05: rgba(28, 39, 51, 0.05);
    --k-ink-06: rgba(28, 39, 51, 0.06);
    --k-ink-07: rgba(28, 39, 51, 0.07);
    --k-ink-10: rgba(28, 39, 51, 0.1);
    --k-ink-15: rgba(28, 39, 51, 0.15);
    --k-ink-18: rgba(28, 39, 51, 0.18);
    --k-ink-25: rgba(28, 39, 51, 0.25);
    --k-ink-30: rgba(28, 39, 51, 0.3);
    --k-ink-35: rgba(28, 39, 51, 0.35);
    --k-haze-06: rgba(30, 78, 140, 0.06);
    --k-haze-08: rgba(30, 78, 140, 0.08);
    --k-haze-12: rgba(30, 78, 140, 0.12);
    --k-haze-14: rgba(30, 78, 140, 0.14);
    --k-haze-18: rgba(30, 78, 140, 0.18);
    --k-haze-25: rgba(30, 78, 140, 0.25);
    --k-haze-35: rgba(30, 78, 140, 0.35);
    --k-haze-50: rgba(30, 78, 140, 0.5);
    --k-frost-25: rgba(207, 224, 245, 0.25);
}

/* ---- The respondent kit's palette (the design language, 2026-09-18) ----
 * The values the owner approved on the design-language specimen, read off
 * the reference screens rather than chosen: the cool page ground, one navy,
 * one grey scale, and two MEANING colours, each in three steps. The bright
 * step (`--qr-good`, `--qr-bad`) is the reference's own and is the default
 * everywhere, including under white words. The `-fill-hc` step is a shade
 * deeper and takes over under white words only in the more-contrast mode
 * (`prefers-contrast: more`, or the person's own Display choice): white on
 * the bright green is 3.2:1 (2.3:1 in dark), enough for large bold text and
 * not for a small button, so the people who need 4.5:1 get it -- every `-hc`
 * fill clears 4.5:1 under white in its own theme, measured -- and nobody else
 * sees a duller green; the bright default in both themes is the owner's
 * choice (2026-09-18). The `-ink` step is the same meaning as TEXT on a wash.
 * `research/static/css/q2_respondent.css` consumes these under `--r-*`
 * working names and holds no literal of its own; the dark half follows the
 * file's `-dark` convention above and is picked by that sheet's three
 * theme selectors. */
:root {
    --qr-page: #eef2f6;
    --qr-surface: #ffffff;
    --qr-sunken: #f4f7fb;
    --qr-panel: #f1f5f9;
    --qr-ink: #1b2637;
    --qr-mut: #55637a;
    --qr-faint: #5f6e86;
    --qr-faint-hc: #4f5e75;
    --qr-line: #dde5f0;
    --qr-line-hc: #c5cfdd;
    --qr-line-soft: #e9eff7;
    /* The CONTROL boundary (2026-09-24, WCAG 2.1 SC 1.4.11): `--qr-line` and
     `--qr-line-hc` are decorative-divider tones -- 1.13-1.57:1 against the
     page and surface they sit on, measured -- fine for a card frame or a
     header rule, which SC 1.4.11 does not cover, but not for the outline
     that is the ONLY thing marking a radio, a checkbox, a text input, a
     slider track or an option card as a control. This pair is for that: a
     radio/checkbox row, an input or select border, a slider track, a
     segmented or scale button, a toggle, and an option/card-grid choice all
     read this instead of `--qr-line`. 3.84:1 against the white surface and
     3.41:1 against the page is the floor across every ground the kit paints
     a control on (measured against surface/page/sunken/panel: 3.41-3.84:1);
     `-hc` reuses `--qr-mut`'s tone for the `data-contrast="more"` state,
     5.41-6.08:1 on the same grounds. */
    --qr-control-line: #75839a;
    --qr-control-line-hc: #55637a;
    /* The respondent Theme Studio's DEFAULT preset (owner, 2026-09-26): the
     * accent moves off the retired analyst blue to #153b68, measured
     * directly off the owner's approved baseline screenshots' "Start Study"
     * button (the respondent identity must never borrow the analyst's
     * blue). Every other value in this block is untouched, so the default
     * preset renders pixel-for-pixel identical to the pre-existing kit
     * except this one family. insights/respondent_studio_presets.py's
     * "default" preset restates these same values -- this is the ONE file a
     * colour literal may live in (insights/respondent_studio.py emits
     * nothing for the "default" preset for exactly that reason). */
    --qr-acc: #153b68;
    --qr-acc-deep: #022955;
    --qr-acc-soft: #e8eff8;
    --qr-on-acc: #ffffff;
    /* The answer-SELECTION role (owner, 2026-10-07: a selection colour must not
     * nudge an answer, a client's brand may still wear the buttons, the
     * progress and the mark). Defaults to the neutral selection colour the
     * accent family carried before the split, so the default look moves
     * nothing; a client brand that is red, orange, amber or green leaves these
     * three alone (insights/respondent_palette.py). */
    --qr-sel: #153b68;
    --qr-sel-deep: #022955;
    --qr-sel-soft: #e8eff8;
    --qr-on-sel: #ffffff;
    --qr-on-fill: #ffffff;
    /* Standardized product-image overlay chrome (adversarial review on PR
     * #575, M1): a baked image's badge/flash/label/band are named roles on
     * THIS palette, never a literal in q2library/chrome_tokens.py and never
     * the client's brand accent -- q2library.chrome_tokens.resolve_chrome_values
     * reads these same keys via insights.respondent_studio.derive_tokens,
     * which computes the non-default-preset values from the roles above
     * (badge/plate = --qr-surface, flash = --qr-acc/--qr-on-acc, label ink
     * = --qr-mut, band = --qr-acc-soft) -- restated here as literals for
     * exactly the reason --qr-acc itself is (the "default" preset emits no
     * CSS of its own). */
    --qr-stim-matte: #ffffff;
    --qr-stim-badge-surface: #ffffff;
    --qr-stim-badge-ink: #1b2637;
    --qr-stim-flash-surface: #153b68;
    --qr-stim-flash-ink: #ffffff;
    --qr-stim-label-ink: #55637a;
    --qr-stim-label-plate: #ffffff;
    --qr-stim-band-surface: #e8eff8;
    --qr-good: #00a73e;
    --qr-good-fill-hc: #0a8230;
    --qr-good-ink: #0a7a30;
    --qr-good-wash: #c9f5d9;
    --qr-good-line: #8fe3ac;
    --qr-good-halo: rgba(0, 167, 62, 0.45);
    --qr-bad: #e72f39;
    --qr-bad-fill-hc: #dc2a34;
    --qr-bad-ink: #b3202a;
    --qr-bad-wash: #ffd3d3;
    --qr-bad-line: #ffa7aa;
    /* The BEST / WORST role colours (owner, 2026-10-07: "For the max diff I
     * think I'd prefer a semi-transparent green and red with solid green and
     * red borders"). Used ONLY by the two labelled role controls -- a MaxDiff
     * Best/Worst pick and a swipe's Best/Worst side -- never on a rating scale
     * or an ordinary selection, which stays on --qr-acc. The words and the
     * check/cross icons always stay on the control, so colour is never the
     * only cue. Practice constants, not studio roles: no preset or client look
     * sets them. The line pair was chosen to stay apart under protan, deutan
     * and tritan vision (OKLab distance >= 0.10 in every simulation, checked
     * by insights.respondent_gates); the fill is the line colour at low
     * alpha, so the label ink stays above 4.5:1 on it over any preset's card. */
    --qr-best-line: #157955;
    --qr-best-fill: rgba(21, 121, 85, 0.16);
    --qr-worst-line: #8a1a1a;
    --qr-worst-fill: rgba(138, 26, 26, 0.14);
    --qr-mark: #6d3fc4;
    --qr-mark-wash: #f1eafd;
    --qr-off: #c2cad5;
    --qr-focus: #bccde2;
    --qr-knob: #ffffff;
    --qr-knob-shadow: rgba(0, 0, 0, 0.25);
    --qr-thumb-shadow: rgba(16, 32, 48, 0.25);
    --qr-caption-veil: rgba(0, 0, 0, 0.72);
    /* The radius scale both planes share (PR 6, 2026-09-19): a card, a
     control, a pill. */
  --qr-r: 6px;
  --qr-r-sm: 4px;
  --qr-r-ctl: 4px;
  /* The analyst plane's own card and control radii. Separate palettes per
     audience: the respondent radii above move with the respondent look and
     must not carry the analyst workspace with them. */
  --q2s-r: 12px;
  --q2s-r-sm: 8px;
  --qr-r-pill: 999px;
  --qr-shadow-1: 0 1px 2px rgba(16, 32, 48, 0.05), 0 4px 14px -8px rgba(16, 32, 48, 0.12);
    --qr-shadow-2: 0 2px 4px rgba(16, 32, 48, 0.06), 0 14px 32px -12px rgba(16, 32, 48, 0.22);
    --qr-shadow-3: 0 4px 8px rgba(16, 32, 48, 0.08), 0 28px 48px -16px rgba(16, 32, 48, 0.30);
    /* The CARD family on the kit, review round 1, B2 (2026-09-20): the swipe
     composition reads this to size #tinderslide under whatever chrome a
     given study's shell actually renders (a title/subtitle push it well
     past a guessed constant). A small script in clinical/swipe_task.html
     measures it and overwrites this value on the document element at
     runtime; the static 64px here is the declared default this file's own
     contract requires (KitTokenTests: every --qr-* the kit reads must be
     declared somewhere in this file, so a typo'd or unspliced name fails
     loudly instead of silently painting nothing) and is also what a
     no-script render falls back to before the measurement script runs. */
    --qr-swipe-top: 64px;
    /* The respondent Theme Studio's MOTION vocabulary (owner, 2026-09-26):
     * speed (off/subtle/standard) and whether the pop/bounce animations use
     * the spring curve at all. These are the kit's own shipped durations
     * and curves, moved here from the literals `q2_respondent.css` used to
     * declare directly (`--r-d-state`/`--r-d-hover`/`--r-d-enter`/
     * `--r-ease`/`--r-spring`), which now read these tokens instead --
     * a no-op at computed-value time for the default preset, and the seam
     * insights/respondent_studio.py's style block overrides for any other
     * preset. `prefers-reduced-motion` is UNCHANGED and still wins over
     * whatever speed a study picked (see that file's own comment): it
     * REPLACES the chosen duration with 1ms rather than removing the rule. */
    --qr-motion-state: 120ms;
    --qr-motion-hover: 180ms;
    --qr-motion-enter: 320ms;
    --qr-motion-ease: cubic-bezier(.2, .7, .2, 1);
    --qr-motion-spring: cubic-bezier(.34, 1.56, .64, 1);
    /* M6 (2026-09-26): the dimensionless multiplier every literal duration
     * with no named token of its own (a card's max-width transition, a
     * progress fill, a spinner) scales by, so a studio speed of "off"
     * reaches them too. 1 for the default/standard speed -- every
     * `calc(<literal> * var(--r-d-scale))` below therefore computes back
     * to exactly the literal it replaced when unconfigured. */
    --qr-motion-scale: 1;
    /* `off` motion paused rather than sped up: an endless loop whose period is
     * scaled to about 1% would strobe, so a look with motion off stops the
     * loop instead (`animation-play-state`). */
    --qr-motion-loop: running;
    /* The respondent Theme Studio's FONT override (owner, 2026-09-26):
     * `q2_respondent.css` reads these through `var(--qr-studio-font,
     * var(--r-sans))` / `var(--qr-studio-fs, 16px)` -- KitTokenTests
     * requires every `--qr-*` name the kit reads to be declared somewhere
     * in this file regardless of whether the read itself carries a
     * fallback, so a typo'd or unspliced name fails loudly rather than
     * quietly falling back. The values here are exactly that same
     * fallback -- the kit's own shipped stack and size -- so an
     * unconfigured study is unaffected; insights/respondent_studio.py's
     * style block overrides both for any preset other than the default. */
    --qr-studio-font: Manrope, "Segoe UI", system-ui, -apple-system, "Helvetica Neue", Arial, sans-serif;
    --qr-studio-fs: 16px;
    /* STRUCTURE tokens (owner directive, 2026-10-07: the respondent look is
     * structure as well as colour; every look inherits these and a look may
     * override one -- `insights.respondent_studio.style_block` emits the
     * overrides from the preset's `scale`/`measure`/`density`/`header`/
     * `radius`/`radius_ctl` roles, and the Theme Studio sets exactly those).
     * Defaults are the `premium` scale, the `wide` measure, `comfortable`
     * density and the `band` header. */
    --qr-fs-title: 34px;
    --qr-fs-question: 28px;
    --qr-fs-lead: 17px;
    --qr-fs-label: 16px;
    --qr-fs-option: 17px;
    --qr-fs-pick: 13px;
    --qr-fs-title-ph: 22px;
    --qr-fs-question-ph: 24px;
    --qr-fs-lead-ph: 17px;
    --qr-gap-section-ph: 20px;
    --qr-gap-title: 24px;
    --qr-opt-min-h: 64px;
    --qr-opt-pad: 18px 20px;
    --qr-bar-h: 64px;
    --qr-bar-h-ph: 52px;
    --qr-bar-alpha: 96%;
    --qr-focus-w: 3px;
    --qr-ease-ui: cubic-bezier(.2, .7, .2, 1);
    --qr-dur-progress: 400ms;
    --qr-fw-title: 800;
    --qr-fw-question: 700;
    --qr-fw-lead: 400;
    --qr-lh-lead: 1.5;
    --qr-measure: 1190px;
    --qr-measure-read: 880px;
    --qr-gap-section: 40px;
    --qr-gap-stack: 18px;
    --qr-pad-card: 32px;
    --qr-pad-page: 28px;
    --qr-bar-pad-y: 8px;
    --qr-bar-shadow: 0 1px 2px rgba(16, 32, 48, 0.06);
    --qr-progress-h: 8px;
    --qr-progress-label: 13px;
}
@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) {
        --qr-page-dark: #10151d;
        --qr-surface-dark: #171e28;
        --qr-sunken-dark: #1d2631;
        --qr-panel-dark: #1a2330;
        --qr-ink-dark: #e8eefa;
        --qr-mut-dark: #a3b1c6;
        --qr-faint-dark: #7b8aa0;
        --qr-faint-hc-dark: #a3b1c6;
        --qr-line-dark: #2b3644;
        --qr-line-hc-dark: #3a4858;
        --qr-line-soft-dark: #232e3b;
        --qr-control-line-dark: #647890;
        --qr-control-line-hc-dark: #a3b1c6;
        --qr-acc-dark: #8fb6e8;
        --qr-acc-deep-dark: #b3cdf0;
        --qr-acc-soft-dark: #1b2a3d;
        --qr-on-acc-dark: #0d1620;
        --qr-sel-dark: #8fb6e8;
        --qr-sel-deep-dark: #b3cdf0;
        --qr-sel-soft-dark: #1b2a3d;
        --qr-on-sel-dark: #0d1620;
        --qr-good-dark: #22c55e;
        --qr-good-fill-hc-dark: #12813a;
        --qr-good-ink-dark: #6fe39a;
        --qr-good-wash-dark: #12331f;
        --qr-good-line-dark: #1f5a34;
        --qr-bad-dark: #ff4d57;
        --qr-bad-fill-hc-dark: #d5303a;
        --qr-bad-ink-dark: #ff8a90;
        --qr-bad-wash-dark: #3a1618;
        --qr-bad-line-dark: #6b2328;
        --qr-best-line-dark: #7ee0b4;
        --qr-best-fill-dark: rgba(126, 224, 180, 0.18);
        --qr-worst-line-dark: #d0404d;
        --qr-worst-fill-dark: rgba(208, 64, 77, 0.22);
        --qr-mark-dark: #b79cf0;
        --qr-mark-wash-dark: #251a3a;
        --qr-off-dark: #3a4757;
        --qr-focus-dark: #35526e;
        --qr-shadow-1-dark: 0 1px 2px rgba(0, 0, 0, 0.4), 0 6px 18px -10px rgba(0, 0, 0, 0.6);
        --qr-shadow-2-dark: 0 2px 4px rgba(0, 0, 0, 0.5), 0 16px 36px -14px rgba(0, 0, 0, 0.7);
        --qr-shadow-3-dark: 0 4px 8px rgba(0, 0, 0, 0.5), 0 30px 50px -16px rgba(0, 0, 0, 0.8);
    }
}
:root[data-theme="dark"] {
    --qr-page-dark: #10151d;
    --qr-surface-dark: #171e28;
    --qr-sunken-dark: #1d2631;
    --qr-panel-dark: #1a2330;
    --qr-ink-dark: #e8eefa;
    --qr-mut-dark: #a3b1c6;
    --qr-faint-dark: #7b8aa0;
    --qr-faint-hc-dark: #a3b1c6;
    --qr-line-dark: #2b3644;
    --qr-line-hc-dark: #3a4858;
    --qr-line-soft-dark: #232e3b;
    --qr-control-line-dark: #647890;
    --qr-control-line-hc-dark: #a3b1c6;
    --qr-acc-dark: #8fb6e8;
    --qr-acc-deep-dark: #b3cdf0;
    --qr-acc-soft-dark: #1b2a3d;
    --qr-on-acc-dark: #0d1620;
    --qr-sel-dark: #8fb6e8;
    --qr-sel-deep-dark: #b3cdf0;
    --qr-sel-soft-dark: #1b2a3d;
    --qr-on-sel-dark: #0d1620;
    --qr-good-dark: #22c55e;
    --qr-good-fill-hc-dark: #12813a;
    --qr-good-ink-dark: #6fe39a;
    --qr-good-wash-dark: #12331f;
    --qr-good-line-dark: #1f5a34;
    --qr-bad-dark: #ff4d57;
    --qr-bad-fill-hc-dark: #d5303a;
    --qr-bad-ink-dark: #ff8a90;
    --qr-bad-wash-dark: #3a1618;
    --qr-bad-line-dark: #6b2328;
    --qr-best-line-dark: #7ee0b4;
    --qr-best-fill-dark: rgba(126, 224, 180, 0.18);
    --qr-worst-line-dark: #d0404d;
    --qr-worst-fill-dark: rgba(208, 64, 77, 0.22);
    --qr-mark-dark: #b79cf0;
    --qr-mark-wash-dark: #251a3a;
    --qr-off-dark: #3a4757;
    --qr-focus-dark: #35526e;
    --qr-shadow-1-dark: 0 1px 2px rgba(0, 0, 0, 0.4), 0 6px 18px -10px rgba(0, 0, 0, 0.6);
    --qr-shadow-2-dark: 0 2px 4px rgba(0, 0, 0, 0.5), 0 16px 36px -14px rgba(0, 0, 0, 0.7);
    --qr-shadow-3-dark: 0 4px 8px rgba(0, 0, 0, 0.5), 0 30px 50px -16px rgba(0, 0, 0, 0.8);
}

/* ---- the Theme Studio preview stage's OWN defaults (lane G1) --------- */
/* `.q2st-pv` (insights/static/css/q2_appearance.css) paints a miniature
 * analyst workspace with its own custom properties (`--frame`, `--primary`,
 * ...) that `q2_studio.js` overwrites on load and on every edit. The names
 * below are the "Navy and claret" preset's own values (studio_presets.py's
 * `navyRed`) so the stage never flashes unstyled before the script runs --
 * the ONE default block, held here because this is the one file a colour
 * literal may live in, and referenced by `q2_appearance.css` with no
 * literal fallback of its own. */
/* ---- the analyst workspace's own defaults (q2workspace, PR #478) ------ */
/* The workspace stylesheet (`q2workspace/workspace.css`, a sibling lane)
 * scopes its color roles under `#q2ws` and reads every one of them from a
 * `--q2ws-*` token here, so a chosen look overrides them on an ancestor
 * instead of being shadowed by the sheet's own declarations. The Theme
 * Studio (`insights/studio.py`) emits the SAME names on `:root` for a
 * person who has chosen a look -- this is the "Navy and claret" default
 * that renders when nobody has. Font/size/row/radius/gap are this lane's
 * own addition: the workspace sheet still carries those five as literals
 * (its own layout is not wired to a per-user override yet), but the
 * Studio's emission needs a floor to fall back to and a name that will
 * not collide with anything on `:root`, so the defaults are declared here
 * -- the one file a color literal (and, by the same ratchet, a studio
 * default) may live in. */
:root {
  --q2ws-frame: #0b1b33; --q2ws-frame-ink: #ffffff; --q2ws-primary: #0b1b33; --q2ws-primary-ink: #ffffff; --q2ws-sel: #b3261e;
  --q2ws-canvas: #f2f1ee; --q2ws-surface: #ffffff; --q2ws-head: #ecebe7; --q2ws-grid: #dddad4; --q2ws-ink: #1a1a1a; --q2ws-ink2: #585755;
  --q2ws-good: #0a793f; --q2ws-warn: #8a5d00; --q2ws-bad: #b3261e; --q2ws-sign: #0b1b33; --q2ws-sign-ink: #ffffff; --q2ws-head-ink: #585755;
  --q2ws-c1: #c7362a; --q2ws-c2: #2f55b0; --q2ws-c3: #1f8a4c; --q2ws-c4: #e0a030; --q2ws-c5: #8a4bb3;
  --q2ws-c6: #d06a9a; --q2ws-c7: #ac5c55; --q2ws-c8: #586d9f; --q2ws-c9: #4f8a68; --q2ws-c10: #b99658;
  --q2ws-heat: #0b1b33;
  /* The swipe report's four series, in order: strong like, like, dislike,
     strong dislike. Roles on the analyst palette: the look's positive role,
     a lighter positive, a lighter negative, the look's negative role -- so a
     reader never sees a like in a warning colour, and a client's look moves
     all four. Never the respondent task's tokens: the report is another
     audience's surface. */
  --q2ws-swipe-1: var(--q2ws-good);
  --q2ws-swipe-2: color-mix(in srgb, var(--q2ws-good) 50%, var(--q2ws-surface));
  --q2ws-swipe-3: color-mix(in srgb, var(--q2ws-bad) 50%, var(--q2ws-surface));
  --q2ws-swipe-4: var(--q2ws-bad);
  --q2ws-shadow: rgba(0, 0, 0, .35); --q2ws-shadow-strong: rgba(0, 0, 0, .45);
  --q2ws-font: "Segoe UI Variable Text", "Segoe UI", system-ui, -apple-system, sans-serif;
  --q2ws-fs: 12px; --q2ws-row: 22px; --q2ws-r: 2px; --q2ws-gap: 6px;
}

/* The workspace FOLLOWS THE PLATFORM THEME when nobody has chosen a look.
 * Before this block the `--q2ws-*` defaults were light-only while the page
 * around them went dark, and every workspace element that inherited the
 * platform's own dark ink (a `label`, an `h4`) painted #e9eff6 on a white
 * view -- 38 labels at 1.16:1 under an OS or explicit dark theme (third
 * review). These are "Navy and claret, dark" (studio_presets.py's
 * `navyRedDark`) exactly as `insights/studio.py` emits it for a person who
 * saves that look -- the repaired values (its `sel` lifted to read on the
 * view), with the "claret and navy, for dark screens" chart set. Same three
 * selectors as the rest of this file; a saved look's own block repeats
 * under all three and comes later, so it still wins. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --q2ws-frame: #0a111d; --q2ws-frame-ink: #ffffff; --q2ws-primary: #c4394f; --q2ws-primary-ink: #ffffff; --q2ws-sel: #e8697c;
    --q2ws-canvas: #0f1726; --q2ws-surface: #152033; --q2ws-head: #1b2940; --q2ws-grid: #27364f; --q2ws-ink: #e6ebf2; --q2ws-ink2: #a3aec0;
    --q2ws-good: #4fae76; --q2ws-warn: #e0b24a; --q2ws-bad: #e46e6d; --q2ws-sign: #e6ebf2; --q2ws-sign-ink: #0d1620; --q2ws-head-ink: #a3aec0;
    --q2ws-c1: #d2566a; --q2ws-c2: #6f8fd0; --q2ws-c3: #b98a2c; --q2ws-c4: #a47ac0; --q2ws-c5: #3f9a64;
    --q2ws-c6: #b26d78; --q2ws-c7: #7b8db0; --q2ws-c8: #a48a56; --q2ws-c9: #9881a8; --q2ws-c10: #619375;
    --q2ws-heat: #6f8fd0;
    --q2ws-shadow: rgba(0, 0, 0, .55); --q2ws-shadow-strong: rgba(0, 0, 0, .65);
  }
}
:root[data-theme="dark"] {
    --q2ws-frame: #0a111d; --q2ws-frame-ink: #ffffff; --q2ws-primary: #c4394f; --q2ws-primary-ink: #ffffff; --q2ws-sel: #e8697c;
    --q2ws-canvas: #0f1726; --q2ws-surface: #152033; --q2ws-head: #1b2940; --q2ws-grid: #27364f; --q2ws-ink: #e6ebf2; --q2ws-ink2: #a3aec0;
    --q2ws-good: #4fae76; --q2ws-warn: #e0b24a; --q2ws-bad: #e46e6d; --q2ws-sign: #e6ebf2; --q2ws-sign-ink: #0d1620; --q2ws-head-ink: #a3aec0;
    --q2ws-c1: #d2566a; --q2ws-c2: #6f8fd0; --q2ws-c3: #b98a2c; --q2ws-c4: #a47ac0; --q2ws-c5: #3f9a64;
    --q2ws-c6: #b26d78; --q2ws-c7: #7b8db0; --q2ws-c8: #a48a56; --q2ws-c9: #9881a8; --q2ws-c10: #619375;
    --q2ws-heat: #6f8fd0;
    --q2ws-shadow: rgba(0, 0, 0, .55); --q2ws-shadow-strong: rgba(0, 0, 0, .65);
}

:root {
    --q2st-pv-frame: #0b1b33;
    --q2st-pv-primary: #0b1b33;
    --q2st-pv-sel: #b3261e;
    --q2st-pv-canvas: #f2f1ee;
    --q2st-pv-surface: #ffffff;
    --q2st-pv-head: #ecebe7;
    --q2st-pv-grid: #dddad4;
    --q2st-pv-ink: #1a1a1a;
    --q2st-pv-ink2: #5c5b58;
    --q2st-pv-good: #107c41;
    --q2st-pv-warn: #8a5d00;
    --q2st-pv-bad: #b3261e;
    --q2st-pv-sign: #0b1b33;
    --q2st-pv-heat: #0b1b33;
    --q2st-pv-frame-ink: #ffffff;
    --q2st-pv-primary-ink: #ffffff;
    --q2st-pv-head-ink: #5c5b58;
    --q2st-pv-sign-ink: #ffffff;
}
