:root {
    /* Declares dark support to the UA. Without this, Android's WebView treats the page as
       light-only: System theme never matches prefers-color-scheme, and (with algorithmic
       darkening enabled in MauiProgram) Chromium AUTO-INVERTS the palette instead of using
       our real dark tokens - the "broken colors" failure. `only light` on the explicit
       Light toggle forbids that inversion on dark-OS devices. */
    color-scheme: light dark;

    /* Surfaces & text */
    --color-bg:              #F8F7F2;
    --color-surface:         #FFFFFF;
    --color-surface-muted:   #F1EEE6;
    --color-border:          #E7E2D7;
    --color-border-strong:   #D2CABA;
    --color-text:            #211D16;
    --color-text-muted:      #6B6355;
    --color-text-subtle:     #736B5C;   /* AA: ~4.9:1 on --color-bg (≥4.5) */
    /* Amber is a light accent - dark ink on fills, never white. */
    --color-text-on-accent:  #201A11;
    /* Theme-independent white - do not tie to --color-text-on-accent. */
    --color-text-on-dark:    #FFFFFF;
    --color-overlay:         rgba(33, 27, 18, .55);
    --color-scrim:           rgba(33, 27, 18, .45);

    /* App chrome */
    --color-chrome:          #1F2530;
    --color-chrome-text:     #F4F1E9;
    --color-chrome-muted:    #A9A294;
    --color-chrome-hover:    rgba(255, 255, 255, .07);
    --color-chrome-active:   rgba(255, 255, 255, .12);
    --color-chrome-edge:     rgba(255, 255, 255, .08);
    /* Phase 5B tab-bar pill fill - one step up FROM --color-chrome, not a reuse of it. In every
       dark palette --color-bg and --color-chrome sit within ~1:1 of each other (rule 7: dark
       separation is surface-lightening, never shadow), so the floating pill needs its own
       lightened rung or it vanishes against the ground it floats over. Paired with a
       --color-chrome-edge hairline on .tab-bar__pill so it never relies on lightness alone -
       the same border+lift combo --color-card-border gives a borderless card. In light palettes
       this is cosmetic (bg-vs-chrome is already 11-16:1); values below are the retired native
       chrome's ChromePillLight/Dark (App.xaml / ThemeExtensions.ChromeColors), hue-matched per
       theme rather than one flat navy borrowed across every palette. */
    --color-chrome-pill:     #2B3340;
    --color-chrome-mark:     #E9B455;

    /* Matches PdfTheme.Accent (#D69528) - keep in sync. */
    --color-primary:         #D69528;
    --color-primary-hover:   #C28420;
    --color-primary-active:  #A06E12;
    --color-primary-soft:    #FAF1DB;
    --color-primary-softer:  #FDF8EE;
    --color-primary-text:    #8A5E0F;
    --color-primary-bright:  #E9B455;

    /* Separate from --color-primary - amber fails 3:1 WCAG for non-text. */
    --color-focus:           #A06E12;

    --color-surface-hover:   #F2EFE7;
    --color-surface-active:  #EAE5D9;

    --color-surface-sunken:  #EDE9DF;
    --color-surface-raised:  #FFFFFF;

    --color-card:            #FFFFFF;
    --color-card-raised:     #FFFFFF;
    /* Transparent in light; faint edge-light in dark (see dark block). */
    --color-card-border:     transparent;
    --color-row-zebra:       var(--color-surface-muted);

    /* Registration anchor-pair hues, scoped away from --color-primary. */
    --reg-anchor-c1:         #DC2626;
    --reg-anchor-c2:         #0284C7;
    --reg-anchor-c3:         #047857;
    --reg-anchor-c4:         #7C3AED;
    --reg-anchor-c5:         #EA580C;
    --reg-anchor-c6:         #DB2777;
    --reg-marker-ink:        #FFFFFF;
    /* Theme-independent - halo reads over photographic tiles. */
    --reg-marker-halo:       #FFFFFF;
    --reg-marker-halo-edge:  rgba(15, 23, 42, .55);

    /* Status */
    --color-danger:          #D92D20;
    /* Ink for a solid --color-danger fill. Light red in dark mode is a LIGHT fill, so it
       takes dark ink for the same reason the amber accent does (white was 3.10:1). */
    --color-text-on-danger:  #FFFFFF;
    --color-danger-soft:     #FEE9E7;
    --color-danger-text:     #B42318;
    --color-success:         #12915F;
    --color-success-soft:    #DCF5EA;
    --color-success-text:    #0E7A4F;
    --color-warning:         #DC8A09;
    --color-warning-soft:    #FCEFD8;
    --color-warning-text:    #A96703;

    /* Bridged into maps.module.js via getComputedStyle - Mapbox can't read var(). */
    --color-wall:            #1E293B;
    --color-wall-vertex:     #0D9488;
    /* Property line - violet, so it never reads as a wall or an amber closet ring. */
    --color-boundary:        #6D4AFF;

    /* Radii */
    --radius-sm:    5px;
    --radius-md:    8px;
    --radius-lg:    12px;
    --radius-xl:    16px;
    --radius-pill:  9999px;

    /* rem, not px - a text-bearing band must grow with type rather than clip it. */
    --app-chrome-height: 3rem;      /* 48px @ 16px root */
    --touch-target-min:  2.75rem;   /* 44px @ 16px root */

    /* Spacing (4px base, expressed in rem - see the type scale note below) */
    /* The half-step is the icon<->label micro-gap, an unnamed scale step that 31 bare `2px`
       declarations across the feature sheets were already using. Theme-independent. */
    --space-0-5: 0.125rem; /*  2px */
    --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 */

    /* Aliases (-rest/-card/-pop) live only here - don't redeclare in dark/PM blocks. */
    /* rgba/hex only - no oklch (older Android WebView rejects it). */
    --shadow-0:    none;
    --shadow-1:    inset 0 1px 2px rgba(255, 255, 255, 0), 0 1px 2px rgba(61, 50, 32, .10), 0 2px 8px rgba(61, 50, 32, .08);
    --shadow-2:    inset 0 1px 2px rgba(255, 255, 255, 0), 0 2px 4px rgba(61, 50, 32, .10), 0 6px 16px rgba(61, 50, 32, .12);
    --shadow-3:    inset 0 1px 2px rgba(255, 255, 255, 0), 0 4px 12px rgba(61, 50, 32, .11), 0 16px 48px rgba(61, 50, 32, .18);
    --shadow-sm:   0 1px 2px rgba(61, 50, 32, .06), 0 1px 1px rgba(61, 50, 32, .04);
    --shadow-md:   0 2px 6px rgba(61, 50, 32, .08), 0 1px 2px rgba(61, 50, 32, .06);
    --shadow-lg:   0 12px 32px rgba(61, 50, 32, .13), 0 2px 8px rgba(61, 50, 32, .07);
    --shadow-pop:  var(--shadow-3);
    --shadow-card: var(--shadow-2);
    --shadow-rest: var(--shadow-1);
    --shadow-inset: inset 0 1px 3px rgba(61, 50, 32, .07);
    --shadow-focus-ring: 0 0 0 3px rgba(214, 149, 40, .32);

    /* Theme-independent - no dark-block duplicate. */
    --motion-fast:   100ms;
    --motion-base:   150ms;
    --motion-medium: 200ms;
    --motion-slow:   220ms;
    --ease-out:      ease-out;
    --ease-in:       ease-in;
    --ease-standard: cubic-bezier(0.2, 0.8, 0.2, 1);
    --ease-spring:   cubic-bezier(0.34, 1.56, 0.64, 1);

    /* Set inline on <html> by theme.module.js setUiScale. --ui-scale is the TOTAL
       magnification of a CSS px (OS text-size multiplier, clamped) and exists to divide
       env(safe-area-inset-*) back out - env() resolves unmagnified and is then blown up,
       under a native page zoom (iOS) exactly as under CSS zoom (Android). --ui-zoom is the
       part applied HERE as CSS zoom; it is 1 on iOS, where WKWebView.PageZoom already did
       it. Only map-canvas.css consumes --ui-zoom, to counter-zoom itself back to 1:1. */
    --ui-scale: 1;
    --ui-zoom: 1;

    /* Measured viewport units, published by theme.module.js initViewportMetrics: one hundredth
       of the usable height/width expressed in the ZOOMED subtree's own coordinate space. CSS
       `zoom` does not rescale vh/vw, so a raw 100vh overflows a zoomed shell whose overflow is
       hidden. Always consume with the fallback - `calc(var(--vh, 1vh) * 88)` - so every rule
       stays valid before JS runs, and on a platform (iOS PageZoom) where vh is already correct. */
    --vh: 1vh;
    --vw: 1vw;

    /* THE page side gutter. Every routed page's header band AND its scrolling body consume
       this one value, so the content column can never drift between the two (Home used to sit
       at 20px in the header and 16px in the body) and can never disagree between pages (the
       app shipped four different gutters at once: 12 on site/project detail, 16 on Home/Photos,
       20 on the Home header, 24 on Clients/Kits/Settings - so the column jumped on every
       navigation). Change the gutter here, not in a page sheet. The phone step is in the
       media block below (599px == the phone breakpoint; var() is invalid in media conditions). */
    --page-gutter: var(--space-6);

    /* ── Layer ladder ──────────────────────────────────────────────────────────────
       Theme-independent - no dark-block duplicate, same rule as the motion tokens.

       TWO BANDS. Conflating them is the trap this scale exists to close.

       LOCAL band (raw single/double digits, deliberately NOT tokenized): stacking
       among siblings INSIDE one component's own stacking context - a pin's icon over
       its cone, a wall vertex over its edge, the welcome-intro art. A raw number is
       honest there because the values are never compared across components. The
       contract is that the owning container MUST actually establish a stacking
       context (`isolation: isolate` - never rely on `transform`/`zoom`, which stop
       creating one at their identity value and would leak the whole band).

       APP band (these tokens): anything that ESCAPES its container - `position:fixed`,
       or absolute against the shell. Every such element takes a token, never a
       literal. A new overlay picks the rung that describes it; it never invents a
       number and never hand-tunes ±1 against a neighbour.

       Each rung names what it must cover. The order is load-bearing. */
    --z-content:        0;   /* in-flow page content */
    --z-raised:        10;   /* an item lifting above its own list (a selected pin) */
    --z-page-chrome:   15;   /* the floating .page__header - over a lifted list item, under the drawer.
                                DELIBERATELY below --z-anchored: a dropdown opened from a list row must
                                overlay the header exactly as it did when the header was still in flow. */
    --z-drawer:        20;   /* tablet editor sidebar */
    --z-drawer-toggle: 30;   /* its open/close handle */
    --z-editor-chrome: 40;   /* editor furniture pinned to the canvas edge: toolbars, phone top bar */
    --z-editor-banner: 50;   /* the advisory banner STACK over the canvas (one container, see below) */
    --z-editor-bar:    60;   /* phone bottom bar */
    --z-editor-tool:   70;   /* phone wall toolbar - above the bar */
    --z-editor-sheet:  80;   /* phone sheet - covers everything docked */
    --z-anchored:     100;   /* panels anchored to a trigger: overflow menu, place search */
    --z-tab-bar:      150;   /* the phone shell's floating tab bar - shell furniture, so above an
                                anchored page panel, and far below --z-coach so a tutorial can still
                                point AT the bar (and therefore below --z-modal/--z-toast too) */
    --z-coach:        900;   /* tutorial coach - DELIBERATELY below modals, it must not block one */
    --z-modal:       1000;   /* modal backdrop + modal */
    --z-modal-stack: 1100;   /* a modal opened from a modal */
    --z-toast:       1200;   /* MUST outrank a stacked modal - it hosts the delete Undo (rule 11) */
    --z-error:       1300;   /* #blazor-error-ui - must outrank an open modal to be readable */
    --z-debug:       1400;   /* debug dock */
    --z-menu:        1500;   /* context / overflow menu, summoned at the cursor over anything */
    --z-intro:       2000;   /* first-run welcome intro - covers the entire shell */

    /* -apple-system first for native SF; else vendored Inter Variable. */
    --font-sans: -apple-system, BlinkMacSystemFont, "Inter", "Segoe UI Variable Text", "Segoe UI", system-ui, sans-serif;
    /* ui-monospace resolves SF Mono on Apple; else Cascadia/Segoe UI Mono/Consolas. */
    --font-mono: ui-monospace, SFMono-Regular, "Cascadia Mono", "Segoe UI Mono", Consolas, monospace;

    /* rem, anchored to the `html { font-size: 100% }` in base.css, so 1rem = 16px - it exists
       so type and spacing can respond to a root font-size lever without touching the ~900 px
       literals in the feature sheets. Radii and hairline borders stay px on purpose: they
       should not track font size.

       Retuned to Apple's iOS/iPadOS Large(default) text-style table: nine of the eleven steps
       now land exactly on a named style. The reading tier moved ONE RUNG DOWN the ladder -
       `body` binds --text-md (17px Body/Headline, HIG's platform default), and --text-base
       became 16px Callout, which is also exactly WebKit's focus auto-zoom threshold, so a
       focused .input no longer zooms the page and collapses the measured --vh. 4xl/5xl are
       deliberately off-ladder display sizes (hero glyphs, stat numerals) HIG does not govern.
       Theme-independent - these NEVER get a dark-block duplicate. */
    --text-2xs:  0.6875rem;   /* 11px - Caption 2. The iOS floor. Non-linguistic badges/counts only. */
    --text-xs:   0.75rem;     /* 12px - Caption 1 */
    --text-sm:   0.8125rem;   /* 13px - Footnote */
    --text-base: 1rem;        /* 16px - Callout. Dense rows, chips, .input (the auto-zoom floor). */
    --text-md:   1.0625rem;   /* 17px - Body/Headline. << `body` binds HERE (base.css) */
    --text-lg:   1.25rem;     /* 20px - Title 3 */
    --text-xl:   1.375rem;    /* 22px - Title 2 */
    --text-2xl:  1.75rem;     /* 28px - Title 1 */
    --text-3xl:  2.125rem;    /* 34px - Large Title */
    --text-4xl:  2.5rem;      /* 40px - off-ladder display numeral */
    --text-5xl:  3rem;        /* 48px - off-ladder hero glyph (empty-state icons) */

    --leading-xs:   1.4;
    --leading-sm:   1.45;
    --leading-base: 1.5;
    --leading-md:   1.5;
    --leading-lg:   1.4;
    --leading-xl:   1.3;
    --leading-2xl:  1.25;
    --leading-3xl:  1.2;
    --leading-4xl:  1.15;

    --weight-regular:  400;
    --weight-medium:   500;
    --weight-semibold: 600;
    --weight-bold:     700;

    /* Theme-independent, like the type scale - no dark-block duplicate. */
    --tracking-tight: -0.02em;
    --tracking-loose:  0.04em;
    --tracking-wide:   0.08em;

    /* FOV preset colors */
    --color-fov-90:          #3B82F6;
    --color-fov-90-soft:     rgba(59, 130, 246, 0.25);
    --color-fov-120:         #22C55E;
    --color-fov-120-soft:    rgba(34, 197, 94, 0.25);
    --color-fov-180:         #F97316;
    --color-fov-180-soft:    rgba(249, 115, 22, 0.25);
    --color-fov-360:         #A855F7;
    --color-fov-360-soft:    rgba(168, 85, 247, 0.25);
    --color-fov-custom:      #94A3B8;
    --color-fov-custom-soft: rgba(148, 163, 184, 0.25);

    /* Bridged into maps.module.js via getComputedStyle, like --color-wall. */
    --color-measure:         #F97316;

    /* One gradient shared by hero CTA, survey pill, Camera FAB. */
    --gradient-primary: linear-gradient(180deg, var(--color-primary-bright), var(--color-primary));
    /* Theme-independent colored glow - reads on light and near-black. */
    --shadow-glow-primary: 0 10px 24px -6px rgba(214, 149, 40, .45);

    /* Sits over the dark satellite map - theme-independent by design. */
    /* Deeper tint than the old .68/.74 - these bars lost their backdrop blur (an Android
       per-frame perf killer over the GL map; see designs.css), so opacity now carries
       the legibility the blur used to. */
    --frost-canvas-bar:       rgba(26, 21, 13, .90);
    --frost-canvas-dock:      rgba(26, 21, 13, .93);
    --frost-canvas-hairline:  rgba(255, 255, 255, .09);
    --frost-canvas-ink:       #FFFFFF;
    /* Rides over live map tiles, so the composited background is variable - measured
       2.96:1 worst case at #CBBFA6. Brightened to clear 4.5:1 over the lightest. */
    --frost-canvas-ink-muted: #EEEBE3;

    /* ── Material ladder (the Liquid Glass approximation) ───────────────────────────
       Geometry only - theme-independent, no dark twin, same rule as the motion and
       layer tokens. The per-theme half is the two FILL tokens below, which live in
       every block that redefines --color-card (see the note there).

       These are inert until an element opts in inside the `@supports` rung in
       components.css AND the platform stamps [data-glass="on"] (ThemeInterop, iOS
       only - HIG-REBUILD-PLAN.md §3.1.2). The floor is opaque everywhere else, and
       on a WebView without backdrop-filter that floor is PERMANENT, not interim
       (§2 decision 16).

       NOTHING over the canvas consumes these. The bars floating over MapCanvas and
       the floor plan keep the --frost-canvas-* set above, which is their permanent
       rendering - Android re-blurs a backdrop on every GL frame (designs.css). */
    --glass-blur-regular: 20px;
    --glass-blur-clear:   30px;
    --glass-saturate:     180%;
    /* materials.md's literal 35% dimming layer, for clear over bright content. */
    --glass-dim-clear:    rgba(0, 0, 0, .35);

    /* Per-theme - a translucent cut of this palette's --color-card, so a glass panel
       reads as the same surface as its opaque floor. rgba, never color-mix(): older
       Android WebView rejects it, the same compatibility reason oklch is banned above.
       The `clear` pair has no consumer in this phase (its only legitimate home is
       chrome over media, which is exactly the canvas we permanently excluded) - it is
       defined so a future decision doesn't reinvent it. */
    --glass-fill-regular: rgba(255, 255, 255, .72);
    --glass-fill-clear:   rgba(255, 255, 255, .40);
}

