/* ========================================
   MarketSavant Design Tokens
   Shared by landing (style.css) and app (app.css)
   ======================================== */

/* === BASE TOKENS (Dark Mode Default) === */
:root {
    /* Base Colors (Dark Mode - Default) */
    --color-bg-primary: #0a0a0a;
    --color-bg-secondary: #141414;
    --color-bg-tertiary: #1a1a1a;
    --color-bg-elevated: #1f1f1f;

    /* Every color as a surface a page may sit on (page_look background).
       Written from PAGE_SURFACES in public/js/color-palette.mjs;
       tools/color-palette.test.mjs holds the two equal. */
    --color-bg-teal: #13282c;
    --color-bg-violet: #341d2c;
    --color-bg-amber: #2c2417;
    --color-bg-scarlet: #391d1d;
    --color-bg-gold: #282513;
    --color-bg-emerald: #14291f;
    --color-bg-orange: #302219;
    --color-bg-blue: #192630;
    --color-bg-navy: #1d2438;
    --color-bg-forest: #18281e;
    --color-bg-plum: #321d39;
    --color-bg-slate: #1f252e;
    --color-bg-sand: #2a2419;
    --color-bg-mist: #19212b;
    --color-bg-ice: #132338;
    --color-bg-sage: #17251b;
    --color-bg-blush: #2c1b21;
    --color-bg-stone: #262422;

    --color-text-primary: #ffffff;
    --color-text-secondary: #a0a0a0;
    --color-text-muted: #707070;
    --color-text-on-accent: #ffffff;     /* text on accent-colored surfaces — the fallback, and only that: every accent theme below states its own ink, four of them near-black */
    --color-text-content: #fdf6e3;       /* leaf body/content prose — bright, warm-white; groups content apart from the pure-white page title */
    --color-process-label: #ffffff;      /* process/band name — max contrast on neutral bands (dark) */

    --color-border: #2a2a2a;
    --color-border-hover: #3a3a3a;
    /* The edge of something you press. Separate from --color-border on purpose:
       an interface component's boundary has to be findable against whatever is
       behind it, a divider does not, and raising --color-border to that bar
       would coarsen every rule and box in the product. Guarded at 3:1 against
       every background token, in both modes, by tools/token-contrast.test.mjs. */
    --color-border-control: #6f7175;

    --color-danger: #ef4444;
    --color-danger-hover: #dc2626;

    /* Spacing */
    /* The named spelling. These are ALIASES of the numeric scale below, not a
       second set of values — there is one spacing scale with two names. Both
       spellings are in heavy use (261 and 336 call sites), so the duplication
       is collapsed here rather than by rewriting every use for no visible
       change. New code may use either; prefer the numeric one. */
    --spacing-xs: var(--space-2);   /* 8px  */
    --spacing-sm: var(--space-4);   /* 16px */
    --spacing-md: var(--space-6);   /* 24px */
    --spacing-lg: var(--space-8);   /* 32px */
    --spacing-xl: var(--space-12);  /* 48px */
    --spacing-2xl: var(--space-16); /* 64px */
    --spacing-3xl: var(--space-24); /* 96px */

    /* Finer-grained spacing scale (4px / 8px / 12px / 16px / 20px / 24px / 32px) —     */
    /* used by newer component stylesheets (e.g. manage-tasks.css).                    */
    /* The named scale above (--spacing-xs/sm/md/lg) remains the default for           */
    /* app.css and style.css. Both conventions coexist intentionally.                  */
    --space-1: 0.25rem;  /* 4px */
    --space-2: 0.5rem;   /* 8px */
    --space-3: 0.75rem;  /* 12px */
    --space-4: 1rem;     /* 16px */
    --space-5: 1.25rem;  /* 20px */
    --space-6: 1.5rem;   /* 24px */
    --space-8: 2rem;     /* 32px */
    --space-10: 2.5rem;  /* 40px */
    --space-12: 3rem;    /* 48px */
    --space-16: 4rem;    /* 64px */
    --space-24: 6rem;    /* 96px */

    /* Typography */
    --font-sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
    --font-mono: 'SF Mono', Monaco, 'Cascadia Code', 'Roboto Mono', Consolas, monospace;

    /* ---- Content type scale (elements on a task page) ----------------------
       Added 2026-09-21. Scoped DELIBERATELY to what an element renders, not to
       app chrome — the chrome note below still stands and is why.

       These are semantic ROLES, not sizes, because an author picks what a piece
       of text IS, never how many rem it is. They are the typography behind
       `AtomRole` in src/types/index.ts, which had the vocabulary and no
       appearance. Sizes and line-heights are Tailwind's published scale rather
       than numbers invented here.

       Reach for these in element renderers and in the element presentation
       contract. Do NOT use them to restyle nav, panels, admin widgets or any
       other chrome — that is the half-migrated state the note below prevents. */
    --text-display-base: 2.25rem;
    --text-display-lh-base: 2.5rem;
    --text-heading-base: 1.5rem;
    --text-heading-lh-base: 2rem;
    --text-subheading-base: 1.25rem;
    --text-subheading-lh-base: 1.75rem;
    --text-body-base: 1rem;
    --text-body-lh-base: 1.5rem;
    --text-caption-base: 0.875rem;
    --text-caption-lh-base: 1.25rem;

    /* The roles themselves are the base values, here. An element an author
       sized redeclares every one of them on its own cell as the base times its
       factor (element-presentation.css), so its headings keep their rank. The
       bases are separate names because a token cannot be redeclared in terms
       of itself: that is a cycle, and it silently scales nothing. */
    --text-display: var(--text-display-base);
    --text-display-lh: var(--text-display-lh-base);
    --text-heading: var(--text-heading-base);
    --text-heading-lh: var(--text-heading-lh-base);
    --text-subheading: var(--text-subheading-base);
    --text-subheading-lh: var(--text-subheading-lh-base);
    --text-body: var(--text-body-base);
    --text-body-lh: var(--text-body-lh-base);
    --text-caption: var(--text-caption-base);
    --text-caption-lh: var(--text-caption-lh-base);

    --weight-regular: 400;
    --weight-medium: 500;
    --weight-semibold: 600;
    --weight-bold: 700;

    /* Measure. Long lines are the single commonest reason a page reads as
       unfinished, and nothing in this app has ever constrained one. */
    --measure-prose: 68ch;

    /* THERE IS NO FONT-SIZE SCALE FOR APP CHROME. This is deliberate as of 2026-07-21, and documented
       here because three separate tasks have now lost time writing CSS against
       --font-size-lg / --font-size-sm / --font-size-* tokens that have never existed.
       If you are reaching for one: it is not here, it is not anywhere, and it is not
       under a different name. Write a literal rem.

       Measured 2026-07-21: 499 font-size declarations across 31 stylesheets, using 54
       distinct values that mix px, rem and em, including near-duplicates (0.85rem vs
       .85rem vs 0.875rem; 12px vs 12.5px). Introducing a scale that those 499
       declarations ignore would create a half-migrated state with no answer to "which
       do I use", so the scale is deliberately deferred to its own planned refactor
       rather than added piecemeal.

       This is unchanged by the content scale above: that one governs element
       content on a task page and nothing else. If you are styling chrome, the
       paragraph above still applies to you.

       Convention for new chrome code: a literal rem, matching the nearest existing value.
       0.85rem for small/secondary text and 1.125rem for a section title are the two
       most established. */

    /* Transitions */
    --transition-fast: 150ms ease;
    --transition-base: 250ms ease;
    --transition-slow: 400ms ease;

    /* Borders */
    --radius-sm: 0.375rem;
    --radius-md: 0.5rem;
    --radius-lg: 0.75rem;
    --radius-xl: 1rem;
    --nav-rail-collapsed: 18px;   /* width of a collapsed Task View process rail handle */
    --nav-side-zone-height: 20%;  /* THE BAND THE BUILD RAIL STOPS SHORT OF. It was a
                                     boundary between two rails: nav owned the bottom fifth
                                     of each side edge (Plan 3a §4.0.1 D1), the "Prior
                                     tasks" / "Following tasks" rails claimed it, the Build
                                     rail claimed everything above it on the right, and one
                                     shared number made the two provably disjoint rather
                                     than coincidentally ordered.
                                     Those rails came out on 2026-09-25 (owner: the header's
                                     own neighbours control says the same thing without
                                     covering the work), so NOTHING OCCUPIES THIS BAND NOW.
                                     It is kept because the Build rail's `bottom` and a
                                     prose reference in chat-drawer.css still read it, and
                                     stopping the rail short of the floor is a reasonable
                                     look on its own — but read it as "stop short of an
                                     empty band", not as a boundary with a neighbour.
                                     Whether the Build rail should now run the full height
                                     of the right edge is an open question for the owner. */

    /* Chat popover placement (chat-drawer.css) — and, because they live here, the
       band the Build/authoring rail must keep clear of it (authoring-drawer.css).
       Same reasoning as --nav-side-zone-height above: the two drawers are provably
       disjoint only because ONE set of numbers drives both the popover and the
       reservation made for it. Retune the popover here and the reservation follows.
       --nav-chat-panel-right is the popover's right edge measured from the stage's
       left border edge. An absolutely-positioned child of .nav-stage lays out
       against the stage's PADDING box, which spans the whole stage including its
       18px padding, so that same origin serves the authoring rail too.
       .nav-center's left edge is that 18px padding + one collapsed rail track +
       the stage grid's 16px gap (app.css .nav-stage). The popover then sits one
       inset plus one width further right — but measured from .nav-center's
       PADDING box, not its border box: the popover's wrapper is an absolutely
       positioned child of .nav-center, and an absolute box resolves against its
       containing block's padding box, which starts INSIDE .nav-center's own 1px
       border (app.css .nav-center). That border is the +1px below; without it
       this token undercounts the popover by a pixel and the 16px breathing gap
       the consumer adds silently becomes 15px. Measured in-browser at 901x800
       and again at 1440x900, both identical: .nav-center left = 52, the chat
       wrapper left = 53, popover 77-477. */
    --nav-chat-panel-inset: 24px;  /* popover/launcher offset from .nav-center's corner */
    --nav-chat-panel-width: 400px;
    --nav-chat-panel-right: calc(18px + var(--nav-rail-collapsed) + 16px + 1px
                                 + var(--nav-chat-panel-inset)
                                 + var(--nav-chat-panel-width));

    /* Shadows (Dark Mode) */
    --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.5);
    --shadow-md: 0 4px 6px rgba(0, 0, 0, 0.5);
    --shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.6);
    --shadow-xl: 0 20px 25px rgba(0, 0, 0, 0.7);

    /* Process-strip frame bands. NEUTRAL, now RECEDING (dark mode): shades of the
       node's OLD interior (--color-bg-elevated) so the bands step back and let the
       teal tasks carry the emphasis. The two-shade -alt pair still drives the
       left→right row alternation (see graph-core.computeTaskFrames shadeIndex).
       NEUTRAL (no accent), so these stay on :root and auto-adapt per theme.
       band shade — tune here */
    --strip-fill:       var(--color-bg-elevated);
    --strip-fill-alt:   color-mix(in srgb, var(--color-text-primary)  8%, var(--color-bg-elevated));
    --strip-stroke:     color-mix(in srgb, var(--color-text-primary) 12%, var(--color-bg-elevated));
    --strip-stroke-alt: color-mix(in srgb, var(--color-text-primary) 20%, var(--color-bg-elevated));

    /* Modal scrim — a strong dim behind in-viewport lightboxes/dialogs. Kept
       intentionally heavy in BOTH modes: lightbox content (e.g. slideshow slides)
       is dark, so the backdrop must read as a deep dim even on the light canvas. */
    --color-scrim: rgba(0, 0, 0, 0.78);

    /* A wash over a picture, and the words it carries. The same in both modes
       on purpose: a photograph does not change with the mode, so the wash that
       keeps words readable on it must not either. */
    --wash-dark: #000000;
    --wash-dark-ink: #ffffff;
    --wash-light: #ffffff;
    --wash-light-ink: #141210;
}

