/*
    CueFlect web design tokens — ported from src/CueFlect.App/App.xaml.

    THIS IS THE ONLY FILE IN THE PROJECT THAT MAY CONTAIN A COLOUR LITERAL.
    DesignSystemTests.NoComponentRuleContainsALiteralColour enforces it. A component rule holding its own hex
    is how a token layer stops being retheme-able: the reference app in NatchScraper has four overlapping
    token namespaces plus ~200 loose hexes inside component rules, so changing :root there fixes about half
    the surface. One authority per fact (PRODUCT.md, principle 4).

    ── Two porting rules, and both matter ──────────────────────────────────────────────────────────────────

    1. ALPHA. WPF brushes are #AARRGGBB with ~94% alpha because the desktop overlay floats over a live
       screen. On the web there is nothing behind the page, so the alpha has to be RESOLVED, not copied:

         - Opaque surfaces flatten to plain hex. #F0232830 composited over #181B20 lands within one channel
           step of #232830, so the alpha buys nothing and costs a compositing layer.
         - Strokes and interaction overlays KEEP their alpha. These sit over four different surface levels,
           and flattening one of them to a single hex would break the layering everywhere else.

    2. CONTRAST. Three desktop tokens do not clear WCAG 2.2 AA, which M10-U11 requires and M12-U04 makes
       release-blocking. Measured against --surface-inset (#1D2229), the tightest ground they run on:

         TextMutedBrush  rgba(146,156,166,.53)  2.62:1  FAILS  ->  #929CA6 at full opacity = 5.73:1
         DangerBrush     #E5484D                4.09:1  FAILS normal text  ->  see --danger-text below
         TextSecondary   rgba(201,208,215,.78)  10.3:1  passes; flattened anyway, for rule 1

       The muted-text case is the one that matters: 11px muted captions are the desktop's most common label,
       so the failing token is also the most-used one. Dropping the alpha fixes it and changes no hue.
*/