/* Phone tightens the gutter: 24px each side off a ~385px viewport spends a eighth of the
   screen on margin. This is a LAYOUT token, not a theme one, so it sits outside the
   light/dark blocks and needs no dark twin. */
@media (max-width: 599px) {
    :root {
        --page-gutter: var(--space-4);
    }
}

/* Two triggers: System (@media, NO attribute) and the explicit Dark toggle ([data-theme]).
   Keep the two dark blocks below IDENTICAL - edit both or neither.

   The media block is keyed on :root:not([data-theme]) - System, and only System. It used to
   read :not([data-theme="light"]), which was equivalent while every other theme restated the
   whole dark set. That stops being true the moment a LIGHT palette lands as a DELTA block
   (highnoon / walnut / prussian-light do): on a dark-OS phone this block would match one, and
   every token the delta leaves alone would silently keep its harvest-dark value. */

@media (prefers-color-scheme: dark) {
    :root:not([data-theme]) {
        --color-bg:              #14120D;
        --color-surface:         #1D1A14;
        --color-surface-muted:   #241F18;
        --color-border:          #342E24;
        --color-border-strong:   #493F31;
        --color-text:            #F0EADF;
        --color-text-muted:      #AEA694;
        --color-text-subtle:     #989081;
        --color-text-on-accent:  #201A11;
        --color-overlay:         rgba(0, 0, 0, .62);
        --color-scrim:           rgba(0, 0, 0, .52);

        --color-chrome:          #171410;
        --color-chrome-text:     #F4F1E9;
        --color-chrome-muted:    #ADA593;
        --color-chrome-hover:    rgba(255, 255, 255, .07);
        --color-chrome-active:   rgba(255, 255, 255, .12);
        --color-chrome-edge:     rgba(255, 255, 255, .09);
        /* Same rung as --color-card (var(), not a literal - stays in sync with it for free). */
        --color-chrome-pill:     var(--color-card);
        --color-chrome-mark:     #EDB85A;

        --color-primary:         #E0A63E;
        --color-primary-hover:   #EDB85A;
        --color-primary-active:  #C88F26;
        --color-primary-soft:    #3A2C10;
        --color-primary-softer:  #2A2010;
        --color-primary-text:    #F0C879;
        --color-primary-bright:  #EDB85A;
        --color-focus:           #EDB85A;

        --color-surface-hover:   #241F17;
        --color-surface-active:  #2D2719;

        --color-surface-sunken:  #0D0B07;
        /* Widened so cards read distinct without a border. */
        --color-surface-raised:  #262019;
        --color-card:            #262019;
        --color-card-raised:     #302921;
        /* Hairline stays as edge-light; zebra diverges up the ramp. */
        --color-card-border:     var(--color-border);
        --color-row-zebra:       var(--color-surface-raised);
        /* The glass fill tracks --color-card, so it lives in every block that redefines it -
           six today, which is exactly the count the dark-sync gate balances against. The three
           LIGHT deltas (highnoon / walnut / prussian-light) leave --color-card at #FFFFFF, so
           :root's white fill is already correct for them and they take no override. */
        --glass-fill-regular:    rgba(38, 32, 25, .72);
        --glass-fill-clear:      rgba(38, 32, 25, .40);
        /* Light cool so it reads over near-black satellite imagery. */
        --color-wall:            #DAE2EC;
        /* Brighter teal so it reads over near-black satellite. */
        --color-wall-vertex:     #2DD4BF;
        /* Lifted violet so the property line survives dark satellite imagery. */
        --color-boundary:        #A78BFA;

        /* Keep in sync with light + the other dark block. */
        --reg-anchor-c1:         #DC2626;
        --reg-anchor-c2:         #0284C7;
        --reg-anchor-c3:         #047857;
        --reg-anchor-c4:         #7C3AED;
        --reg-anchor-c5:         #EA580C;
        --reg-anchor-c6:         #DB2777;

        --color-danger:          #F2655A;
        --color-text-on-danger:  #2A0F0C;
        --color-danger-soft:     #3A1714;
        --color-danger-text:     #FCA59D;
        --color-success:         #2FBE82;
        --color-success-soft:    #0F2E22;
        --color-success-text:    #7BE0B4;
        --color-warning:         #ECA13C;
        --color-warning-soft:    #34250D;
        --color-warning-text:    #F5C97F;

        /* Aliases live only in :root - keep both dark blocks in sync. */
        --shadow-0:    none;
        --shadow-1:    inset 0 1px 2px rgba(255, 255, 255, .12), 0 1px 2px rgba(0, 0, 0, .38), 0 2px 8px rgba(0, 0, 0, .30);
        --shadow-2:    inset 0 1px 2px rgba(255, 255, 255, .17), 0 2px 4px rgba(0, 0, 0, .42), 0 6px 16px rgba(0, 0, 0, .40);
        --shadow-3:    inset 0 1px 2px rgba(255, 255, 255, .24), 0 4px 12px rgba(0, 0, 0, .48), 0 16px 48px rgba(0, 0, 0, .55);
        --shadow-sm:   0 1px 2px rgba(0, 0, 0, .35), 0 1px 1px rgba(0, 0, 0, .25);
        --shadow-md:   0 2px 6px rgba(0, 0, 0, .40), 0 1px 2px rgba(0, 0, 0, .30);
        --shadow-lg:   0 12px 32px rgba(0, 0, 0, .50), 0 2px 8px rgba(0, 0, 0, .35);
        --shadow-inset: inset 0 1px 3px rgba(0, 0, 0, .35);
        --shadow-focus-ring: 0 0 0 3px rgba(224, 166, 62, .34);
    }
}