/* === LIGHT MODE ===
   The `body` prefix is load-bearing, not decoration. `data-mode` is an ordinary
   attribute name in this codebase, not a reserved one: the theme switcher's own
   buttons carry it (`<button class="mode-option" data-mode="light">` in app.html,
   index.html, login.html and signup.html), and so does `.atom-bundle[data-mode]`
   in atom-layout.css. A bare `[data-mode="light"]` therefore matched the Light
   button too, redeclaring this whole palette on that button's subtree whatever
   mode the page was actually in. Measured in Chromium on 2026-09-06, dark mode,
   login.html: the Light option's own text resolved --color-text-secondary to the
   light-mode #5c636a against the menu's --color-bg-elevated #1f1f1f, 2.71:1 —
   below the 4.5:1 AA body floor, at rest. With the prefix it reads the dark
   #a0a0a0 and measures 6.30:1.
   Only <body> carries the real mode (theme-switcher.js sets it there), so scope
   every mode/theme palette block to <body> and nothing else.
   tools/token-contrast.test.mjs fails if a bare selector comes back. */
body[data-mode="light"] {
    /* Phase B B1: soft gray canvas + crisp white cards. The ramp now ascends in
       brightness (primary = canvas/lowest → elevated = white/highest), matching the
       dark-mode "elevated = raised/lighter" convention. Starting values, tuned live. */
    --color-bg-primary: #f3f4f6;         /* page/canvas — soft gray, no more "nuclear white" */
    --color-bg-secondary: #f7f8fa;       /* re-seated between canvas and white */
    --color-bg-tertiary: #fbfbfc;        /* re-seated between canvas and white */
    --color-bg-elevated: #ffffff;        /* cards/raised — crisp white */
    /* The directory's own page color, darker than the app's canvas so its window
       reads as a separate reference against both the dark app and plain white. */
    --color-bg-reference: #dfe2e7;

    /* The surfaces a page may sit on, light values (see :root). */
    --color-bg-teal: #e7f6f9;
    --color-bg-violet: #faf1f7;
    --color-bg-amber: #f9f3e8;
    --color-bg-scarlet: #fbf1f1;
    --color-bg-gold: #faf4d2;
    --color-bg-emerald: #e3f8ee;
    --color-bg-orange: #faf2ed;
    --color-bg-blue: #edf5fa;
    --color-bg-navy: #f0f3fb;
    --color-bg-forest: #ecf6f0;
    --color-bg-plum: #f9f1fb;
    --color-bg-slate: #f1f4f7;
    --color-bg-sand: #f8f1e4;
    --color-bg-mist: #eef3f8;
    --color-bg-ice: #eaf3fd;
    --color-bg-sage: #ecf4ed;
    --color-bg-blush: #fbeff2;
    --color-bg-stone: #f3f2f0;

    --color-text-primary: #1a1a1a;
    /* Darkened from #6c757d on 2026-07-22. The old value passed AA only on the
       lighter half of the ramp — 4.69:1 on white cards but 4.26:1 on the canvas
       (--color-bg-primary, the body surface at app.css:16), under the 4.5:1 body
       requirement. Whether supporting text was accessible depended on which
       surface it happened to sit on, and this token has 161 references.
       #5c636a clears 4.5:1 against ALL FOUR background tokens (worst case 5.53:1
       on the canvas) while staying well clear of --color-text-primary at 15.81:1,
       so the primary/secondary hierarchy still reads.
       tools/token-contrast.test.mjs recomputes this and fails if it regresses. */
    --color-text-secondary: #5c636a;
    /* NOT for body or supporting text — 1.89:1 to 2.07:1 in light mode, failing AA
       on every surface. Decorative/large-only. Use --color-text-secondary instead. */
    --color-text-muted: #adb5bd;
    --color-text-on-accent: #ffffff;     /* text on accent-colored surfaces — the fallback, and only that: every accent theme below states its own ink, four of them near-black */
    --color-text-content: #2b2410;       /* leaf body/content prose — warm near-black; groups content apart from the page title (light mode) */
    --color-process-label: #000000;      /* process/band name — max contrast on neutral bands (light) */

    --color-border: #d6dae0;             /* a touch stronger so card edges read on white */
    --color-border-hover: #c2c8d0;
    --color-border-control: #80858d;     /* the edge of something you press — see the dark block */

    --color-danger: #ef4444;
    --color-danger-hover: #dc2626;

    --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05);
    --shadow-md: 0 4px 6px rgba(0, 0, 0, 0.07);
    --shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.1);
    --shadow-xl: 0 20px 25px rgba(0, 0, 0, 0.15);

    /* Process-strip bands — NEUTRAL, RECEDING (light mode). Same formulas as the
       dark block: shades of the node's OLD interior (--color-bg-elevated, = white
       here) so the bands step back and let the teal tasks carry the emphasis. The
       mixes reference per-theme tokens, so they auto-adapt.
       band shade — tune here */
    --strip-fill:       var(--color-bg-elevated);
    --strip-fill-alt:   color-mix(in srgb, var(--color-text-primary)  8%, var(--color-bg-elevated));
    --strip-stroke:     color-mix(in srgb, var(--color-text-primary) 12%, var(--color-bg-elevated));
    --strip-stroke-alt: color-mix(in srgb, var(--color-text-primary) 20%, var(--color-bg-elevated));

    /* Modal scrim — still a strong dim in light mode (lightbox content is dark). */
    --color-scrim: rgba(0, 0, 0, 0.72);
}

