/* GENERATED by web/scripts/generate-seo-artifacts.mjs — do not edit.
 * Source: src/pages/app/theme/surface-tokens.css, src/pages/app/theme/color-tokens.css, src/pages/app/theme/typography-tokens.css, src/pages/app/theme/radius-tokens.css
 *          seo/marketing-layout.css */
/* ------------------------------------------------------------------------- */
/* source: src/pages/app/theme/surface-tokens.css */
/* #1733 — Surface tokens (split out of tokens.css, which hit the 500-line cap).
   Backgrounds + the surface-elevation scale, across all three themes. Imported by
   tokens.css; values are unchanged (a pure mechanical split — see the git
   sorted-diff losslessness check in the PR). */
[data-theme="dark"],
:root {
  /* ── Backgrounds (Linear-inspired cool dark) ── */
  --app-bg-base: #0f1011;
  --app-bg-base-mid: #131415;
  --app-bg-base-end: #0d0e0f;
  --app-bg-sidebar: rgba(15,16,17,0.94);
  --app-bg-topbar: rgba(15,16,17,0.72);
  --app-bg-surface: rgba(25,26,27,0.88);
  --app-bg-card: rgba(25,26,27,0.92);
  --app-bg-input: #191a1b;
  /* #421: opaque theme-aware modal panel surface (alpha=1 all themes; modals stack over a half-transparent backdrop — #420 bleed-through bug). */
  --app-bg-modal-panel: #191a1b;
  --app-bg-elevated: rgba(255,255,255,0.03);
  --app-bg-elevated-hover: rgba(255,255,255,0.05);
  /* #1732 — surface elevation scale (was undefined → rgba-fallback dead-token in Workflow* CSS/TSX). Values = prior fallbacks (byte-exact, zero visual change), now theme-driven. `-active` 0.06 disambiguates the overload (the token was used at both 0.04 hover and 0.06 selected). */
  --app-surface-elevated: rgba(255,255,255,0.04);
  --app-surface-elevated-active: rgba(255,255,255,0.06);
  --app-surface-elevated-strong: rgba(255,255,255,0.1);
  --app-bg-glass-start: rgba(255,255,255,0.05);
  --app-bg-glass-end: rgba(255,255,255,0.02);
  --app-bg-hover-start: rgba(255,255,255,0.05);
  --app-bg-hover-end: rgba(255,255,255,0.02);
}

[data-theme="light"] {
  /* ── Backgrounds ── */
  --app-bg-base: #f5f5fa;
  --app-bg-base-mid: #ededf4;
  --app-bg-base-end: #f0f0f8;
  --app-bg-sidebar: rgba(255,255,255,0.94);
  --app-bg-topbar: rgba(255,255,255,0.82);
  --app-bg-surface: rgba(255,255,255,0.92);
  --app-bg-card: rgba(255,255,255,0.96);
  --app-bg-input: #fff;
  --app-bg-modal-panel: #ffffff;
  --app-bg-elevated: rgba(0,0,0,0.02);
  --app-bg-elevated-hover: rgba(0,0,0,0.04);
  /* #1734 — theme-aware surface elevation: the dark scale is WHITE overlays
     (invisible on a light background), so light overrides with BLACK overlays
     (same magnitude as dark 0.04/0.06/0.10; mirrors `--app-bg-elevated`). Without
     this, hover/selected fills (workflow scope buttons, list rows) read as
     invisible in light (Gemini advisory). DELIBERATE visual change. */
  --app-surface-elevated: rgba(0,0,0,0.04);
  --app-surface-elevated-active: rgba(0,0,0,0.06);
  --app-surface-elevated-strong: rgba(0,0,0,0.1);
  --app-bg-glass-start: rgba(255,255,255,0.8);
  --app-bg-glass-end: rgba(255,255,255,0.5);
  --app-bg-hover-start: rgba(0,0,0,0.03);
  --app-bg-hover-end: rgba(0,0,0,0.01);
}

/* ------------------------------------------------------------------------- */
/* source: src/pages/app/theme/color-tokens.css */
/* #1733 — Colour tokens (split out of tokens.css, which hit the 500-line cap).
   Text / borders / brand / semantic (teal/success/info/flow/danger/warning/purple)
   / message-bubble / selection / scrollbar, across all three themes. Imported by
   tokens.css; values are unchanged (pure mechanical split — git sorted-diff check). */