/* ── Increase Contrast promotes High noon ──────────────────────────────────────────────
   accessibility.md: "If your app doesn't provide this minimum contrast by default, ensure it
   at least provides a higher contrast color scheme when the system setting Increase Contrast
   is turned on." High noon IS that scheme - it is the harvest hue with the ground warmed, the
   borders hardened and the ink pushed to near-black - so the setting picks it up rather than
   inventing a tenth palette nobody can preview from the picker.

   SYSTEM ONLY. Both blocks are keyed :root:not([data-theme]), the same discipline the System-
   dark block above documents: an explicit palette is a CHOICE, and silently re-skinning someone
   who picked Walnut because their OS contrast switch is on is the documented failure this
   selector exists to prevent. (:not([data-theme]) also cannot match an element that carries the
   attribute, so ordering against the explicit blocks below is irrelevant - only the ordering
   against the System-dark block above is, and it is: this comes after, so it wins.)

   These two blocks are VERBATIM COPIES of :root[data-theme="highnoon"] and
   :root[data-theme="highnoon-dark"] below. CSS has no way to alias one selector's declarations
   onto another (nesting @media inside the palette rule would do it, but nested CSS is exactly
   the kind of thing older Android WebView drops on the floor - the same compatibility reason
   oklch and color-mix() are banned). Edit High noon, edit these. The light block is the DELTA
   the palette itself is; the dark block is the full override set, and its names are a strict
   superset of the light one's, which is why the light block needs no prefers-color-scheme
   guard - a dark-OS user has every one of its values restated below. */