/* Every accent theme below is scoped to <body> for the same reason the light-mode
   palette above is: the switcher's theme buttons carry the attribute themselves
   (`<button class="theme-option" data-theme="violet">`), so a bare
   [data-theme="violet"] redeclared the violet accents on that button's subtree
   regardless of the active theme. Measured in Chromium on 2026-09-06 before the
   prefix: --color-accent-primary resolved to #a35d8b on the Violet option while
   <body> carried teal's #149ab0, which is what .theme-option:focus painted its
   outline from. With the prefix both read #149ab0. */

/* A PAGE may also choose an accent, and joins the block rather than copying it
   (Task 4.1). The composed renderer stamps the chosen word on the page root,
   and every theme block below carries a second selector for it, so a page's
   accent and the app's accent can never hold different hexes — including
   --color-text-on-accent, which each theme now states for itself — an ink left
   to inherit would come from whatever theme the <body> is in, and near-black on
   violet is 4.00:1 where violet's own white is 4.67:1.

   It is qualified by the class, not left as a bare attribute, for exactly the
   reason above: an attribute alone is a palette handed to any element that
   happens to carry it. Only a composed page root may wear this one, and saying
   so in the selector is what keeps that true rather than hoped. */

/* === THE COLOR LIST, PER MODE ===
   One list of eighteen serves a page's background, accent and words and an
   element's color; its first twelve are also the app's themes (owner,
   2026-09-26, and "make all colors available for all options", 2026-09-28). Each color is declared ONCE per mode, here, and
   every block that wears a color points at these — so an app theme, a page
   accent and an element color cannot hold different values, and all three are
   right in both modes without a set of mode overrides each.

   The theme blocks below point at these with var(). That is safe on a page and
   on an element as well as on <body> because a var() inside a custom property
   is substituted where that property is DECLARED: a cell that declares
   --color-accent-text: var(--hue-navy-text) reads the --hue-navy-text it
   inherits from <body>, which is the current mode's.

   Per color:
     fill — what a button or pill is painted with;
     ink  — the words on that fill;
     text — the color as readable words, on every background and on its own tint.
   The first six fills and inks are the owner's and are the same in both modes;
   a dark hue cannot be findable in both, so the six newer ones differ by mode.

   The values are written from public/js/color-palette.mjs, the numbers the owner
   approved as one picture; tools/color-palette.test.mjs holds the two equal and
   tools/token-contrast.test.mjs scores them where they land. */