[data-theme="dark"],
:root {
  /* ── Text (Linear-inspired cool whites) ── */
  --app-text-primary: #f7f8f8;
  --app-text-heading: #f7f8f8;
  --app-text-body: #d0d6e0;
  --app-text-secondary: #8a8f98;
  --app-text-tertiary: #62666d;
  --app-text-muted: rgba(255, 255, 255, 0.32);
  --app-text-faint: rgba(255, 255, 255, 0.18);
  --app-text-placeholder: rgba(255, 255, 255, 0.28);
  --app-link: #89b4fa;

  /* ── Borders ── */
  --app-border-default: rgba(255, 255, 255, 0.08);
  --app-border-subtle: rgba(255, 255, 255, 0.05);
  --app-border-hover: rgba(255, 255, 255, 0.12);
  --app-border-strong: #2a2e34;
  --app-border-focus: rgba(255, 255, 255, 0.2);

  /* ── Brand (Gold — refined, aligned with OPC) ── */
  --app-brand: #f0c56a;
  --app-brand-hover: #d2a84c;
  --app-brand-bg: rgba(210, 168, 76, 0.1);
  --app-brand-bg-strong: rgba(210, 168, 76, 0.16);
  --app-brand-border: rgba(210, 168, 76, 0.22);
  --app-brand-border-hover: rgba(210, 168, 76, 0.34);
  --app-brand-glow: rgba(210, 168, 76, 0.28);
  --app-brand-shadow: rgba(210, 168, 76, 0.18);

  /* ── Teal (secondary accent, from OPC) ── */
  --app-teal: #78c1b0;
  --app-teal-bg: rgba(120, 193, 176, 0.1);
  --app-teal-border: rgba(120, 193, 176, 0.2);

  /* ── Success (Green) ── */
  --app-success: #00e696;
  --app-success-bg: rgba(0, 230, 150, 0.1);
  --app-success-border: rgba(0, 230, 150, 0.25);
  --app-success-glow: rgba(0, 230, 150, 0.4);

  /* ── Info (Teal-Cyan, warmer than before) ── */
  --app-info: #78c1b0;
  --app-info-bg: rgba(120, 193, 176, 0.1);
  --app-info-bg-subtle: rgba(120, 193, 176, 0.06);
  --app-info-border: rgba(120, 193, 176, 0.25);
  --app-info-border-subtle: rgba(120, 193, 176, 0.16);

  /* ── Flow / W6 state badge — Material Blue 300, dark-mode. ── */
  --app-flow-state-text: #64b5f6;
  --app-flow-state-bg: rgba(100, 181, 246, 0.18);

  /* ── Danger (Red) ── */
  --app-danger: #ff4444;
  --app-danger-text: #ff6666;
  --app-danger-bg: rgba(255, 68, 68, 0.1);
  --app-danger-border: rgba(255, 68, 68, 0.25);

  /* ── Warning (Gold — same as brand for dark) ── */
  --app-warning: #f0c56a;
  --app-warning-bg: rgba(210, 168, 76, 0.1);
  --app-warning-border: rgba(210, 168, 76, 0.22);

  /* ── Purple ── */
  --app-purple: #a078ff;
  --app-purple-bg: rgba(160, 120, 255, 0.08);
  --app-purple-border: rgba(160, 120, 255, 0.16);
  --app-purple-glow: rgba(160, 120, 255, 0.06);

  /* ── Selection + scrollbar ── */
  --app-selection-bg: rgba(210, 168, 76, 0.25);
  --app-selection-color: #fff;
  --app-selected-row-bg: var(--ide-accent-color);
  --app-selected-row-border: var(--ide-accent-color);
  --app-selected-row-title-color: #fff;
  --app-selected-row-secondary-color: rgba(255, 255, 255, 0.86);
  --app-selected-row-faint-color: rgba(255, 255, 255, 0.68);
  --app-scrollbar-thumb: rgba(255, 255, 255, 0.08);
  --app-scrollbar-thumb-hover: rgba(255, 255, 255, 0.14);

  /* ── Message bubble tokens (task #300 — WCAG AA verified) ── */
  --app-message-text: #e8edf2;
  --app-message-meta-text: #9aa3ad;
  --app-message-selected-text: #fff6dc;
  /* Human-specified bubble colours. Every STATE VARIANT is now DERIVED from its own base with
     color-mix, so the family cannot drift again: the previous variants were literals from the OLD
     palette (gold for the blue bubble, teal for the green one), which is why expanding a message
     in tree mode turned the human bubble gold again (it hits [data-selected="true"]).
     Dark base is dark, so hover/active/selected LIGHTEN it; borders take the base toward white. */
  --app-message-other-bg: #293c40;   /* Human-specified: agent bubble = GREEN (dark) */
  --app-message-other-bg-hover: color-mix(in srgb, var(--app-message-other-bg) 90%, white);
  --app-message-other-bg-active: color-mix(in srgb, var(--app-message-other-bg) 82%, white);
  --app-message-other-bg-selected: color-mix(in srgb, var(--app-message-other-bg) 86%, white);
  --app-message-other-border: color-mix(in srgb, var(--app-message-other-bg) 72%, white);
  --app-message-other-border-selected: color-mix(in srgb, var(--app-message-other-bg) 46%, white);
  /* Human: "消息背景的蓝色，调得浅一些" — the DARK human bubble, a step lighter.
     The blue moves from its old value to #3160b4: message text (e8edf2) still measures 5.15:1
     (was 8.21), and it separates from the page (101018) better: 3.12:1 (was 1.96). The
     hover/active/selected variants are color-mix() derived from THIS token, so they follow
     automatically — nothing else to edit.
     NOTE: no comment LINE here may begin with a hash — the marketing CSS generator reads such a
     line as a markdown heading and the repo's own gate fails on marker lines (it did, 48/1). */
  /* Human read the previous value as "foreground / button" rather than a message surface, and the
     PM quantified why: its saturation was the outlier in the family —
         dark human   H220 S84% L64%   <- this one
         dark agent   H190 S22% L21%
         light human  H210 S100% L83%  (high S, but high L => still reads as a surface)
         light agent  H120 S71%  L96%
     The dark human bubble now sits with its siblings on saturation: H220 S32% L62%, which still
     carries the text at 5.80:1 against #0f1730. */
  --app-message-own-bg: rgba(240, 197, 106, 0.105);
  --app-message-own-bg-hover: rgba(240, 197, 106, 0.155);
  --app-message-own-bg-active: rgba(240, 197, 106, 0.205);
  --app-message-own-bg-selected: rgba(240, 197, 106, 0.24);
  --app-message-own-border: rgba(240, 197, 106, 0.3);
  --app-message-own-border-selected: rgba(240, 197, 106, 0.6);
  /* Offline status dot. The rule in sidebar.css used a hardcoded rgba(255,255,255,0.2), which is
     a literal — outside the audit's scope (it scans tokens) — and on the DARK sidebar it measures
     only 1.84:1. Kept here unchanged so this commit causes zero dark change. */
  /* Shared with light on purpose (Human's request): 3.72/3.36 here, 4.68/5.08 there. */
  --app-status-offline: rgba(255, 255, 255, 0.2);
  --app-status-online: #00E696;
  --app-status-away: #f0c56a;
  --app-status-never-connected: rgba(255, 255, 255, 0.2);
  --app-status-info: rgba(255, 255, 255, 0.15);
  --app-status-success: #27a644;
}