@media (prefers-contrast: more) {
    :root:not([data-theme]) {
        --color-bg:              #FCF7EA;
        --color-surface:         #FFFFFF;
        --color-surface-muted:   #F5EDD9;
        --color-border:          #E4D7B8;
        --color-border-strong:   #C9B482;
        --color-text:            #17120A;
        --color-text-muted:      #5C5340;
        --color-text-subtle:     #655B46;   /* AA: 6.3:1 on --color-bg */
        --color-text-on-accent:  #1B1305;
        --color-overlay:         rgba(28, 21, 8, .55);
        --color-scrim:           rgba(28, 21, 8, .45);

        --color-chrome:          #241C0C;
        --color-chrome-text:     #FBF4E2;
        --color-chrome-muted:    #B3A785;
        --color-chrome-mark:     #F2B03A;

        --color-primary:         #E39A12;
        --color-primary-hover:   #CE8A08;
        --color-primary-active:  #A76E00;
        --color-primary-soft:    #FBEDCF;
        --color-primary-softer:  #FEF7E8;
        --color-primary-text:    #7E5305;   /* AA: 6.7:1 on white - the accent itself is 2.0:1 */
        --color-primary-bright:  #F2B03A;
        --color-focus:           #A76E00;   /* 4.3:1 on white, over the 3:1 non-text min */

        --color-surface-hover:   #F7F1DF;
        --color-surface-active:  #F0E7CE;
        --color-surface-sunken:  #F2EAD4;

        --shadow-focus-ring: 0 0 0 3px rgba(227, 154, 18, .34);
        --shadow-glow-primary: 0 10px 24px -6px rgba(227, 154, 18, .45);
    }
}

@media (prefers-contrast: more) and (prefers-color-scheme: dark) {
    :root:not([data-theme]) {
        --color-bg:              #100D06;
        --color-surface:         #1B160D;
        --color-surface-muted:   #221C11;
        --color-border:          #332A19;
        --color-border-strong:   #4C3F25;
        --color-text:            #FBF3E2;
        --color-text-muted:      #B5A98C;
        --color-text-subtle:     #9E9377;   /* AA: 6.4:1 on --color-bg, 5.3:1 on --color-card */
        --color-text-on-accent:  #1B1305;
        --color-overlay:         rgba(0, 0, 0, .62);
        --color-scrim:           rgba(0, 0, 0, .52);

        --color-chrome:          #130F07;
        --color-chrome-text:     #FBF4E2;
        --color-chrome-muted:    #B3A785;
        --color-chrome-hover:    rgba(255, 255, 255, .07);
        --color-chrome-active:   rgba(255, 255, 255, .12);
        --color-chrome-edge:     rgba(255, 255, 255, .09);
        --color-chrome-mark:     #F9C158;

        --color-primary:         #F2AE33;
        --color-primary-hover:   #F9C158;
        --color-primary-active:  #D6931C;
        --color-primary-soft:    #43310F;
        --color-primary-softer:  #2E230C;
        --color-primary-text:    #F7D18A;
        --color-primary-bright:  #F9C158;
        --color-focus:           #F9C158;

        --color-surface-hover:   #231D12;
        --color-surface-active:  #2C2416;

        --color-surface-sunken:  #0A0804;
        --color-surface-raised:  #282013;
        --color-card:            #282013;
        --color-card-raised:     #332A1A;
        --color-card-border:     var(--color-border);
        --color-row-zebra:       var(--color-surface-raised);
        --glass-fill-regular:    rgba(40, 32, 19, .72);
        --glass-fill-clear:      rgba(40, 32, 19, .40);
        --color-wall:            #DAE2EC;
        --color-wall-vertex:     #2DD4BF;
        --color-boundary:        #A78BFA;

        --reg-anchor-c1:         #DC2626;
        --reg-anchor-c2:         #0284C7;
        --reg-anchor-c3:         #047857;
        --reg-anchor-c4:         #7C3AED;
        --reg-anchor-c5:         #EA580C;
        --reg-anchor-c6:         #DB2777;

        --color-danger:          #F2655A;
        --color-text-on-danger:  #2A0F0C;
        --color-danger-soft:     #3A1714;
        --color-danger-text:     #FCA59D;
        --color-success:         #2FBE82;
        --color-success-soft:    #0F2E22;
        --color-success-text:    #7BE0B4;
        --color-warning:         #ECA13C;
        --color-warning-soft:    #34250D;
        --color-warning-text:    #F5C97F;

        --shadow-0:    none;
        --shadow-1:    inset 0 1px 2px rgba(255, 255, 255, .12), 0 1px 2px rgba(0, 0, 0, .38), 0 2px 8px rgba(0, 0, 0, .30);
        --shadow-2:    inset 0 1px 2px rgba(255, 255, 255, .17), 0 2px 4px rgba(0, 0, 0, .42), 0 6px 16px rgba(0, 0, 0, .40);
        --shadow-3:    inset 0 1px 2px rgba(255, 255, 255, .24), 0 4px 12px rgba(0, 0, 0, .48), 0 16px 48px rgba(0, 0, 0, .55);
        --shadow-sm:   0 1px 2px rgba(0, 0, 0, .35), 0 1px 1px rgba(0, 0, 0, .25);
        --shadow-md:   0 2px 6px rgba(0, 0, 0, .40), 0 1px 2px rgba(0, 0, 0, .30);
        --shadow-lg:   0 12px 32px rgba(0, 0, 0, .50), 0 2px 8px rgba(0, 0, 0, .35);
        --shadow-inset: inset 0 1px 3px rgba(0, 0, 0, .35);
        --shadow-focus-ring: 0 0 0 3px rgba(242, 174, 51, .36);
        --shadow-glow-primary: 0 10px 24px -6px rgba(242, 174, 51, .45);
    }
}

/* Explicit Light must forbid the WebView's algorithmic darkening (auto-inversion) on a
   dark-OS device - hence `only`. Explicit Dark tells the UA to render native widgets
   (selects, scrollbars, keyboard accessories) dark alongside our dark tokens. */