:root {
    --hue-teal-fill: #149ab0;
    --hue-teal-ink: #141210;
    --hue-teal-text: #15a0b7;
    --hue-violet-fill: #a35d8b;
    --hue-violet-ink: #ffffff;
    --hue-violet-text: #b57ea2;
    --hue-amber-fill: #eaa835;
    --hue-amber-ink: #141210;
    --hue-amber-text: #eaa835;
    --hue-scarlet-fill: #d32f2f;
    --hue-scarlet-ink: #ffffff;
    --hue-scarlet-text: #ff4d3d;
    --hue-gold-fill: #ffd700;
    --hue-gold-ink: #141210;
    --hue-gold-text: #ffd700;
    --hue-emerald-fill: #00e676;
    --hue-emerald-ink: #141210;
    --hue-emerald-text: #00e676;
    --hue-orange-fill: #ed701d;
    --hue-orange-ink: #141210;
    --hue-orange-text: #f36f16;
    --hue-blue-fill: #309ce8;
    --hue-blue-ink: #141210;
    --hue-blue-text: #2b9dee;
    --hue-navy-fill: #456bd3;
    --hue-navy-ink: #ffffff;
    --hue-navy-text: #6c8ce2;
    --hue-forest-fill: #3fa66a;
    --hue-forest-ink: #141210;
    --hue-forest-text: #39ac69;
    --hue-plum-fill: #b177c5;
    --hue-plum-ink: #141210;
    --hue-plum-text: #b97ccd;
    --hue-slate-fill: #8796ab;
    --hue-slate-ink: #141210;
    --hue-slate-text: #8295b0;
    --hue-sand-fill: #9d7834;
    --hue-sand-ink: #141210;
    --hue-sand-text: #b18b43;
    --hue-mist-fill: #5482b0;
    --hue-mist-ink: #141210;
    --hue-mist-text: #7294b6;
    --hue-ice-fill: #3580d4;
    --hue-ice-ink: #141210;
    --hue-ice-text: #5a94d6;
    --hue-sage-fill: #3a9245;
    --hue-sage-ink: #141210;
    --hue-sage-text: #4aa455;
    --hue-blush-fill: #c85b76;
    --hue-blush-ink: #141210;
    --hue-blush-text: #cc788d;
    --hue-stone-fill: #867e6c;
    --hue-stone-ink: #141210;
    --hue-stone-text: #969188;
}

body[data-mode="light"] {
    --hue-teal-fill: #149ab0;
    --hue-teal-ink: #141210;
    --hue-teal-text: #0f7080;
    --hue-violet-fill: #a35d8b;
    --hue-violet-ink: #ffffff;
    --hue-violet-text: #985681;
    --hue-amber-fill: #eaa835;
    --hue-amber-ink: #141210;
    --hue-amber-text: #956410;
    --hue-scarlet-fill: #d32f2f;
    --hue-scarlet-ink: #ffffff;
    --hue-scarlet-text: #ca2b2b;
    --hue-gold-fill: #ffd700;
    --hue-gold-ink: #141210;
    --hue-gold-text: #7f6b00;
    --hue-emerald-fill: #00e676;
    --hue-emerald-ink: #141210;
    --hue-emerald-text: #007d40;
    --hue-orange-fill: #c15710;
    --hue-orange-ink: #ffffff;
    --hue-orange-text: #b34d09;
    --hue-blue-fill: #1474b8;
    --hue-blue-ink: #ffffff;
    --hue-blue-text: #0f6fb3;
    --hue-navy-fill: #1d367c;
    --hue-navy-ink: #ffffff;
    --hue-navy-text: #193580;
    --hue-forest-fill: #2d764b;
    --hue-forest-ink: #ffffff;
    --hue-forest-text: #28784a;
    --hue-plum-fill: #743a88;
    --hue-plum-ink: #ffffff;
    --hue-plum-text: #77358d;
    --hue-slate-fill: #4b596c;
    --hue-slate-ink: #ffffff;
    --hue-slate-text: #475871;
    --hue-sand-fill: #956e25;
    --hue-sand-ink: #ffffff;
    --hue-sand-text: #886628;
    --hue-mist-fill: #4478ab;
    --hue-mist-ink: #ffffff;
    --hue-mist-text: #456f99;
    --hue-ice-fill: #1e74d4;
    --hue-ice-ink: #ffffff;
    --hue-ice-text: #226cbf;
    --hue-sage-fill: #32853d;
    --hue-sage-ink: #ffffff;
    --hue-sage-text: #34793d;
    --hue-blush-fill: #cf3f63;
    --hue-blush-ink: #ffffff;
    --hue-blush-text: #c0385a;
    --hue-stone-fill: #7d7464;
    --hue-stone-ink: #ffffff;
    --hue-stone-text: #706c63;
}