:root {
    color-scheme: dark;

    /* ── Surfaces ──────────────────────────────────────────────────────────────────────────────────────
       The four-level ladder the desktop uses: base < inset < panel < raised. Inset is DARKER than panel
       on purpose — it is the recessed well that content sits in (answer body, text fields), while raised
       is the lifted chrome (headers, popovers, dialogs). */
    --surface-base: #181b20;            /* OverlayBackgroundBrush   — the page */
    --surface-inset: #1d2229;           /* PanelInsetBrush          — wells, inputs, table bodies */
    --surface-panel: #232830;           /* PanelBackgroundBrush     — cards, panels */
    --surface-raised: #2b3139;          /* PanelRaisedBrush         — headers, menus, dialogs, tooltips */
    --surface-sunken: #151b22;          /* dialog window background in every WPF dialog */
    --surface-footer: #111820;          /* the shared dialog footer bar */
    --surface-header-row: #202832;      /* GridView column header / ProfileConfig sub-header */

    /*
       The email-preview frame's ground, and the one token here that is NOT part of the palette.

       It is deliberately white in both schemes, because what sits on it is not part of this page: an email
       carries its own complete styling and its own dark-scheme handling, and it has to be judged against what
       a mail client will show rather than against the console's surface. Letting the console's ground show
       through would have an operator sign off on a rendering nobody ever receives.

       It lives here because colour literals belong in this file and nowhere else — the rule holds even for a
       value the palette does not own, since the alternative is one hex in a component rule and a scan that
       has to learn exceptions.
    */
    --email-preview-canvas: #ffffff;

    /* ── Strokes ───────────────────────────────────────────────────────────────────────────────────────
       Alpha retained (rule 1). Hairlines cross all four surface levels and must stay relative to them. */
    --stroke: rgba(139, 148, 159, 0.243);        /* StrokeBrush       #3E8B949F */
    --stroke-strong: rgba(116, 125, 136, 0.38);  /* StrokeStrongBrush #61747D88 */

    /* ── Text ──────────────────────────────────────────────────────────────────────────────────────────
       All three flattened to full opacity. --text-muted is the contrast correction. */
    --text-primary: #f7f8fa;            /* TextPrimaryBrush   */
    --text-secondary: #c9d0d7;          /* TextSecondaryBrush */
    --text-muted: #929ca6;              /* TextMutedBrush, de-alphaed: 2.62:1 -> 5.73:1 */

    /* ── Accent — teal. Selection, current state, primary emphasis. 10.65:1 on inset. ──────────────── */
    --accent: #72e6d4;                          /* AccentBrush     */
    --accent-soft: rgba(114, 230, 212, 0.157);  /* AccentSoftBrush #2872E6D4 */
    --accent-selected: rgba(114, 230, 212, 0.149);
    --accent-line: rgba(114, 230, 212, 0.35);
    --on-accent: #0b1418;               /* badge text on a filled accent — the desktop's own value */

    /* ── Action — gold. The single primary call to action, exactly as the desktop uses it. ───────────
       ActionText on Action is 10.87:1. The desktop deliberately reverts to a NEUTRAL hover rather than a
       brighter gold; that reads as a deliberate step-down and is preserved in CfButton. */
    --action: #e6d27b;                  /* ActionBrush      */
    --action-hover: #f2df8b;            /* ActionHoverBrush */
    --action-border: #f7e8a4;
    --action-text: #241f0a;             /* ActionTextBrush — warm near-black */

    /* ── Status ────────────────────────────────────────────────────────────────────────────────────────
       --danger-fill vs --danger-text is the second contrast correction, not a duplicate. #E5484D is
       4.09:1 on inset: fine as a fill or a 1px border, below AA as normal-size text. #EC5D63 (the
       desktop's own DangerHoverBrush) is 4.81:1 and becomes the text token. */
    --warning: #ffc96b;                 /* WarningBrush      — 10.5:1 */
    --danger-fill: #e5484d;             /* DangerBrush       — fills and borders only */
    --danger-text: #ec5d63;             /* DangerHoverBrush  — 4.81:1, safe as text */
    --danger-pressed: #c93c42;          /* DangerPressedBrush */
    --info: #74b8ff;                    /* the desktop's only blue accent, at text weight */
    --success: #7ad98a;                 /* session status dot, SessionManagementWindow */

    --warning-wash: rgba(255, 201, 107, 0.12);
    --danger-wash: rgba(229, 72, 77, 0.12);
    /* Derived from --info, like every sibling wash. It was rgba(59,130,246,.12) — a blue that appears
       nowhere else in this file — which made the one token whose whole job is single authority the only
       one disagreeing with itself. */
    --info-wash: rgba(116, 184, 255, 0.12);
    --success-wash: rgba(122, 217, 138, 0.12);
    --accent-wash: rgba(114, 230, 212, 0.1);

    /* ── Input-mode colours — carried over so the web can name the desktop's modes in its own palette. */
    --mode-audio: #ffb86b;              /* InputOrangeBrush — computer audio */
    --mode-mic: #74b8ff;                /* InputBlueBrush   — microphone */
    --mode-keyboard: #72e6d4;           /* accent           — typed */

    /* ── The spectrum ──────────────────────────────────────────────────────────────────────────────────
       Straight off the brand mark (cueflect-audio-prism.svg, and App.xaml:19–82 which duplicates it): an
       amber sound wave enters the glass, teal answer lines leave it. Those two ramps are the product's
       mechanism drawn, so they become the page's light rather than staying inside a 26px logo. */
    --spectrum-warm-hi: #ffd18e;
    --spectrum-warm: #ffb86b;
    --spectrum-warm-lo: #ff9b45;
    --spectrum-cool-hi: #8dfff5;
    --spectrum-cool: #72e6d4;
    --spectrum-cool-lo: #31c8bb;

    /* The light field. Very low alpha on purpose — this is a light source behind the page, not a
       decorative wash, and the moment it is legible as "a gradient" it has gone too far. */
    --field-warm: rgba(255, 155, 69, 0.09);
    --field-cool: rgba(49, 200, 187, 0.11);
    --field-cool-hi: rgba(141, 255, 245, 0.05);

    /* ── Glass ─────────────────────────────────────────────────────────────────────────────────────────
       Translucent surfaces so the light field reads THROUGH them. Paired with --edge-lit, which is the
       highest-value depth device on a dark UI: a 1px inset white highlight along the top edge is what
       makes a plane look lit from above instead of painted. */
    --glass: rgba(38, 44, 53, 0.62);
    --glass-strong: rgba(43, 49, 57, 0.8);
    --glass-inset: rgba(24, 28, 34, 0.55);
    --glass-blur: blur(16px) saturate(125%);
    --edge-lit: inset 0 1px 0 rgba(255, 255, 255, 0.07);
    --edge-lit-strong: inset 0 1px 0 rgba(255, 255, 255, 0.11);

    /* ── Elevation ─────────────────────────────────────────────────────────────────────────────────────
       Ambient + key + lit edge, layered. An elevated surface takes one of these and drops its hairline
       border: a 1px border under a wide soft shadow is the ghost card. Hairlines stay for flat dividers. */
    --elev-1: 0 1px 2px rgba(0, 0, 0, 0.35),
              0 4px 10px -4px rgba(0, 0, 0, 0.4),
              inset 0 1px 0 rgba(255, 255, 255, 0.06);
    --elev-2: 0 2px 4px rgba(0, 0, 0, 0.3),
              0 14px 32px -10px rgba(0, 0, 0, 0.6),
              inset 0 1px 0 rgba(255, 255, 255, 0.07);
    --elev-3: 0 4px 8px rgba(0, 0, 0, 0.35),
              0 28px 64px -18px rgba(0, 0, 0, 0.7),
              inset 0 1px 0 rgba(255, 255, 255, 0.09);

    /* ── Glow — the accent behaving as light rather than as a 1px outline. ──────────────────────────── */
    --glow-cool: 0 12px 48px -16px rgba(114, 230, 212, 0.45);
    --glow-cool-strong: 0 0 0 1px rgba(114, 230, 212, 0.22),
                        0 14px 56px -14px rgba(114, 230, 212, 0.55);
    --glow-warm: 0 12px 48px -16px rgba(255, 155, 69, 0.42);
    --glow-action: 0 10px 34px -12px rgba(230, 210, 123, 0.5);
    --glow-action-strong: 0 14px 46px -12px rgba(242, 223, 139, 0.72);

    /* The specular sweep that crosses glass on hover. */
    --sheen: linear-gradient(100deg,
                             rgba(255, 255, 255, 0) 32%,
                             rgba(255, 255, 255, 0.11) 48%,
                             rgba(255, 255, 255, 0) 64%);

    /*
        The spectrum rule — the chrome's own hairline, and the one place the two ramps meet on a single
        line. Warm where the wave enters at the left, cool where the answer lines leave at the right, and
        deliberately near-nothing across the middle so it reads as a lit edge rather than as a rainbow.

        It bookends the page: the header wears it along its bottom edge, the footer plate along its top.
        One token because the two must not drift — a gradient copied into two files is a gradient that
        gets tuned in one of them.
    */
    --rule-spectrum: linear-gradient(90deg,
                                     rgba(255, 155, 69, 0.55) 0%,
                                     rgba(255, 184, 107, 0.14) 24%,
                                     rgba(255, 255, 255, 0) 48%,
                                     rgba(114, 230, 212, 0.18) 74%,
                                     rgba(49, 200, 187, 0.6) 100%);

    /* ── The white-alpha ladder ────────────────────────────────────────────────────────────────────────
       Lifted verbatim from App.xaml's literals. This ladder IS the desktop's interaction system: every
       hover, press and row highlight is a white veil over whatever surface is underneath, which is why it
       works identically on all four levels. Alpha retained (rule 1). */
    --overlay-01: rgba(255, 255, 255, 0.063);   /* #10FFFFFF */
    --overlay-02: rgba(255, 255, 255, 0.078);   /* #14FFFFFF */
    --overlay-03: rgba(255, 255, 255, 0.086);   /* #16FFFFFF */
    --overlay-04: rgba(255, 255, 255, 0.102);   /* #1AFFFFFF */
    --overlay-05: rgba(255, 255, 255, 0.118);   /* #1EFFFFFF */
    --overlay-06: rgba(255, 255, 255, 0.141);   /* #24FFFFFF */
    --overlay-07: rgba(255, 255, 255, 0.165);   /* #2AFFFFFF */
    --overlay-08: rgba(255, 255, 255, 0.188);   /* #30FFFFFF */

    /* Semantic bindings — the desktop's ToolbarButtonStyle mapping, so a control never picks a rung. */
    --control-bg: var(--overlay-04);
    --control-bg-hover: var(--overlay-08);
    --control-bg-active: var(--overlay-02);
    --chip-bg: var(--overlay-05);
    --row-hover: var(--overlay-03);
    --row-selected: var(--accent-selected);

    /* ── Type ──────────────────────────────────────────────────────────────────────────────────────────
       Segoe UI Variable Text is Windows 11 only, so the stack degrades rather than assuming it. No
       webfont: self-hosting Segoe is a licensing problem and M10-U11's CSP will refuse a third-party one.
       Operate mode is well served by a system stack anyway. */
    --font-ui: "Segoe UI Variable Text", "Segoe UI", system-ui, -apple-system, "Helvetica Neue", Arial,
        sans-serif;
    --font-mono: "Cascadia Mono", Consolas, ui-monospace, "SF Mono", Menlo, monospace;

    /*
        The display voice — Archivo, self-hosted (SIL OFL 1.1), one variable file covering weight 400–800
        and width 100–125%.

        Chosen from the subject's world rather than from taste: this product lives among oscilloscopes,
        audio interfaces and instrument faceplates, and that world's lettering is wide, flat-terminalled
        grotesque. Set EXPANDED (--display-stretch) it reads as machined signage rather than as another
        startup sans, which is the whole reason a display face was worth 90KB.

        Display only. Every UI label, button and data value stays on --font-ui so the portal still feels
        native — a display face in a settings label is the Operate-mode failure.
    */
    --font-display: "Archivo", "Segoe UI Variable Display", "Segoe UI", system-ui, sans-serif;
    --display-stretch: 116%;

    /*
        THE UI SCALE IS WEB-NATIVE AND DELIBERATELY NOT THE DESKTOP'S.

        It used to be a literal port: App.xaml's 24 FontSize setters run 11–15px and these tokens mirrored
        them one for one. WPF FontSize is an absolute device-independent unit, and those values are tuned for
        a translucent overlay floating over a live interview — something that must never compete with the
        call underneath it. On a full page with nothing behind it the same ramp reads as unreadably small:
        card body copy landed at 12px and section labels at 11px.

        So the whole UI ramp moved up 2px, every step, which keeps each one distinct and keeps its job. This
        is the same correction DESIGN.md records for the display scale — "the timidity that made every page
        read at one size" — applied to the ramp underneath it. Do not re-port these from App.xaml.

        Fixed rem, never fluid — users view product UI at a consistent DPI.
    */
    --text-2xs: 0.8125rem;  /* 13px — CaptionTextStyle, the section-label idiom */
    --text-xs: 0.875rem;    /* 14px — body copy, chips, field labels, tooltips */
    --text-sm: 0.9375rem;   /* 15px — buttons, tabs, banner headings, titles */
    --text-base: 1rem;      /* 16px — modal titles */
    --text-md: 1.0625rem;   /* 17px — input text, long-form body, the body default */
    --text-lg: 1.1875rem;   /* 19px */
    --text-xl: 1.25rem;     /* 20px — the wordmark, page titles */
    --text-2xl: 1.625rem;   /* 26px — h1, which has to hold its ratio against a 17px body */
    --text-3xl: 2rem;
    --text-4xl: 2.75rem;

    /* Display sizes. Fluid, unlike the UI scale above — these are headlines, not controls, and the
       previous 3.75rem ceiling was the timidity that made every page read at one size. */
    /* 5.25rem, not 6: at 6 the home claim ran to six lines and the hero became a wall of type with the
       supporting copy pushed below the fold. Display scale is a ratio against the body text, and past a
       point more size buys line count rather than presence. */
    --text-display: clamp(2.375rem, 1.1rem + 4.6vw, 5.25rem);
    --text-display-sm: clamp(1.75rem, 1.15rem + 2.1vw, 2.75rem);
    /* A third step, because two was not a hierarchy: page titles and section headings were both on
       --text-display-sm, so on /pricing the h1 and the h2 below it set at exactly the same size. */
    --text-display-xs: clamp(1.5rem, 1.1rem + 1.5vw, 2.125rem);

    /* The desktop uses Bold exactly twice in 1774 lines. Two weights is the system. */
    --weight-normal: 400;
    --weight-semibold: 600;

    --leading-tight: 1.2;
    --leading-snug: 1.4;
    --leading-normal: 1.55;
    --leading-relaxed: 1.7;

    --tracking-caption: 0.04em;   /* 13px SemiBold micro-labels only */
    --tracking-display: -0.02em;

    /* ── Space — the desktop's own rhythm (2/4/6/8/12/16/18/20), extended upward for page layout. ──── */
    --space-05: 0.125rem;
    --space-1: 0.25rem;
    --space-15: 0.375rem;
    --space-2: 0.5rem;
    --space-3: 0.75rem;
    --space-4: 1rem;
    --space-5: 1.25rem;
    --space-6: 1.5rem;
    --space-8: 2rem;
    --space-10: 2.5rem;
    --space-12: 3rem;
    --space-16: 4rem;
    --space-20: 5rem;

    /* ── Shape — the desktop's four radii, unchanged. Nothing here is a pill except small controls. ── */
    --radius-xs: 4px;      /* chips, list rows */
    --radius-sm: 5px;      /* buttons, inputs, tooltips */
    --radius-md: 6px;      /* panels, banners, popovers */
    --radius-lg: 8px;      /* the shell, modal cards */
    --radius-pill: 999px;

    /* ── Elevation — one border OR one shadow, never a hairline under a wide soft shadow. Both carry a
       real offset and a soft blur, matching the desktop's four DropShadowEffect scales. ─────────────── */
    --shadow-popover: 0 5px 16px rgba(0, 0, 0, 0.45);
    --shadow-modal: 0 8px 24px rgba(0, 0, 0, 0.5);
    --shadow-shell: 0 8px 28px rgba(0, 0, 0, 0.42);

    /* The modal scrim — the desktop's #B0000000, used behind every in-window modal it draws. */
    --scrim: rgba(0, 0, 0, 0.69);

    /* ── Motion ────────────────────────────────────────────────────────────────────────────────────────
       The desktop has NO hover transitions — every state is an instant WPF trigger swap. Instant reads as
       broken on the web, so this sits deliberately below Operate mode's 150–250ms band: fast enough to
       feel like the desktop, slow enough not to flicker. Colour and border only; never layout, never
       transform on a row. */
    --motion-fast: 120ms;
    --motion-medium: 200ms;
    /* Reveals and the load sequence. Long enough to read as choreography, short enough that a returning
       visitor is never waiting on it. */
    --motion-reveal: 620ms;
    --motion-scene: 1600ms;
    --ease-out: cubic-bezier(0.16, 1, 0.3, 1);

    /* ── Focus ─────────────────────────────────────────────────────────────────────────────────────────
       The desktop's largest a11y gap: ToolbarButtonStyle, PrimaryButtonStyle and DangerDialogButtonStyle
       define no keyboard-focus state at all, and text inputs show only an accent caret. WPF falls back to
       a dotted FocusVisualStyle; the web has no such fallback and WCAG 2.4.7 is release-blocking, so this
       ring is an addition, not a port. */
    --focus-ring: 2px solid var(--accent);
    --focus-offset: 2px;

    /* ── Layout ─────────────────────────────────────────────────────────────────────────────────────── */
    --header-height: 4rem;        /* 64px — the brand lockup is the bar's largest object and a 36px mark
                                     needs the room; the desktop caption bar is 40px, itself below the
                                     web touch floor */
    --measure: 68ch;              /* prose measure */
    --page-max: 68rem;
    /* The wide shell. A token rather than a literal in .page-shell--wide, because the account menu has to
       compute the same edge to align itself under the avatar when anchor positioning is unavailable —
       two hand-written copies of one width is a menu that drifts off the bar the day someone widens it. */
    --page-max-wide: 78rem;
    --page-gutter: var(--space-6);
}