/* EVERY theme must appear in one of these two lists. A light palette left out of the
   `only light` side is the one that actually breaks: Android's WebView auto-inverts it on a
   dark-OS device, which is the "broken colors" failure described at the top of this file. */
:root[data-theme="light"],
:root[data-theme="highnoon"],
:root[data-theme="walnut"],
:root[data-theme="prussian-light"] { color-scheme: only light; }
:root[data-theme="dark"],
:root[data-theme="prussian"],
:root[data-theme="highnoon-dark"],
:root[data-theme="walnut-dark"] { color-scheme: dark; }

/* Identical to the block above - keep in sync. */
:root[data-theme="dark"] {
    --color-bg:              #14120D;
    --color-surface:         #1D1A14;
    --color-surface-muted:   #241F18;
    --color-border:          #342E24;
    --color-border-strong:   #493F31;
    --color-text:            #F0EADF;
    --color-text-muted:      #AEA694;
    --color-text-subtle:     #989081;
    --color-text-on-accent:  #201A11;
    --color-overlay:         rgba(0, 0, 0, .62);
    --color-scrim:           rgba(0, 0, 0, .52);

    --color-chrome:          #171410;
    --color-chrome-text:     #F4F1E9;
    --color-chrome-muted:    #ADA593;
    --color-chrome-hover:    rgba(255, 255, 255, .07);
    --color-chrome-active:   rgba(255, 255, 255, .12);
    --color-chrome-edge:     rgba(255, 255, 255, .09);
    /* Identical to the @media block above - keep in sync (var(), not a literal). */
    --color-chrome-pill:     var(--color-card);
    --color-chrome-mark:     #EDB85A;

    --color-primary:         #E0A63E;
    --color-primary-hover:   #EDB85A;
    --color-primary-active:  #C88F26;
    --color-primary-soft:    #3A2C10;
    --color-primary-softer:  #2A2010;
    --color-primary-text:    #F0C879;
    --color-primary-bright:  #EDB85A;
    --color-focus:           #EDB85A;

    --color-surface-hover:   #241F17;
    --color-surface-active:  #2D2719;

    --color-surface-sunken:  #0D0B07;
    /* Widened so cards read distinct without a border. */
    --color-surface-raised:  #262019;
    --color-card:            #262019;
    --color-card-raised:     #302921;
    /* Hairline stays as edge-light; zebra diverges up the ramp. */
    --color-card-border:     var(--color-border);
    --color-row-zebra:       var(--color-surface-raised);
    /* Keep in sync with the media block - see the note there. */
    --glass-fill-regular:    rgba(38, 32, 25, .72);
    --glass-fill-clear:      rgba(38, 32, 25, .40);
    /* Light cool so it reads over near-black satellite imagery. */
    --color-wall:            #DAE2EC;
    /* Brighter teal so it reads over near-black satellite. */
    --color-wall-vertex:     #2DD4BF;
    /* Lifted violet so the property line survives dark satellite imagery. */
    --color-boundary:        #A78BFA;

    /* Keep in sync with light + the other dark block. */
    --reg-anchor-c1:         #DC2626;
    --reg-anchor-c2:         #0284C7;
    --reg-anchor-c3:         #047857;
    --reg-anchor-c4:         #7C3AED;
    --reg-anchor-c5:         #EA580C;
    --reg-anchor-c6:         #DB2777;

    --color-danger:          #F2655A;
    --color-text-on-danger:  #2A0F0C;
    --color-danger-soft:     #3A1714;
    --color-danger-text:     #FCA59D;
    --color-success:         #2FBE82;
    --color-success-soft:    #0F2E22;
    --color-success-text:    #7BE0B4;
    --color-warning:         #ECA13C;
    --color-warning-soft:    #34250D;
    --color-warning-text:    #F5C97F;

    /* Identical to the @media block above - keep in sync. */
    --shadow-0:    none;
    --shadow-1:    inset 0 1px 2px rgba(255, 255, 255, .12), 0 1px 2px rgba(0, 0, 0, .38), 0 2px 8px rgba(0, 0, 0, .30);
    --shadow-2:    inset 0 1px 2px rgba(255, 255, 255, .17), 0 2px 4px rgba(0, 0, 0, .42), 0 6px 16px rgba(0, 0, 0, .40);
    --shadow-3:    inset 0 1px 2px rgba(255, 255, 255, .24), 0 4px 12px rgba(0, 0, 0, .48), 0 16px 48px rgba(0, 0, 0, .55);
    --shadow-sm:   0 1px 2px rgba(0, 0, 0, .35), 0 1px 1px rgba(0, 0, 0, .25);
    --shadow-md:   0 2px 6px rgba(0, 0, 0, .40), 0 1px 2px rgba(0, 0, 0, .30);
    --shadow-lg:   0 12px 32px rgba(0, 0, 0, .50), 0 2px 8px rgba(0, 0, 0, .35);
    --shadow-inset: inset 0 1px 3px rgba(0, 0, 0, .35);
    --shadow-focus-ring: 0 0 0 3px rgba(224, 166, 62, .34);
}

/* Prussian - an explicit-only third theme (never matched by prefers-color-scheme, so unlike
   Dark it has no @media twin to keep in sync). Same surface-lightening rule as Dark: a
   blue-black ground with cards stepping UP a prussian ramp, gold brightened one stop to hold
   against it. Colour-picked from the "Prussian dark - night / in-vehicle" concept. */