/* === THEME: TEAL (Default) === */
/* Phase B B2 (round 2): blue-green, nudged ~5% back toward green per owner.
   Primary #149ab0 = rgb(20,154,176). from→via→to sweeps green→blue so gradient
   surfaces (landing/auth) read as a gentle left-to-right evolution. */
body[data-theme="teal"],
.cockpit--composed[data-page-accent="teal"],
.cockpit-cell[data-element-color="teal"] {
    --color-accent-from: #18a0a0;   /* slightly greener edge */
    --color-accent-via: #149ab0;    /* primary */
    --color-accent-to: #1186bc;     /* slightly bluer edge */
    --color-accent-primary: var(--hue-teal-fill);
    --color-accent-primary-hover: #11849a;
    --color-accent-glow: rgba(20, 154, 176, 0.3);
    --color-accent-subtle: rgba(20, 154, 176, 0.03);
    --color-text-on-accent: var(--hue-teal-ink);  /* near-black ink — white on #149ab0 is 3.34:1, under the 4.5:1 AA body floor; this ink is 5.59:1 */
    --color-accent-text: var(--hue-teal-text);  /* the color as readable words, per mode — see THE COLOR LIST */
}

/* === THEME: VIOLET === */
/* Phase B B2 (round 2): dusky mauve, pushed ~5%+ toward rose-red per owner.
   Primary #a35d8b = rgb(163,93,139). from→via→to sweeps violet→rose. */
body[data-theme="violet"],
.cockpit--composed[data-page-accent="violet"],
.cockpit-cell[data-element-color="violet"] {
    --color-accent-from: #9c5f9a;   /* slightly more violet edge */
    --color-accent-via: #a35d8b;    /* primary */
    --color-accent-to: #ad5a7e;     /* rose edge */
    --color-accent-primary: var(--hue-violet-fill);
    --color-accent-primary-hover: #8e5079;
    --color-accent-glow: rgba(163, 93, 139, 0.3);
    --color-accent-subtle: rgba(163, 93, 139, 0.03);
    --color-text-on-accent: var(--hue-violet-ink);  /* white ink — 4.67:1 on #a35d8b, the reading it has always resolved to; stated rather than inherited because a PAGE may carry this palette (Task 4.1) under a <body> whose own theme inks near-black, and #141210 on #a35d8b is 4.00:1, under the AA body floor */
    --color-accent-text: var(--hue-violet-text);  /* the color as readable words, per mode — see THE COLOR LIST */
}

/* === THEME: AMBER === */
/* Phase B B2 (round 2): orange-yellow gold, brightened to drop the muddy/brown
   cast per owner (was #d9a22e, too dark). Primary #eaa835 = rgb(234,168,53).
   from→via→to sweeps yellow-gold→orange. */
body[data-theme="amber"],
.cockpit--composed[data-page-accent="amber"],
.cockpit-cell[data-element-color="amber"] {
    --color-accent-from: #eeb33e;   /* yellow-gold edge */
    --color-accent-via: #eaa835;    /* primary */
    --color-accent-to: #e2912a;     /* orange edge */
    --color-accent-primary: var(--hue-amber-fill);
    --color-accent-primary-hover: #d4922a;
    --color-accent-glow: rgba(234, 168, 53, 0.3);
    --color-accent-subtle: rgba(234, 168, 53, 0.03);
    --color-text-on-accent: var(--hue-amber-ink);  /* near-black ink — white on #eaa835 is 2.07:1, the worst of the six; this ink is 9.03:1 */
    --color-accent-text: var(--hue-amber-text);  /* the color as readable words, per mode — see THE COLOR LIST */
}

/* === THEME: SCARLET (vivid warm red) === */
/* Was "mauve" (muted brick #b54a44) — too low-chroma and too close in hue to
   Violet, so it never earned its own slot. Now a clean warm red that separates
   cleanly from the violet-rose neighbour. */
body[data-theme="scarlet"],
.cockpit--composed[data-page-accent="scarlet"],
.cockpit-cell[data-element-color="scarlet"] {
    --color-accent-from: #e23b32;   /* brighter warm edge */
    --color-accent-via: #d32f2f;    /* primary */
    --color-accent-to: #c0243a;     /* deeper crimson edge */
    --color-accent-primary: var(--hue-scarlet-fill);
    --color-accent-primary-hover: #b92828;
    --color-accent-glow: rgba(211, 47, 47, 0.3);
    --color-accent-subtle: rgba(211, 47, 47, 0.03);
    --color-text-on-accent: var(--hue-scarlet-ink);  /* white ink — 4.98:1 on #d32f2f, the reading it has always resolved to; stated rather than inherited because a PAGE may carry this palette (Task 4.1) under a <body> whose own theme inks near-black, and #141210 on #d32f2f is 3.75:1, under the AA body floor */
    --color-accent-text: var(--hue-scarlet-text);  /* the color as readable words, per mode — see THE COLOR LIST */
}

/* === THEME: GOLD (bright gold-yellow) === */
/* Brightened from the old muted #c2a24a (which read too close to Amber). The
   fill is bright enough that global white-on-accent text is illegible, so this
   theme overrides --color-text-on-accent to a dark ink (mode-independent —
   gold stays bright in both light and dark). */
body[data-theme="gold"],
.cockpit--composed[data-page-accent="gold"],
.cockpit-cell[data-element-color="gold"] {
    --color-accent-from: #ffdf3a;   /* lighter yellow edge */
    --color-accent-via: #ffd700;    /* primary */
    --color-accent-to: #f5c400;     /* deeper gold edge */
    --color-accent-primary: var(--hue-gold-fill);
    --color-accent-primary-hover: #e6c200;
    --color-accent-glow: rgba(255, 215, 0, 0.3);
    --color-accent-subtle: rgba(255, 215, 0, 0.03);
    --color-text-on-accent: var(--hue-gold-ink);  /* near-black ink — white is illegible on bright gold */
    --color-accent-text: var(--hue-gold-text);  /* the color as readable words, per mode — see THE COLOR LIST */
}