[data-theme="light"] {
  /* ── Text ── */
  --app-text-primary: #1a1a2e;
  --app-text-heading: #111128;
  --app-text-body: #2d2d44;
  --app-text-secondary: rgba(0, 0, 0, 0.55);
  --app-text-tertiary: rgba(0, 0, 0, 0.4);
  --app-text-muted: rgba(0, 0, 0, 0.3);
  --app-text-faint: rgba(0, 0, 0, 0.18);
  --app-text-placeholder: rgba(0, 0, 0, 0.28);
  --app-link: #2563eb;

  /* ── Borders ── */
  --app-border-default: rgba(0, 0, 0, 0.08);
  --app-border-subtle: rgba(0, 0, 0, 0.04);
  --app-border-hover: rgba(0, 0, 0, 0.15);
  --app-border-strong: #8282a4;   /* PR-L: 1.41:1 -> 3.70:1 panel / 3.40:1 canvas; 29 real 1px boundaries, not decorative */
  /* Follow-up item 17: 1.61:1 / 1.60:1 -> #2563eb = 5.17:1 / 4.76:1.
     One value raises all 9 consumption sites (3 as `outline: 2px solid`, 6 as `border-color`,
     plus one var() fallback). Dark keeps rgba(255,255,255,0.2), which measures 1.89:1 and is
     recorded as a pre-existing gap pending Human authorisation — that is why this is
     light-scoped. */
  --app-border-focus: #2563eb;

  /* ── Brand (Gold — darker for readability on light) ── */
  --app-brand: #806600;           /* PR-L: 2.50:1 -> 5.50:1 panel / 5.06:1 canvas (PM checklist item 2) */
  --app-brand-hover: #a88700;    /* PR-L: 2.86:1 -> 3.43:1 panel / 3.15:1 canvas (>=3:1 required);
                                   the hover step from --app-brand #806600 narrows 1.92 -> 1.61:1
                                   but stays well above the faint band (~1.05:1) */
  --app-brand-bg: rgba(197, 160, 0, 0.08);
  --app-brand-bg-strong: rgba(197, 160, 0, 0.14);
  --app-brand-border: rgba(197, 160, 0, 0.25);
  --app-brand-border-hover: rgba(197, 160, 0, 0.4);
  --app-brand-glow: rgba(197, 160, 0, 0.2);
  --app-brand-shadow: rgba(197, 160, 0, 0.12);

  /* ── Success (Green) ── */
  --app-success: #00a86b;
  --app-success-bg: rgba(0, 168, 107, 0.08);
  --app-success-border: rgba(0, 168, 107, 0.2);
  --app-success-glow: rgba(0, 168, 107, 0.25);

  /* ── Info (Blue) ── */
  --app-info: #0090cc;
  --app-info-bg: rgba(0, 144, 204, 0.08);
  --app-info-bg-subtle: rgba(0, 144, 204, 0.05);
  --app-info-border: rgba(0, 144, 204, 0.2);
  --app-info-border-subtle: rgba(0, 144, 204, 0.12);

  /* ── Flow / W6 state badge — Material Blue 700, light-mode mirror. ── */
  --app-flow-state-text: #1976d2;
  --app-flow-state-bg: rgba(25, 118, 210, 0.12);

  /* ── Danger (Red) ── */
  --app-danger: #d43030;
  --app-danger-text: #c52a2a;
  --app-danger-bg: rgba(212, 48, 48, 0.06);
  --app-danger-border: rgba(212, 48, 48, 0.18);

  /* ── Warning ── */
  --app-warning: #8a6d00;         /* PR-L: 2.86:1 -> 4.92:1 panel / 4.53:1 canvas (PM checklist item 2) */
  --app-warning-bg: rgba(184, 149, 0, 0.07);
  --app-warning-border: rgba(184, 149, 0, 0.18);

  /* ── Purple ── */
  --app-purple: #7c5cbf;
  --app-purple-bg: rgba(124, 92, 191, 0.07);
  --app-purple-border: rgba(124, 92, 191, 0.16);
  --app-purple-glow: rgba(124, 92, 191, 0.06);

  /* ── Selection + scrollbar ── */
  --app-selection-bg: rgba(197, 160, 0, 0.2);
  --app-selection-color: #000;
  --app-scrollbar-thumb: rgba(0, 0, 0, 0.12);
  --app-scrollbar-thumb-hover: rgba(0, 0, 0, 0.22);

  /* ── Message bubble tokens (task #300 — WCAG AA verified) ── */
  --app-message-text: #202436;
  --app-message-meta-text: #5d6472;
  --app-message-selected-text: #1b1b22;
  /* Same derivation, light side: the base is light, so hover/active/selected DARKEN it (mixed
     toward black) and borders take the base further toward black for a visible edge. */
  --app-message-other-bg: #edfced;   /* Human-specified: agent bubble = GREEN (light) */
  --app-message-other-bg-hover: color-mix(in srgb, var(--app-message-other-bg) 94%, black);
  --app-message-other-bg-active: color-mix(in srgb, var(--app-message-other-bg) 88%, black);
  --app-message-other-bg-selected: color-mix(in srgb, var(--app-message-other-bg) 91%, black);
  --app-message-other-border: color-mix(in srgb, var(--app-message-other-bg) 62%, black);
  --app-message-other-border-selected: color-mix(in srgb, var(--app-message-other-bg) 40%, black);
  --app-message-own-bg: #a6d2ff;     /* Human-specified: human bubble = BLUE (light) */
  --app-message-own-bg-hover: color-mix(in srgb, var(--app-message-own-bg) 94%, black);
  --app-message-own-bg-active: color-mix(in srgb, var(--app-message-own-bg) 88%, black);
  --app-message-own-bg-selected: color-mix(in srgb, var(--app-message-own-bg) 91%, black);
  --app-message-own-border: color-mix(in srgb, var(--app-message-own-bg) 62%, black);
  --app-message-own-border-selected: color-mix(in srgb, var(--app-message-own-bg) 40%, black);
  /* Helper chat box + its shell (Human report), same scan as above. */
  --company-info-inner: linear-gradient(135deg, #ffffff 0%, #f0f0f8 100%);
  --company-info-outer: #f0f0f8;
  --company-info-hover: #e8e8f0;
  --ide-button-bg-color: rgba(0, 0, 0, 0.04);
  --ide-warning-text-color: #8a6d00;
  /* Offline status dot, LIGHT: the same literal composited on the light sidebar (#f5f5fa) gives
     rgb(247,247,251) = 1.02:1 — invisible, which is what the Human reported. #8282a4 (the same
     signed grey used for --app-border-strong) measures 3.40:1 on the light sidebar.
     NOTE: on DARK this literal measures only 1.84:1, so the dark dot is also below 3:1 — that
     needs a value change in the dark block and therefore Human authorisation; not done here. */
  /* Human instruction, verbatim: "浅色背景下，干脆把离线的圆点颜色显示成黑色得了" — in light mode
     make the offline dot black. Applied to the three grey dots, which are one family (he said
     "offline"; the PM scoped it to the family, and these are the three he reported twice).
     Pure black on the light surfaces measures ~19:1, i.e. far past anything the eye can miss.
     (If it should instead match the app's own text colour it would be #1a1a2e — one line.)
     Dark is untouched. */
  /* Human, second pass: "这个改变看着不是特别明显，都再浅一些" — one more step lighter.
     The ladder so far: #8282a4 (3.40, he said invisible) -> #52526b (6.96) -> #000000 (19.32,
     his own words) -> #2a2a3c (12.93, "not very noticeable") -> and now back to #52526b (6.96).
     Note it has come full circle: the value the derivation produced is where his eye settled,
     one step at a time. 6.96:1 keeps it clear of the small-shape margin line (~4.5). */
  /* Human: "或者调成跟深色主题同样的颜色，因为之前已经确认了，不是因为那个颜色的问题，而是因为没显示"
     — ONE value for both themes. It cannot be dark's rgba(255,255,255,0.2) (invisible on light), so
     the shared value is a mid grey measured against BOTH grounds:
       value 76769a  light 4.00 (canvas) / 4.35 (panel)   dark 4.35 (page) / 3.92 (panel)
     i.e. clear of 3 everywhere and near the ~4.5 that a small dot wants. */
  --app-status-offline: #76769a;
  /* Same class as the offline dot: the online/away dots were hardcoded literals too, and on the
     light sidebar all three measure below 3:1 (offline 1.02 / online 1.51 / away 1.50).
       online #00E696 -> #007a54   5.37:1 on the light sidebar (the same green derived for --online)
       away   #f0c56a -> #8a6d00   4.92:1 (the signed amber used for --warning)
     Dark keeps its own values below (online 11.5:1, away 11.63:1 - both pass, so keeping them is
     not merely a choice, it is correct). */
  --app-status-online: #007a54;
  --app-status-away: #8a6d00;
  /* Same class as the three status dots, found by the Human re-checking: these two elements had
     LITERALS while their own siblings used tokens — `.app-machine-status.connected` is
     var(--app-success) and `.disconnected` is var(--app-danger), but `.never_connected` was
     rgba(255,255,255,0.2); likewise `.app-activity-dot.info`. White at those alphas is invisible
     on the light panel. A full sweep of the app CSS for white backgrounds with alpha >= 0.15
     returned exactly these two (the other ~70 matches are var(--token, fallback) forms, where the
     white is only a fallback, or tints at 0.02-0.12 that are not invisible). */
  --app-status-never-connected: #000000;
  --app-status-info: #000000;
  /* Fifth spot, found by the sibling test: `.app-activity-dot.success` was the last literal in its
     family (`.info` already used a token), and it measures 2.92:1 on the light sidebar — 0.08
     short of 3. Light takes the green already justified for --app-status-online (5.37 on the
     panel / 4.94 on the sidebar); dark keeps #27a644, which measures 5.38/5.97 and therefore
     PASSES — so for this one, keeping it really is correct (per value, not by a blanket phrase).
     Its sibling `.error` #FF4444 measures 3.41/3.14 and PASSES: it is a theme-blind literal but
     NOT a visible defect, so it is recorded and deliberately NOT changed. */
  --app-status-success: #007a54;
  /* PR-L: minimum alpha for 4.5:1 on the light selected background (#2563eb) is 0.901,
     so 0.92 is the lowest passing value; the two tiers collapse at this background. */
  --app-selected-row-secondary-color: rgba(255, 255, 255, 0.92);
  --app-selected-row-faint-color: rgba(255, 255, 255, 0.92);
  /* Follow-up: the structural surfaces. Both are consumed via `background` on the app
     surface (App.css:40/55/294/912/1140/1313 + mobile.css:65) and were dark-only, so light
     mode painted a near-black sidebar/content ground (#101018 / #16161e). Values reuse the
     already-signed light surface family: canvas #f5f5fa, panel #ffffff. The ratio column is
     deliberately left blank for surfaces — a surface has no adjacent ground to be measured
     against; the criterion is that a light value exists at all. */

  /* Follow-up, boundary: .app-sidebar already had `border-right: 1px solid
     var(--app-border-default)`, but in light that composites to rgb(225,225,230) = 1.2:1
     against the sidebar / 1.3:1 against the content, so the boundary was invisible. Raised
     to --app-border-strong (#8282a4, signed in PR-L commit 11 for exactly this class of 1px
     component boundary) = 3.4:1 / 3.7:1. Light-scoped: dark keeps its own value. */
  --app-sidebar-divider: var(--app-border-strong);
}


/* Follow-up, structural surfaces — SPECIFICITY NOTE (found by render smoke, not by the build).
   --sidebar-bg / --content-bg are also declared at `:root` in App.css:12/16. An attribute
   selector `[data-theme="light"]` and `:root` have EQUAL specificity (0,1,0), so the later
   stylesheet won and the light values never applied — the build was green and the audit was
   unaffected, only the rendered page showed it. `html[data-theme="light"]` is (0,1,1) and wins.
   Values reuse the already-signed light surface family. Dark keeps App.css's values. */
html[data-theme="light"] {
  --sidebar-bg: #f5f5fa;
  --content-bg: #ffffff;
  /* Follow-up items 1-3 (sample batch). All three reuse SIGNED light values, no new colours.
     These tokens are also declared at `:root` in App.css, so they need the same specificity
     treatment as the surfaces above — hence this block rather than [data-theme="light"].
     Measured (composited value vs ITS OWN ground):
       --text-secondary rgba(0,0,0,0.55) -> panel rgb(115,115,115) 4.74:1 / canvas rgb(110,110,112) 4.68:1
                          minimum passing alpha 0.55, adjacent failing 0.54
       --text-primary   #1a1a2e (= --app-text-primary light)  17.06:1 / 15.70:1
       --accent         #806600 (= --app-brand light)          5.50:1 /  5.06:1
     Note: an earlier reading of 4.36:1 for --text-secondary on the canvas was my own arithmetic
     artifact (comparing the composite against the raw canvas rather than against the composite
     over the canvas); the signed value passes as-is and did not need fixing. */
  --text-secondary: rgba(0, 0, 0, 0.55);
  --text-primary: #1a1a2e;
  --accent: #806600;
  /* Follow-up batch 1 (items 4-7). All signed values, no new colours. Measured as the
     composited value against ITS OWN ground (panel #ffffff / canvas #f5f5fa):
       --danger          #c52a2a               5.62:1 / 5.17:1   (= --app-danger-text light)
                         criterion: used for BOTH text (9) and non-text (14) => judged by the
                         stricter, text >=4.5
       --sidebar-active  #2563eb               5.17:1 / 4.76:1   (accent family; role = color:
                         on sidebar nav, so text >=4.5; the same colour elsewhere is a fill)
       --sidebar-text    rgba(0,0,0,0.55)      rgb(115,115,115) 4.74:1 / rgb(110,110,112) 4.68:1
       --sidebar-section rgba(0,0,0,0.60)      rgb(102,102,102) 5.74:1 / rgb(98,98,100) 5.60:1
                         minimum passing alpha 0.55, adjacent failing 0.54; 0.60 gives margin
                         while staying distinct from --sidebar-text (0.55). */
  --danger: #c52a2a;
  --sidebar-active: #2563eb;
  --sidebar-text: rgba(0, 0, 0, 0.55);
  --sidebar-section: rgba(0, 0, 0, 0.60);
  /* Follow-up batch 2 (items 8-10).
       --text-dim  rgba(0,0,0,0.55)  rgb(115,115,115) 4.74:1 / rgb(110,110,112) 4.68:1
                   minimum passing alpha 0.55, adjacent failing 0.54 — "dim" wants the least
                   emphasis that still passes, so the minimum is the right choice.
       --online    #007a54  5.37:1 / 4.94:1
                   consumed BOTH as a background (App.css:1418 status dot, >=3) and as text
                   (PairApprovalPage.css:85, >=4.5) => the stricter applies. The signed
                   --app-success light #00a86b measures only 3.08:1 / 2.84:1 and FAILS, so the
                   hue is kept and the lightness dropped (same treatment as --app-brand /
                   --app-border-strong). #00815a (4.90/4.51) is the minimum passing; #007a54
                   chosen for margin on the canvas.
       --app-badge-unread-color  #ffffff  5.50:1 on the light brand ground #806600
                   consumed at reply-tree-controls.css:93 as a badge's text colour over the
                   brand background. Dark uses #000 on a bright badge; light inverts to #fff on
                   the darker gold — black there measures 3.82:1 and would fail. */
  --text-dim: rgba(0, 0, 0, 0.55);
  --online: #007a54;
  --app-badge-unread-color: #ffffff;
  /* Human reports: helper chat box + company screens still dark in light. Dark-only tokens,
     found by scanning every `background` token with no light value then alias-resolving (15 of
     39 were aliases that already resolve to tokens WITH light values - not defects). These eight
     MUST live here (0,1,1): [data-theme="light"] ties with :root (0,1,0) and loses to source
     order - the render check caught 8 of 13 having no effect until they moved here. */
  --surface: #ffffff;
  --entry-bg: #f5f5fa;
  --thread-bg: #f5f5fa;
  --tag-bg: #f0f0f8;
  --code-bg: #f0f0f8;
  --border: #8282a4;
  --cyan: #00707f;
  --accent-hover: #9a7b00;
}
/* ------------------------------------------------------------------------- */
/* source: src/pages/app/theme/typography-tokens.css */
/* #1733 — Typography tokens (split out of tokens.css). The font stacks are
   theme-independent (defined once on `:root`/dark; light does not redefine
   them in the original). Imported by tokens.css; values unchanged. */
[data-theme="dark"],
:root {
  /* ── Font stacks ── */
  --app-font-sans: "Inter Variable", "Inter", "Noto Sans SC", "PingFang SC", "Microsoft YaHei", -apple-system, BlinkMacSystemFont, sans-serif;
  --app-font-mono: "JetBrains Mono", "Fira Code", monospace;
}
/* ------------------------------------------------------------------------- */
/* source: src/pages/app/theme/radius-tokens.css */
/* #1733 — Radius / effects tokens (split out of tokens.css): border radii, blur,
   shadows, and the emoji icon scale, across all three themes (light only redefines
   shadows; dark also sets radii/blur). Imported by tokens.css; values
   unchanged (pure mechanical split — git sorted-diff check). */
[data-theme="dark"],
:root {
  /* ── Shadows (Linear-inspired subtle depth) ── */
  --app-shadow-sm: rgba(0,0,0,0.03) 0px 1.2px 0px 0px;
  --app-shadow-md: rgba(0,0,0,0.2) 0px 0px 0px 1px;
  --app-shadow-lg: rgba(0,0,0,0.4) 0px 2px 4px;
  --app-shadow-card: rgba(0,0,0,0.2) 0px 0px 0px 1px;
  --app-shadow-card-hover: rgba(0,0,0,0.4) 0px 2px 4px;
  --app-shadow-inset-glow: rgba(0,0,0,0.2) 0px 0px 12px 0px inset;

  /* ── Radii + blur (Linear-inspired tighter radii) ── */
  --app-radius-sm: 6px;
  --app-radius-md: 8px;
  --app-radius-lg: 12px;
  --app-radius-xl: 12px;
  --app-radius-2xl: 16px;
  /* Scoped to .app-message-avatar only (channel/reply tree/monologue/grouped list); sidebar/member/status/logo icons keep their existing border-radius. */
  --app-radius-avatar: var(--app-radius-sm);
  --app-blur-sm: 8px;
  --app-blur-md: 14px;
  --app-blur-lg: 24px;
  --app-emoji-icon-scale: 0.9;
}

[data-theme="light"] {
  /* ── Shadows ── */
  --app-shadow-sm: 0 1px 3px rgba(0,0,0,0.06);
  --app-shadow-md: 0 4px 12px rgba(0,0,0,0.08);
  --app-shadow-lg: 0 12px 36px rgba(0,0,0,0.1);
  --app-shadow-card: 0 2px 8px rgba(0,0,0,0.06);
  --app-shadow-card-hover: 0 8px 24px rgba(0,0,0,0.1);
  --app-shadow-inset-glow: 0 1px 0 rgba(255,255,255,0.5) inset;
}

/* ------------------------------------------------------------------------- */
/* source: seo/marketing-layout.css */
/* Task #913 — layout layer for the PUBLIC marketing pages.
 *
 * Design-system gate: this file must NOT declare colours, radii, spacing or
 * typography VALUES. Every visual value comes from the app's own token layer,
 * which `web/scripts/generate-seo-artifacts.mjs` prepends when it builds
 * `web/public/marketing/marketing.css` from:
 *   src/pages/app/theme/{surface-tokens,color-tokens,typography-tokens,radius-tokens}.css
 * Gate (enforced by scripts/seo-static-check.mjs): no hex colour literal and no
 * `--app-*` / `--mkt-*` token DEFINITION may appear below. */

:root {
  --mkt-measure: 68ch;
  --mkt-gutter: 24px;
  --mkt-max: 1080px;
}

* { box-sizing: border-box; }

body.mkt {
  margin: 0;
  background: var(--app-bg-base);
  color: var(--app-text-body);
  font-family: Inter, Manrope, system-ui, -apple-system, "Segoe UI", sans-serif;
  font-size: 16px;
  line-height: 1.65;
}

.mkt-skip { position: absolute; left: -9999px; }
.mkt-skip:focus {
  left: var(--mkt-gutter);
  top: 8px;
  padding: 8px 12px;
  background: var(--app-bg-modal-panel);
  border-radius: var(--app-radius-sm);
  z-index: 10;
}

.mkt-header {
  border-bottom: 1px solid var(--app-border-default);
  background: var(--app-bg-topbar);
}
.mkt-header__inner,
.mkt-main,
.mkt-footer__inner {
  max-width: var(--mkt-max);
  margin: 0 auto;
  padding: 0 var(--mkt-gutter);
}
.mkt-header__inner {
  display: flex;
  flex-wrap: wrap;
  gap: 12px 20px;
  align-items: center;
  justify-content: space-between;
  min-height: 64px;
}
.mkt-brand {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  color: var(--app-text-primary);
  font-weight: 700;
  text-decoration: none;
}
.mkt-brand img { display: block; }

.mkt-nav ul {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 18px;
  margin: 0;
  padding: 0;
  list-style: none;
}
.mkt-nav a { color: var(--app-text-secondary); font-size: 14px; text-decoration: none; }
.mkt-nav a:hover,
.mkt-nav a[aria-current="page"] { color: var(--app-text-primary); }

.mkt-cta {
  display: inline-block;
  padding: 9px 16px;
  border: 1px solid var(--app-border-strong);
  border-radius: var(--app-radius-sm);
  background: var(--app-surface-elevated);
  color: var(--app-text-primary);
  font-size: 14px;
  font-weight: 600;
  text-decoration: none;
}

.mkt-main { padding-top: 48px; padding-bottom: 64px; }
.mkt-hero { max-width: var(--mkt-measure); }
.mkt-eyebrow {
  margin: 0 0 8px;
  color: var(--app-text-tertiary);
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
.mkt-main h1 { margin: 0 0 16px; color: var(--app-text-primary); font-size: clamp(30px, 4.4vw, 46px); line-height: 1.15; }
.mkt-main h2 { margin: 40px 0 12px; color: var(--app-text-primary); font-size: 24px; line-height: 1.3; }
.mkt-main h3 { margin: 24px 0 8px; color: var(--app-text-primary); font-size: 17px; }
.mkt-main p,
.mkt-main li { max-width: var(--mkt-measure); }
.mkt-lede { font-size: 18px; color: var(--app-text-secondary); }

.mkt-figure { margin: 32px 0 0; max-width: var(--mkt-max); }
.mkt-figure img {
  width: 100%;
  height: auto;
  border: 1px solid var(--app-border-default);
  border-radius: var(--app-radius-lg);
  background: var(--app-bg-input);
}
.mkt-figure figcaption { margin-top: 8px; color: var(--app-text-muted); font-size: 13px; }

.mkt-cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 16px;
  margin: 24px 0 0;
  padding: 0;
  list-style: none;
}
.mkt-card {
  padding: 20px;
  border: 1px solid var(--app-border-default);
  border-radius: var(--app-radius-lg);
  background: var(--app-bg-elevated);
}
.mkt-card h3 { margin-top: 0; }
.mkt-card p { margin-bottom: 0; }

.mkt-steps { counter-reset: mkt-step; padding-left: 0; list-style: none; }
.mkt-steps > li { position: relative; margin: 0 0 20px; padding-left: 44px; }
.mkt-steps > li::before {
  counter-increment: mkt-step;
  content: counter(mkt-step);
  position: absolute;
  left: 0;
  top: 2px;
  width: 28px;
  height: 28px;
  border: 1px solid var(--app-border-strong);
  border-radius: 999px;
  color: var(--app-text-primary);
  font-size: 13px;
  font-weight: 700;
  text-align: center;
  line-height: 26px;
}

.mkt-next {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 12px;
  margin: 48px 0 0;
  padding: 20px 0 0;
  border-top: 1px solid var(--app-border-default);
  list-style: none;
}
.mkt-next a { color: var(--app-text-primary); }

.mkt-footer { border-top: 1px solid var(--app-border-default); color: var(--app-text-muted); font-size: 13px; }
.mkt-footer__inner { padding-top: 24px; padding-bottom: 32px; }
.mkt-footer a { color: var(--app-text-tertiary); }