:root[data-theme="prussian"] {
    --color-bg:              #0D131B;
    --color-surface:         #131D29;
    --color-surface-muted:   #16202D;
    --color-border:          #25344A;
    --color-border-strong:   #374A63;
    --color-text:            #E9EFF7;
    --color-text-muted:      #93A0B1;   /* AA: 7.1:1 on --color-bg, 5.8:1 on --color-card */
    --color-text-subtle:     #8794A6;   /* AA: 6.1:1 on --color-bg, 5.0:1 on --color-card */
    --color-text-on-accent:  #211809;   /* 8.4:1 on --color-primary - gold is a LIGHT accent */
    --color-overlay:         rgba(4, 8, 14, .66);
    --color-scrim:           rgba(4, 8, 14, .55);

    --color-chrome:          #0A1018;
    --color-chrome-text:     #EDF2F9;
    --color-chrome-muted:    #96A3B6;
    --color-chrome-hover:    rgba(255, 255, 255, .07);
    --color-chrome-active:   rgba(255, 255, 255, .12);
    --color-chrome-edge:     rgba(255, 255, 255, .09);
    --color-chrome-pill:     var(--color-card);
    --color-chrome-mark:     #EFBB59;

    /* Gold, one stop up from the light-theme amber so it survives the near-black ground. */
    --color-primary:         #E4AA3C;
    --color-primary-hover:   #F0BC5C;
    --color-primary-active:  #C8901F;
    --color-primary-soft:    #33291A;
    --color-primary-softer:  #241E13;
    --color-primary-text:    #F2CB80;   /* 10.1:1 on --color-card - the accent itself is 2.2:1 */
    --color-primary-bright:  #F0BC5C;
    --color-focus:           #F0BC5C;   /* 8.9:1 on --color-card, well over the 3:1 non-text min */

    --color-surface-hover:   #1B2836;
    --color-surface-active:  #223041;

    --color-surface-sunken:  #070B12;
    /* Cards carry the theme: 2.8x the ground's luminance - the same step Dark uses (2.78x). */
    --color-surface-raised:  #182534;
    --color-card:            #182534;
    --color-card-raised:     #1F2D40;
    /* Hairline stays as edge-light; zebra diverges up the ramp. */
    --color-card-border:     var(--color-border);
    --color-row-zebra:       var(--color-surface-raised);
    --glass-fill-regular:    rgba(24, 37, 52, .72);
    --glass-fill-clear:      rgba(24, 37, 52, .40);
    /* Satellite overlays - identical to both dark blocks, they answer to imagery not theme. */
    --color-wall:            #DAE2EC;
    --color-wall-vertex:     #2DD4BF;
    --color-boundary:        #A78BFA;

    /* Keep in sync with light + both dark blocks. */
    --reg-anchor-c1:         #DC2626;
    --reg-anchor-c2:         #0284C7;
    --reg-anchor-c3:         #047857;
    --reg-anchor-c4:         #7C3AED;
    --reg-anchor-c5:         #EA580C;
    --reg-anchor-c6:         #DB2777;

    /* Hues match Dark; only the soft grounds are blue-shifted onto the prussian ramp. */
    --color-danger:          #F2655A;
    --color-text-on-danger:  #2A0F0C;
    --color-danger-soft:     #3A1A21;
    --color-danger-text:     #FCA59D;
    --color-success:         #2FBE82;
    --color-success-soft:    #0F2E2B;
    --color-success-text:    #7BE0B4;
    --color-warning:         #ECA13C;
    --color-warning-soft:    #342A18;
    --color-warning-text:    #F5C97F;

    /* Identical to both dark blocks - separation is surface-lightening, shadow is only depth. */
    --shadow-0:    none;
    --shadow-1:    inset 0 1px 2px rgba(255, 255, 255, .12), 0 1px 2px rgba(0, 0, 0, .38), 0 2px 8px rgba(0, 0, 0, .30);
    --shadow-2:    inset 0 1px 2px rgba(255, 255, 255, .17), 0 2px 4px rgba(0, 0, 0, .42), 0 6px 16px rgba(0, 0, 0, .40);
    --shadow-3:    inset 0 1px 2px rgba(255, 255, 255, .24), 0 4px 12px rgba(0, 0, 0, .48), 0 16px 48px rgba(0, 0, 0, .55);
    --shadow-sm:   0 1px 2px rgba(0, 0, 0, .35), 0 1px 1px rgba(0, 0, 0, .25);
    --shadow-md:   0 2px 6px rgba(0, 0, 0, .40), 0 1px 2px rgba(0, 0, 0, .30);
    --shadow-lg:   0 12px 32px rgba(0, 0, 0, .50), 0 2px 8px rgba(0, 0, 0, .35);
    --shadow-inset: inset 0 1px 3px rgba(0, 0, 0, .35);
    --shadow-focus-ring: 0 0 0 3px rgba(228, 170, 60, .34);
    /* ponytail: --frost-canvas-* stays warm here, same as Dark leaves it - the editor's
       frosted bar is a dark-in-every-theme instrument surface, not part of the palette. */
}

/* ── The extra palettes ────────────────────────────────────────────────────────
   Every one is EXPLICIT-ONLY, exactly like Prussian: prefers-color-scheme never
   matches them, so none needs an @media twin to keep in sync.

   The LIGHT palettes are deltas over :root - they restate only what the palette
   actually moves. That works only because the System-dark @media block is keyed on
   :root:not([data-theme]) (see above): the older :not([data-theme="light"]) would
   still have matched them on a dark-OS phone and leaked harvest-dark values into
   every token a delta block leaves alone. The DARK palettes restate the FULL dark
   override set, the same rule Prussian follows - :root is light, so anything
   omitted falls through to a light value.

   The invariant that bites: --color-text-on-accent is ink for a FILL, so it follows
   the ACCENT's lightness, not the theme's. Amber and gold are light accents and
   take dark ink; Walnut's berry is a DARK accent and takes white in light mode (and
   is lifted in dark mode, which puts it back on dark ink). Never copy one palette's
   on-accent into another. */

/* High noon - the harvest hue with the contrast cranked: warmer ground, harder
   borders, a more saturated amber, near-black warm ink. */
:root[data-theme="highnoon"] {
    --color-bg:              #FCF7EA;
    --color-surface:         #FFFFFF;
    --color-surface-muted:   #F5EDD9;
    --color-border:          #E4D7B8;
    --color-border-strong:   #C9B482;
    --color-text:            #17120A;
    --color-text-muted:      #5C5340;
    --color-text-subtle:     #655B46;   /* AA: 6.3:1 on --color-bg */
    --color-text-on-accent:  #1B1305;
    --color-overlay:         rgba(28, 21, 8, .55);
    --color-scrim:           rgba(28, 21, 8, .45);

    --color-chrome:          #241C0C;
    --color-chrome-text:     #FBF4E2;
    --color-chrome-muted:    #B3A785;
    --color-chrome-pill:     #2B2417;
    --color-chrome-mark:     #F2B03A;

    --color-primary:         #E39A12;
    --color-primary-hover:   #CE8A08;
    --color-primary-active:  #A76E00;
    --color-primary-soft:    #FBEDCF;
    --color-primary-softer:  #FEF7E8;
    --color-primary-text:    #7E5305;   /* AA: 6.7:1 on white - the accent itself is 2.0:1 */
    --color-primary-bright:  #F2B03A;
    --color-focus:           #A76E00;   /* 4.3:1 on white, over the 3:1 non-text min */

    --color-surface-hover:   #F7F1DF;
    --color-surface-active:  #F0E7CE;
    --color-surface-sunken:  #F2EAD4;

    --shadow-focus-ring: 0 0 0 3px rgba(227, 154, 18, .34);
    --shadow-glow-primary: 0 10px 24px -6px rgba(227, 154, 18, .45);
}

/* High noon dark - full dark override set (see the note above). */
:root[data-theme="highnoon-dark"] {
    --color-bg:              #100D06;
    --color-surface:         #1B160D;
    --color-surface-muted:   #221C11;
    --color-border:          #332A19;
    --color-border-strong:   #4C3F25;
    --color-text:            #FBF3E2;
    --color-text-muted:      #B5A98C;
    --color-text-subtle:     #9E9377;   /* AA: 6.4:1 on --color-bg, 5.3:1 on --color-card */
    --color-text-on-accent:  #1B1305;
    --color-overlay:         rgba(0, 0, 0, .62);
    --color-scrim:           rgba(0, 0, 0, .52);

    --color-chrome:          #130F07;
    --color-chrome-text:     #FBF4E2;
    --color-chrome-muted:    #B3A785;
    --color-chrome-hover:    rgba(255, 255, 255, .07);
    --color-chrome-active:   rgba(255, 255, 255, .12);
    --color-chrome-edge:     rgba(255, 255, 255, .09);
    --color-chrome-pill:     var(--color-card);
    --color-chrome-mark:     #F9C158;

    --color-primary:         #F2AE33;
    --color-primary-hover:   #F9C158;
    --color-primary-active:  #D6931C;
    --color-primary-soft:    #43310F;
    --color-primary-softer:  #2E230C;
    --color-primary-text:    #F7D18A;
    --color-primary-bright:  #F9C158;
    --color-focus:           #F9C158;

    --color-surface-hover:   #231D12;
    --color-surface-active:  #2C2416;

    --color-surface-sunken:  #0A0804;
    --color-surface-raised:  #282013;
    --color-card:            #282013;
    --color-card-raised:     #332A1A;
    --color-card-border:     var(--color-border);
    --color-row-zebra:       var(--color-surface-raised);
    --glass-fill-regular:    rgba(40, 32, 19, .72);
    --glass-fill-clear:      rgba(40, 32, 19, .40);
    /* Satellite overlays answer to imagery, not theme - identical in every dark block. */
    --color-wall:            #DAE2EC;
    --color-wall-vertex:     #2DD4BF;
    --color-boundary:        #A78BFA;

    --reg-anchor-c1:         #DC2626;
    --reg-anchor-c2:         #0284C7;
    --reg-anchor-c3:         #047857;
    --reg-anchor-c4:         #7C3AED;
    --reg-anchor-c5:         #EA580C;
    --reg-anchor-c6:         #DB2777;

    --color-danger:          #F2655A;
    --color-text-on-danger:  #2A0F0C;
    --color-danger-soft:     #3A1714;
    --color-danger-text:     #FCA59D;
    --color-success:         #2FBE82;
    --color-success-soft:    #0F2E22;
    --color-success-text:    #7BE0B4;
    --color-warning:         #ECA13C;
    --color-warning-soft:    #34250D;
    --color-warning-text:    #F5C97F;

    --shadow-0:    none;
    --shadow-1:    inset 0 1px 2px rgba(255, 255, 255, .12), 0 1px 2px rgba(0, 0, 0, .38), 0 2px 8px rgba(0, 0, 0, .30);
    --shadow-2:    inset 0 1px 2px rgba(255, 255, 255, .17), 0 2px 4px rgba(0, 0, 0, .42), 0 6px 16px rgba(0, 0, 0, .40);
    --shadow-3:    inset 0 1px 2px rgba(255, 255, 255, .24), 0 4px 12px rgba(0, 0, 0, .48), 0 16px 48px rgba(0, 0, 0, .55);
    --shadow-sm:   0 1px 2px rgba(0, 0, 0, .35), 0 1px 1px rgba(0, 0, 0, .25);
    --shadow-md:   0 2px 6px rgba(0, 0, 0, .40), 0 1px 2px rgba(0, 0, 0, .30);
    --shadow-lg:   0 12px 32px rgba(0, 0, 0, .50), 0 2px 8px rgba(0, 0, 0, .35);
    --shadow-inset: inset 0 1px 3px rgba(0, 0, 0, .35);
    --shadow-focus-ring: 0 0 0 3px rgba(242, 174, 51, .36);
    --shadow-glow-primary: 0 10px 24px -6px rgba(242, 174, 51, .45);
}