/* === THEME: EMERALD (vivid spring green) === */
/* New slot. A true green, kept clear of the default Teal's cyan. Like gold, the
   fill is bright enough that white-on-accent text is only borderline-legible, so
   this theme uses the same near-black ink override. */
body[data-theme="emerald"],
.cockpit--composed[data-page-accent="emerald"],
.cockpit-cell[data-element-color="emerald"] {
    --color-accent-from: #33ed8e;   /* lighter mint edge */
    --color-accent-via: #00e676;    /* primary */
    --color-accent-to: #00c853;     /* deeper emerald edge */
    --color-accent-primary: var(--hue-emerald-fill);
    --color-accent-primary-hover: #00c853;
    --color-accent-glow: rgba(0, 230, 118, 0.3);
    --color-accent-subtle: rgba(0, 230, 118, 0.03);
    --color-text-on-accent: var(--hue-emerald-ink);  /* near-black ink — matches gold; white is illegible on bright green */
    --color-accent-text: var(--hue-emerald-text);  /* the color as readable words, per mode — see THE COLOR LIST */
}

/* === THEME: ORANGE === */
body[data-theme="orange"],
.cockpit--composed[data-page-accent="orange"],
.cockpit-cell[data-element-color="orange"] {
    --color-accent-from: color-mix(in srgb, var(--hue-orange-fill) 88%, #ffffff);
    --color-accent-via: var(--hue-orange-fill);
    --color-accent-to: color-mix(in srgb, var(--hue-orange-fill) 88%, #000000);
    --color-accent-primary: var(--hue-orange-fill);
    --color-accent-primary-hover: color-mix(in srgb, var(--hue-orange-fill) 85%, #000000);
    --color-accent-glow: color-mix(in srgb, var(--hue-orange-fill) 30%, transparent);
    --color-accent-subtle: color-mix(in srgb, var(--hue-orange-fill) 3%, transparent);
    --color-text-on-accent: var(--hue-orange-ink);
    --color-accent-text: var(--hue-orange-text);
}

/* === THEME: BLUE === */
body[data-theme="blue"],
.cockpit--composed[data-page-accent="blue"],
.cockpit-cell[data-element-color="blue"] {
    --color-accent-from: color-mix(in srgb, var(--hue-blue-fill) 88%, #ffffff);
    --color-accent-via: var(--hue-blue-fill);
    --color-accent-to: color-mix(in srgb, var(--hue-blue-fill) 88%, #000000);
    --color-accent-primary: var(--hue-blue-fill);
    --color-accent-primary-hover: color-mix(in srgb, var(--hue-blue-fill) 85%, #000000);
    --color-accent-glow: color-mix(in srgb, var(--hue-blue-fill) 30%, transparent);
    --color-accent-subtle: color-mix(in srgb, var(--hue-blue-fill) 3%, transparent);
    --color-text-on-accent: var(--hue-blue-ink);
    --color-accent-text: var(--hue-blue-text);
}

/* === THEME: NAVY === */
body[data-theme="navy"],
.cockpit--composed[data-page-accent="navy"],
.cockpit-cell[data-element-color="navy"] {
    --color-accent-from: color-mix(in srgb, var(--hue-navy-fill) 88%, #ffffff);
    --color-accent-via: var(--hue-navy-fill);
    --color-accent-to: color-mix(in srgb, var(--hue-navy-fill) 88%, #000000);
    --color-accent-primary: var(--hue-navy-fill);
    --color-accent-primary-hover: color-mix(in srgb, var(--hue-navy-fill) 85%, #000000);
    --color-accent-glow: color-mix(in srgb, var(--hue-navy-fill) 30%, transparent);
    --color-accent-subtle: color-mix(in srgb, var(--hue-navy-fill) 3%, transparent);
    --color-text-on-accent: var(--hue-navy-ink);
    --color-accent-text: var(--hue-navy-text);
}

/* === THEME: FOREST === */
body[data-theme="forest"],
.cockpit--composed[data-page-accent="forest"],
.cockpit-cell[data-element-color="forest"] {
    --color-accent-from: color-mix(in srgb, var(--hue-forest-fill) 88%, #ffffff);
    --color-accent-via: var(--hue-forest-fill);
    --color-accent-to: color-mix(in srgb, var(--hue-forest-fill) 88%, #000000);
    --color-accent-primary: var(--hue-forest-fill);
    --color-accent-primary-hover: color-mix(in srgb, var(--hue-forest-fill) 85%, #000000);
    --color-accent-glow: color-mix(in srgb, var(--hue-forest-fill) 30%, transparent);
    --color-accent-subtle: color-mix(in srgb, var(--hue-forest-fill) 3%, transparent);
    --color-text-on-accent: var(--hue-forest-ink);
    --color-accent-text: var(--hue-forest-text);
}

/* === THEME: PLUM === */
body[data-theme="plum"],
.cockpit--composed[data-page-accent="plum"],
.cockpit-cell[data-element-color="plum"] {
    --color-accent-from: color-mix(in srgb, var(--hue-plum-fill) 88%, #ffffff);
    --color-accent-via: var(--hue-plum-fill);
    --color-accent-to: color-mix(in srgb, var(--hue-plum-fill) 88%, #000000);
    --color-accent-primary: var(--hue-plum-fill);
    --color-accent-primary-hover: color-mix(in srgb, var(--hue-plum-fill) 85%, #000000);
    --color-accent-glow: color-mix(in srgb, var(--hue-plum-fill) 30%, transparent);
    --color-accent-subtle: color-mix(in srgb, var(--hue-plum-fill) 3%, transparent);
    --color-text-on-accent: var(--hue-plum-ink);
    --color-accent-text: var(--hue-plum-text);
}

/* === THEME: SLATE (the owner's "charcoal") === */
body[data-theme="slate"],
.cockpit--composed[data-page-accent="slate"],
.cockpit-cell[data-element-color="slate"] {
    --color-accent-from: color-mix(in srgb, var(--hue-slate-fill) 88%, #ffffff);
    --color-accent-via: var(--hue-slate-fill);
    --color-accent-to: color-mix(in srgb, var(--hue-slate-fill) 88%, #000000);
    --color-accent-primary: var(--hue-slate-fill);
    --color-accent-primary-hover: color-mix(in srgb, var(--hue-slate-fill) 85%, #000000);
    --color-accent-glow: color-mix(in srgb, var(--hue-slate-fill) 30%, transparent);
    --color-accent-subtle: color-mix(in srgb, var(--hue-slate-fill) 3%, transparent);
    --color-text-on-accent: var(--hue-slate-ink);
    --color-accent-text: var(--hue-slate-text);
}

/* The six colors that began as page surfaces. A page or an element may wear
   any of them (owner, 2026-09-28: "Make all colors available for all
   options"); they are not app themes, so they have no <body> selector. */
.cockpit--composed[data-page-accent="sand"],
.cockpit-cell[data-element-color="sand"] {
    --color-accent-from: color-mix(in srgb, var(--hue-sand-fill) 88%, #ffffff);
    --color-accent-via: var(--hue-sand-fill);
    --color-accent-to: color-mix(in srgb, var(--hue-sand-fill) 88%, #000000);
    --color-accent-primary: var(--hue-sand-fill);
    --color-accent-primary-hover: color-mix(in srgb, var(--hue-sand-fill) 85%, #000000);
    --color-accent-glow: color-mix(in srgb, var(--hue-sand-fill) 30%, transparent);
    --color-accent-subtle: color-mix(in srgb, var(--hue-sand-fill) 3%, transparent);
    --color-text-on-accent: var(--hue-sand-ink);
    --color-accent-text: var(--hue-sand-text);
}

.cockpit--composed[data-page-accent="mist"],
.cockpit-cell[data-element-color="mist"] {
    --color-accent-from: color-mix(in srgb, var(--hue-mist-fill) 88%, #ffffff);
    --color-accent-via: var(--hue-mist-fill);
    --color-accent-to: color-mix(in srgb, var(--hue-mist-fill) 88%, #000000);
    --color-accent-primary: var(--hue-mist-fill);
    --color-accent-primary-hover: color-mix(in srgb, var(--hue-mist-fill) 85%, #000000);
    --color-accent-glow: color-mix(in srgb, var(--hue-mist-fill) 30%, transparent);
    --color-accent-subtle: color-mix(in srgb, var(--hue-mist-fill) 3%, transparent);
    --color-text-on-accent: var(--hue-mist-ink);
    --color-accent-text: var(--hue-mist-text);
}

.cockpit--composed[data-page-accent="ice"],
.cockpit-cell[data-element-color="ice"] {
    --color-accent-from: color-mix(in srgb, var(--hue-ice-fill) 88%, #ffffff);
    --color-accent-via: var(--hue-ice-fill);
    --color-accent-to: color-mix(in srgb, var(--hue-ice-fill) 88%, #000000);
    --color-accent-primary: var(--hue-ice-fill);
    --color-accent-primary-hover: color-mix(in srgb, var(--hue-ice-fill) 85%, #000000);
    --color-accent-glow: color-mix(in srgb, var(--hue-ice-fill) 30%, transparent);
    --color-accent-subtle: color-mix(in srgb, var(--hue-ice-fill) 3%, transparent);
    --color-text-on-accent: var(--hue-ice-ink);
    --color-accent-text: var(--hue-ice-text);
}

.cockpit--composed[data-page-accent="sage"],
.cockpit-cell[data-element-color="sage"] {
    --color-accent-from: color-mix(in srgb, var(--hue-sage-fill) 88%, #ffffff);
    --color-accent-via: var(--hue-sage-fill);
    --color-accent-to: color-mix(in srgb, var(--hue-sage-fill) 88%, #000000);
    --color-accent-primary: var(--hue-sage-fill);
    --color-accent-primary-hover: color-mix(in srgb, var(--hue-sage-fill) 85%, #000000);
    --color-accent-glow: color-mix(in srgb, var(--hue-sage-fill) 30%, transparent);
    --color-accent-subtle: color-mix(in srgb, var(--hue-sage-fill) 3%, transparent);
    --color-text-on-accent: var(--hue-sage-ink);
    --color-accent-text: var(--hue-sage-text);
}

.cockpit--composed[data-page-accent="blush"],
.cockpit-cell[data-element-color="blush"] {
    --color-accent-from: color-mix(in srgb, var(--hue-blush-fill) 88%, #ffffff);
    --color-accent-via: var(--hue-blush-fill);
    --color-accent-to: color-mix(in srgb, var(--hue-blush-fill) 88%, #000000);
    --color-accent-primary: var(--hue-blush-fill);
    --color-accent-primary-hover: color-mix(in srgb, var(--hue-blush-fill) 85%, #000000);
    --color-accent-glow: color-mix(in srgb, var(--hue-blush-fill) 30%, transparent);
    --color-accent-subtle: color-mix(in srgb, var(--hue-blush-fill) 3%, transparent);
    --color-text-on-accent: var(--hue-blush-ink);
    --color-accent-text: var(--hue-blush-text);
}

.cockpit--composed[data-page-accent="stone"],
.cockpit-cell[data-element-color="stone"] {
    --color-accent-from: color-mix(in srgb, var(--hue-stone-fill) 88%, #ffffff);
    --color-accent-via: var(--hue-stone-fill);
    --color-accent-to: color-mix(in srgb, var(--hue-stone-fill) 88%, #000000);
    --color-accent-primary: var(--hue-stone-fill);
    --color-accent-primary-hover: color-mix(in srgb, var(--hue-stone-fill) 85%, #000000);
    --color-accent-glow: color-mix(in srgb, var(--hue-stone-fill) 30%, transparent);
    --color-accent-subtle: color-mix(in srgb, var(--hue-stone-fill) 3%, transparent);
    --color-text-on-accent: var(--hue-stone-ink);
    --color-accent-text: var(--hue-stone-text);
}

/* === ACCENT GRADIENT (Phase B round 2) ===
   The from→via→to sweep, reusable so app-shell accent surfaces (active pills,
   primary buttons, panel top edge) evolve left-to-right like the landing hero.
   MUST be defined here — on the themed [data-theme] element (the <body>) — NOT on
   :root. The nested var(--color-accent-*) are substituted at the element where
   THIS custom property is declared; the accent vars live on [data-theme]/<body>,
   so declaring these on :root (html) would resolve the inner vars against html
   (where they're absent) → guaranteed-invalid → the poisoned value inherits down
   and every gradient surface silently falls back to no background. */
/* A page that chose its own accent joins this block too (Task 4.1), and has to:
   these are DERIVED values, substituted where they are declared. Left on <body>
   alone, a violet page would paint its primary buttons with the app's teal
   gradient while inking them from violet's own --color-text-on-accent — white
   on teal, 3.34:1, which is the exact pair the teal block overrides that ink to
   avoid. Declaring them on the page root too re-resolves them against the
   accent the page actually chose. */
body[data-theme],
.cockpit--composed[data-page-accent],
.cockpit-cell[data-element-color]:not([data-element-color="default"]) {
    --gradient-accent:   linear-gradient(135deg, var(--color-accent-from), var(--color-accent-via), var(--color-accent-to));
    --gradient-accent-h: linear-gradient(90deg,  var(--color-accent-from), var(--color-accent-via), var(--color-accent-to));

    /* Process-graph NODE stroke — accent-derived, so MUST live here on the themed
       [data-theme] element (the <body>), NOT :root: the nested
       var(--color-accent-primary) is resolved where THIS property is declared, and
       the accent vars only exist on [data-theme]. Declaring on :root would resolve
       the accent to nothing → transparent. (See the note above the gradient tokens
       and tokens.css line ~170.) Consumed by .gmap-node (graph-skin.css). The node
       FILL is no longer accent-derived — it is per-product plain hex on :root (see
       the PRODUCT NODE FILLS block below); the stroke channel stays reserved (§7.5). */
    --node-stroke: color-mix(in srgb, var(--color-accent-primary) 75%, transparent);
}

/* === NODE FILL ===
   A map node paints from its own stored colour; a node with none gets this one
   neutral fill, whatever area it belongs to. The former per-product fills are
   gone: an area's name is data, never a colour key. Kept as a FIXED plain-hex
   core on :root (NOT color-mix, NOT accent-derived) so the white-label look
   survives an accent switch and tools/token-contrast.test.mjs can read it; it
   is dark in both modes and clears 4.5:1 against the always-white
   --node-label-ink (12.63 measured). Strokes stay accent-derived via
   --node-stroke; the stroke channel is reserved (§7.5). */
:root {
    --node-label-ink:        #ffffff;   /* node titles — always white, never the theme-flipping --color-text-on-accent */
    --node-fill-neutral:     #333333;   /* the one fill for a node with no colour of its own */
}

/* === STATUS COLORS (App) === */
:root {
    --success: #4caf82;
    --error: #e05c5c;
    --warning: #e0a44a;
    --info: #3b82f6;
    --running: #7c85ff;
    --status-not-started: rgba(128, 128, 128, 0.3);
    --status-in-dev: rgba(255, 170, 0, 0.3);
    --status-operational: rgba(0, 200, 100, 0.3);
    --status-not-started-text: #808080;
    --status-in-dev-text: #ffaa00;
    --status-operational-text: #00c864;
    --status-unpublished-text: #3b82f6;
}

/* === SENSOR THRESHOLD COLORS === */
:root {
    --sensor-green: #4caf82;
    --sensor-yellow: #e0a44a;
    --sensor-red: #e05c5c;
    /* One grey in both modes, on purpose. This paints marks rather than text —
       a chip's border, an edge's stroke, a legend swatch — so it needs a mid
       grey that holds against the dark and the light canvas alike, and the
       light text-muted would all but vanish on the light one. Written as a
       literal because it never behaved as an alias: this is the value both
       modes have always painted, so stating it changes no pixel.
       tools/token-declaration-site.test.mjs is what keeps it honest. */
    --sensor-calibrating: #707070;
    --sensor-green-subtle: rgba(76, 175, 130, 0.10);
    --sensor-yellow-subtle: rgba(224, 164, 74, 0.10);
    --sensor-red-subtle: rgba(224, 92, 92, 0.10);
    --sensor-calibrating-subtle: rgba(112, 112, 112, 0.10);

    /* Nav-graph status edges (dark/empty have no sensor equivalent) */
    --status-empty: #4a4a52;   /* quiet but visibly-present neutral outline */
    --status-dark: #6a6a72;    /* fogged / desaturated edge for a stale gauge */
    /* Lifecycle in-progress glyph (◐) — a blue distinct from done-green,
       sensor-yellow/red, and the teal accent. Dark-mode value. */
    --lifecycle-progress: #6ba4e0;
}

/* === NAV-GRAPH STATUS EDGES (Light Mode) === */
body[data-mode="light"] {
    --status-empty: #c8ccd2;
    --status-dark: #9aa0a8;
    /* Lifecycle in-progress glyph (◐) — light-mode value. */
    --lifecycle-progress: #2f6fb0;
}

/* === BUILD SELECTION RING ===
   What Build draws around the thing being edited. It is the app's own accent
   as readable ink, taken on <body> so a page or an element that chose an
   accent of its own cannot recolor it: the ring belongs to the editor, not to
   the page being edited, and it must never look like part of the design. */
body {
    --color-build-ring: var(--color-accent-text);
}

/* The name tag Build draws on the thing being edited is Build's label, taken
   on <body> for the same reason as the ring: an element's Text color also sets
   the app's main text color inside it, and the tag must never wear it. */
body {
    --color-build-tag-ink: var(--color-text-primary);
    --color-build-tag-quiet-ink: var(--color-text-secondary);
    --color-build-tag-surface: var(--color-bg-elevated);
    --color-build-tag-quiet-edge: var(--color-border-control);
}