@media (max-width: 40rem) {
    :root {
        --page-gutter: var(--space-4);
    }
}

/*
    ── Syncfusion bridge (M10-U07) ──────────────────────────────────────────────────────────────────────

    The two operations consoles render Syncfusion controls, and Syncfusion ships its own complete palette.
    Left alone, a Fluent2 grid on a CueFlect page is a second design system running beside the first: its
    own greys, its own blue primary, its own borders — all internally consistent, none of them this
    product's. Re-theming it by hand, rule by rule, is the ~200-loose-hexes failure this whole file exists
    to prevent, one surface further out.

    So the theme's OWN variables are pointed at the ladder above. Every value here is a var() reference and
    not a literal, which is what makes this a mapping rather than a fourth palette: change --accent and the
    grid's selection, the dialog's focus ring and the dropdown's active row all move with it.

    `:root:root` IS DELIBERATE AND IS NOT A TYPO. The theme stylesheet is injected by ConsoleLayout through
    <HeadContent>, which renders into `head::after` — i.e. AFTER this file's <link>. Its :root block and
    this one would then have identical specificity and the later one wins, so a plain :root here would be
    overwritten by the very declarations it exists to replace and the bridge would silently do nothing.
    Repeating the selector raises specificity to (0,2,0) without changing what it matches.

    ONLY THE CORE IS MAPPED. The theme defines several hundred component-specific variables; the ones left
    alone inherit Fluent2's neutral greys, which sit close enough to this ladder to read as the same
    family. Mapping all of them would be a second full theme to maintain, and the first thing to rot.
*/
:root:root {
    /* Surfaces. Syncfusion's "content" is a panel in this vocabulary, and its alt1/alt3 are the raised
       and hover steps — so they land on the raised and header-row tokens rather than on darker ones. */
    --color-sf-content-bg-color: var(--surface-panel);
    --color-sf-content-bg-color-alt1: var(--surface-raised);
    --color-sf-content-bg-color-alt2: var(--surface-inset);
    --color-sf-content-bg-color-alt3: var(--surface-header-row);
    --color-sf-content-bg-color-alt4: var(--surface-sunken);
    --color-sf-content-bg-color-hover: var(--row-hover);
    --color-sf-content-bg-color-selected: var(--row-selected);
    --color-sf-content-bg-color-pressed: var(--control-bg-active);
    --color-sf-flyout-bg-color: var(--surface-raised);

    /* Text. The three alt steps are Syncfusion's secondary ladder and map onto this one in order. */
    --color-sf-content-text-color: var(--text-primary);
    --color-sf-content-text-color-alt1: var(--text-secondary);
    --color-sf-content-text-color-alt2: var(--text-muted);
    --color-sf-content-text-color-alt3: var(--text-muted);

    /* Strokes. */
    --color-sf-border: var(--stroke-strong);
    --color-sf-border-light: var(--stroke);
    --color-sf-border-alt: var(--stroke-strong);
    --color-sf-border-hover: var(--stroke-strong);
    --color-sf-border-focus: var(--accent);
    --color-sf-border-selected: var(--accent-line);

    /* Primary. Fluent2's #115ea3 blue is the single most out-of-place value the theme brings; the
       accent is teal here and is what every other selected state on the site already uses. */
    --color-sf-primary: var(--accent);
    --color-sf-primary-light: var(--accent-line);
    --color-sf-primary-text-color: var(--on-accent);

    /* Status. --danger-text rather than --danger-fill: the theme uses this for text as well as for fills,
       and the fill value is the one that does not clear AA at body size on these surfaces. */
    --color-sf-success: var(--success);
    --color-sf-warning: var(--warning);
    --color-sf-danger: var(--danger-text);
    --color-sf-info: var(--info);
}