/* Walnut & berry - warm walnut neutrals under a BERRY primary. The berry is the one
   accent in the app that is DARK: it takes white on-accent ink in light mode (7.1:1),
   the exact inverse of the amber rule every other palette follows. */
:root[data-theme="walnut"] {
    --color-bg:              #F7F2EC;
    --color-surface:         #FFFFFF;
    --color-surface-muted:   #EFE7DE;
    --color-border:          #E4D9CD;
    --color-border-strong:   #CDBCAB;
    --color-text:            #241A15;
    --color-text-muted:      #695A50;
    --color-text-subtle:     #71625A;   /* AA: 5.2:1 on --color-bg */
    --color-text-on-accent:  #FFFFFF;   /* 7.1:1 on the berry - dark ink would be 2.9:1 */
    --color-overlay:         rgba(36, 26, 21, .55);
    --color-scrim:           rgba(36, 26, 21, .45);

    --color-chrome:          #2A1D19;
    --color-chrome-text:     #F6EFE9;
    --color-chrome-muted:    #AD9B90;
    --color-chrome-pill:     #33251F;
    --color-chrome-mark:     #C4617F;

    --color-primary:         #8C3F55;
    --color-primary-hover:   #7A3549;
    --color-primary-active:  #682B3D;
    --color-primary-soft:    #F7E4EA;
    --color-primary-softer:  #FCF2F5;
    --color-primary-text:    #7E3549;   /* 8.4:1 on white - berry needs no lift to read as ink */
    --color-primary-bright:  #A85068;
    /* The berry clears 3:1 unaided, so focus can BE the accent (PM does the same). */
    --color-focus:           var(--color-primary);

    --color-surface-hover:   #F2EBE3;
    --color-surface-active:  #EAE0D6;
    --color-surface-sunken:  #EDE4DA;

    --shadow-focus-ring: 0 0 0 3px rgba(140, 63, 85, .30);
    --shadow-glow-primary: 0 10px 24px -6px rgba(140, 63, 85, .45);
}

/* Walnut & berry dark - full dark override set. The berry is LIFTED here so it still
   reads as a fill over near-black, which flips its ink back to dark (4.7:1). */
:root[data-theme="walnut-dark"] {
    --color-bg:              #151010;
    --color-surface:         #1F1817;
    --color-surface-muted:   #261E1D;
    --color-border:          #382C2B;
    --color-border-strong:   #4E3D3B;
    --color-text:            #F2E7E4;
    --color-text-muted:      #B0A09C;
    --color-text-subtle:     #9A8B87;   /* AA: 5.8:1 on --color-bg, 4.7:1 on --color-card */
    --color-text-on-accent:  #260C16;   /* 4.7:1 on the lifted berry; white would be 3.9:1 */
    --color-overlay:         rgba(0, 0, 0, .62);
    --color-scrim:           rgba(0, 0, 0, .52);

    --color-chrome:          #181212;
    --color-chrome-text:     #F6EFE9;
    --color-chrome-muted:    #AD9B90;
    --color-chrome-hover:    rgba(255, 255, 255, .07);
    --color-chrome-active:   rgba(255, 255, 255, .12);
    --color-chrome-edge:     rgba(255, 255, 255, .09);
    --color-chrome-pill:     var(--color-card);
    --color-chrome-mark:     #D27692;

    --color-primary:         #C4617F;
    --color-primary-hover:   #D27692;
    --color-primary-active:  #A84E69;
    --color-primary-soft:    #3E1F2A;
    --color-primary-softer:  #2C161E;
    --color-primary-text:    #EBA7BC;   /* 7.9:1 on --color-card */
    --color-primary-bright:  #D27692;
    --color-focus:           #D27692;

    --color-surface-hover:   #271F1E;
    --color-surface-active:  #312726;

    --color-surface-sunken:  #0D0909;
    --color-surface-raised:  #2C2322;
    --color-card:            #2C2322;
    --color-card-raised:     #372C2B;
    --color-card-border:     var(--color-border);
    --color-row-zebra:       var(--color-surface-raised);
    --glass-fill-regular:    rgba(44, 35, 34, .72);
    --glass-fill-clear:      rgba(44, 35, 34, .40);
    --color-wall:            #DAE2EC;
    --color-wall-vertex:     #2DD4BF;
    --color-boundary:        #A78BFA;

    --reg-anchor-c1:         #DC2626;
    --reg-anchor-c2:         #0284C7;
    --reg-anchor-c3:         #047857;
    --reg-anchor-c4:         #7C3AED;
    --reg-anchor-c5:         #EA580C;
    --reg-anchor-c6:         #DB2777;

    /* Danger keeps the orange-red of every other dark block - it must never be mistaken
       for the pink-magenta accent, the one real risk in a berry palette. */
    --color-danger:          #F2655A;
    --color-text-on-danger:  #2A0F0C;
    --color-danger-soft:     #3A1714;
    --color-danger-text:     #FCA59D;
    --color-success:         #2FBE82;
    --color-success-soft:    #0F2E22;
    --color-success-text:    #7BE0B4;
    --color-warning:         #ECA13C;
    --color-warning-soft:    #34250D;
    --color-warning-text:    #F5C97F;

    --shadow-0:    none;
    --shadow-1:    inset 0 1px 2px rgba(255, 255, 255, .12), 0 1px 2px rgba(0, 0, 0, .38), 0 2px 8px rgba(0, 0, 0, .30);
    --shadow-2:    inset 0 1px 2px rgba(255, 255, 255, .17), 0 2px 4px rgba(0, 0, 0, .42), 0 6px 16px rgba(0, 0, 0, .40);
    --shadow-3:    inset 0 1px 2px rgba(255, 255, 255, .24), 0 4px 12px rgba(0, 0, 0, .48), 0 16px 48px rgba(0, 0, 0, .55);
    --shadow-sm:   0 1px 2px rgba(0, 0, 0, .35), 0 1px 1px rgba(0, 0, 0, .25);
    --shadow-md:   0 2px 6px rgba(0, 0, 0, .40), 0 1px 2px rgba(0, 0, 0, .30);
    --shadow-lg:   0 12px 32px rgba(0, 0, 0, .50), 0 2px 8px rgba(0, 0, 0, .35);
    --shadow-inset: inset 0 1px 3px rgba(0, 0, 0, .35);
    --shadow-focus-ring: 0 0 0 3px rgba(196, 97, 127, .34);
    --shadow-glow-primary: 0 10px 24px -6px rgba(196, 97, 127, .45);
}

/* Prussian field - the daylight half of Prussian: a cool blue-white ground with gold as
   the ONLY chromatic signal. The one light palette that re-tints the shadows, because the
   warm-brown :root shadow set reads muddy over a cool ground (PM light does the same). */
:root[data-theme="prussian-light"] {
    --color-bg:              #EEF2F8;
    --color-surface:         #FFFFFF;
    --color-surface-muted:   #E3EAF3;
    --color-border:          #D5DEEA;
    --color-border-strong:   #B4C1D3;
    --color-text:            #101A26;
    --color-text-muted:      #53606F;
    --color-text-subtle:     #5C6A7A;   /* AA: 4.9:1 on --color-bg */
    --color-text-on-accent:  #1E1608;
    --color-overlay:         rgba(9, 15, 24, .55);
    --color-scrim:           rgba(9, 15, 24, .45);

    --color-chrome:          #0F1A28;
    --color-chrome-text:     #EDF2F9;
    --color-chrome-muted:    #96A3B6;
    --color-chrome-pill:     #16243A;
    --color-chrome-mark:     #EFBB59;

    --color-primary:         #DEA02B;
    --color-primary-hover:   #C88C1E;
    --color-primary-active:  #A67110;
    --color-primary-soft:    #FAF0D9;
    --color-primary-softer:  #FDF8EC;
    --color-primary-text:    #8A5E0F;
    --color-primary-bright:  #EFBB59;
    --color-focus:           #A67110;   /* 4.2:1 on white */

    --color-surface-hover:   #E9EEF6;
    --color-surface-active:  #DEE6F0;
    --color-surface-sunken:  #E7EDF5;

    /* Cool-tinted elevation - same geometry as :root, prussian ink instead of walnut. */
    --shadow-1:    inset 0 1px 2px rgba(255, 255, 255, 0), 0 1px 2px rgba(16, 26, 38, .09), 0 2px 8px rgba(16, 26, 38, .07);
    --shadow-2:    inset 0 1px 2px rgba(255, 255, 255, 0), 0 2px 4px rgba(16, 26, 38, .09), 0 6px 16px rgba(16, 26, 38, .11);
    --shadow-3:    inset 0 1px 2px rgba(255, 255, 255, 0), 0 4px 12px rgba(16, 26, 38, .10), 0 16px 48px rgba(16, 26, 38, .17);
    --shadow-sm:   0 1px 2px rgba(16, 26, 38, .06), 0 1px 1px rgba(16, 26, 38, .04);
    --shadow-md:   0 2px 6px rgba(16, 26, 38, .08), 0 1px 2px rgba(16, 26, 38, .06);
    --shadow-lg:   0 12px 32px rgba(16, 26, 38, .13), 0 2px 8px rgba(16, 26, 38, .07);
    --shadow-inset: inset 0 1px 3px rgba(16, 26, 38, .07);
    --shadow-focus-ring: 0 0 0 3px rgba(222, 160, 43, .32);
}

/* The PM dialect was DELETED here - three blocks keyed on the PM module attribute selector
   the PM module attribute: the light block, its System-dark twin inside the @media, and the four-selector
   explicit-dark list. (The selector is deliberately not spelled out in full anywhere in this
   file - Phase 4B's exit criterion greps for it.) CLAUDE.md rule 6, the three-dialect system,
   is REPEALED by the §2 dialect-collapse decision recorded in docs/HIG-REBUILD-PLAN.md §3.4.
   The BOM/SOW/CO/COC editors now ride the app palette on whichever of the nine themes the user
   picked - which is the point of the collapse: they were the only surface in the app that
   ignored the theme picker. Do NOT re-derive a PM block.

   The on-accent ink flipped with the deletion: PM paired its indigo accent with WHITE, and every
   app accent is a LIGHT accent taking DARK ink (--color-text-on-accent, per palette - see the
   walnut comments above for why that token follows the ACCENT's lightness, not the theme's).
   Measured after the collapse across all 8 explicit palettes plus System light and System dark,
   15 pairs each: every pair passes WCAG AA on the resting surfaces, and the collapse FIXED 10
   pre-existing PM failures (white on the PM dark indigo measured 3.78:1 filled and 3.10:1
   hovered, in every dark palette). The one pair that still runs thin is the PRESSED gradient
   bottom stop on .btn--primary:active - 3.45:1 (walnut-dark) to 4.27:1 (highnoon). It is
   pre-existing and app-wide, not created here: the gradient's mid-band, where the glyph
   cap-height actually sits, measures 4.55:1–5.23:1 and passes. Fixing it means re-pitching
   --color-primary-active for all 22 pages, which is an app-side decision, not a PM one.

   This deletion is also what moves pre-merge gate 7's expected counts: the --shadow-1 grep goes
   10 → 7, and the --color-card grep goes 9 → 6 (all three PM blocks declared --color-card too,
   so the plan's "--color-card is unaffected" note was wrong). Do not spell either token name
   with its trailing colon in this comment - the gate greps this file and would count it. */

/* ══════════════════════ EDITOR SKIN - the console palette ══════════════════════
   The map editor's own palette: SnapCam frost + amber, every corner squared.
   Consumed by editor-skin.css. `:root:root` (0,2,0), not `:root` - it must outrank
   the theme blocks above, which is what the old [data-map-look] hook did.
   ═══════════════════════════════════════════════════════════════════════════ */
:root:root {
    /* Classic's frost bars, verbatim (tokens.css --frost-canvas-*) */
    --look-chrome-bg: rgba(26, 21, 13, 0.90);
    --look-chrome-bg-2: rgba(38, 31, 20, 0.95);
    --look-sheet-bg: rgba(22, 18, 11, 0.97);
    --look-banner-bg: rgba(26, 21, 13, 0.92);
    --look-tile-bg: rgba(255, 255, 255, 0.09);
    /* Classic's amber pairing: amber fill, dark ink on it */
    --look-tile-bg-active: #D69528;
    --look-fab-bg: #D69528;
    --look-fab-image: linear-gradient(to bottom, #E9B455, #D69528);
    --look-scrim: transparent;

    --look-ink: #FFFFFF;
    --look-ink-muted: #EEEBE3;
    --look-ink-subtle: rgba(255, 255, 255, 0.35);
    --look-ink-on-accent: #201A11;
    --look-accent: #E0A63E;
    --look-accent-2: #EDB85A;
    --look-danger: #FF9C94;

    --look-hairline: rgba(255, 255, 255, 0.22);
    --look-hairline-strong: rgba(255, 255, 255, 0.55);
    --look-border-w: 1px;

    /* THE SPEC: no corners. Squares everywhere, tokens do the whole job. */
    --look-radius-sm: 0;
    --look-radius-md: 0;
    --look-radius-lg: 0;
    --look-radius-pill: 0;

    --look-shadow-sm: 0 4px 12px rgba(0, 0, 0, 0.30);
    --look-shadow-lg: 0 12px 32px rgba(0, 0, 0, 0.40);
    --look-shadow-pop: 0 18px 48px rgba(0, 0, 0, 0.50);
    --look-glow: none;

    /* Classic's type: inherit the app stack untouched */
    --look-font: inherit;
    --look-font-display: inherit;
    --look-case: none;
    --look-tracking: 0.01em;
    --look-weight-label: 600;

    /* Classic pin identity (amber ring, warm ink) on a squared plate */
    --look-pin-bg: rgba(255, 255, 255, 0.97);
    --look-pin-ink: #8A5E0F;
    --look-pin-ring: #D69528;
    --look-pin-badge-bg: #D69528;

    --look-focus: #A06E12;
}
