/* ============================================================
   tokens.css · SINGLE SOURCE for every Teglo wireframe
   ------------------------------------------------------------
   FAITHFUL reconstruction of the canonical DesignSystem token files:
     tokens.base.css        (Layer 1 · theme-agnostic primitives)
     tokens.teglo.dark.css  (Layer 2 · dark)
     tokens.teglo.light.css (Layer 2 · light)
   Upstream source of truth: ORA/.../DesignSystem/tokens/ (Brand Authoring /
   Image-Derived Design Tokens write there). This file is the wireframes'
   synced copy. Values match the canonical files verbatim; do not edit here,
   edit upstream and re-sync.

   LINK in every page <head> BEFORE the page's component <style>:
     <link rel="stylesheet" href="tokens.css">
   Pages define ONLY component CSS. Never inline a :root/[data-theme] token block.

   THEME:    <html data-theme="dark">  default  ·  <html data-theme="light">
   VERTICAL: <html data-vertical="teglo">  default
             override = --color-accent + --accent-rgb only (per verticals.demo.json
             / branding_token_locks). Everything else inherits.

   Source of truth (architecture): Token System Foundation Entity Home
     https://www.notion.so/35f81404729f81859b73ed529ad66a9a
   ============================================================ */

/* ============================================================
   LAYER 1 · BASE · theme-agnostic primitives (tokens.base.css)
   Platform-locked across every vertical, EXCEPT --font-* (vertical-overridable).
   ============================================================ */
:root{
  /* Typography families (vertical-overridable via branding_tokens.typography) */
  --font-display:'Inter Display', system-ui, sans-serif;
  --font-body:'Inter', sans-serif;
  --font-mono:'IBM Plex Mono', monospace;
  --font-heading:var(--font-display);            /* legacy alias */

  /* Type scale */
  --fs-xs:0.6875rem;   /* 11px */
  --fs-sm:0.8125rem;   /* 13px */
  --fs-base:0.9375rem; /* 15px */
  --fs-md:1rem;        /* 16px */
  --fs-lg:1.25rem;     /* 20px */
  --fs-xl:1.75rem;     /* 28px */
  --fs-xxl:2.5rem;     /* 40px */
  /* Micro tier (design-token-layout-consolidation): the mono micro-labels
     and smallest badges the pages carried as literal 9px/8px. Deliberately
     px, not rem: pages set the root to --fs-base (15px), so a rem here
     would silently shrink every micro label below its shipped size —
     measured, not assumed (teglo-diff caught the 1px page-height drift). */
  --fs-2xs:9px;
  --fs-3xs:8px;
  /* Scale aliases (legacy --text-* names) */
  --text-2xs:var(--fs-xs); --text-xs:var(--fs-xs); --text-sm:var(--fs-sm);
  --text-base:var(--fs-base); --text-md:var(--fs-md); --text-lg:var(--fs-lg); --text-xl:var(--fs-xl);

  /* Weights / line-heights / letter-spacing */
  --fw-regular:400; --fw-medium:500; --fw-semibold:600; --fw-bold:700;
  --lh-tight:1.15; --lh-normal:1.5; --lh-relaxed:1.7;
  --ls-tight:-0.01em; --ls-normal:0; --ls-wide:0.04em; --ls-wider:0.08em;

  /* Spacing · 4px grid */
  --space-3xs:0.125rem; --space-2xs:0.25rem; --space-xs:0.5rem; --space-sm:0.75rem;
  --space-md:1rem; --space-lg:1.5rem; --space-xl:2rem; --space-2xl:3rem; --space-3xl:4rem;

  /* Radius (xs + sm retired Phase 1.5; use md/lg).
     design-token-layout-consolidation: md/lg now ride the brand lock's
     radius slots (--radius-small / --radius-base, emitted per-theme by
     lib/brand-inheritance.js from locked_palette V3 tokens). No lock ->
     the fallbacks ARE the previous literals, byte-identical chrome; a
     lock -> one radius token repaints every card, panel and tile. */
  --radius-md:var(--radius-small, 8px); --radius-lg:var(--radius-base, 12px);
  --radius-pill:999px; --radius-circle:50%;
  --radius:var(--radius-lg);

  /* Motion */
  --duration-instant:50ms; --duration-fast:150ms; --duration-medium:250ms; --duration-slow:400ms;
  --ease-out:cubic-bezier(0.16, 1, 0.3, 1);
  --ease-in-out:cubic-bezier(0.65, 0, 0.35, 1);
  --ease:var(--ease-out);                        /* legacy alias */

  /* Breakpoints (size-based; use in @media) */
  --bp-sm:640px; --bp-md:768px; --bp-lg:1024px; --bp-xl:1280px;
  --bp-mobile:var(--bp-sm); --bp-tablet:var(--bp-lg); --bp-desktop:var(--bp-xl); --bp-wide:1600px;

  /* Z-index scale */
  --z-base:0; --z-raised:10; --z-dropdown:100; --z-panel:500; --z-modal:1000; --z-toast:2000;

  /* Layout */
  --container-max:1280px; --sidebar-w:220px;

  /* Accent global aliases (resolve the active theme's accent at use-site).
     --accent-bright existed only as per-page shims (01, 03) while five pages
     referenced it: on every other page .btn-primary:hover computed to
     transparent (invalid at computed-value time), which is how the Network
     "Resolve alerts" pill vanished on hover. Defined once here, both themes
     inherit it through --color-accent-bright. */
  --accent:var(--color-accent);
  --accent-bright:var(--color-accent-bright);

  /* Deprecation aliases (broken-ref fix; Phase 2 deletes) */
  --font:var(--font-body); --font-base:var(--font-body);
}

/* ============================================================
   LAYER 2 · DARK (tokens.teglo.dark.css) · CANONICAL Teglo default
   ============================================================ */
[data-theme="dark"]{
  --color-bg-base:#0A0B0F; --color-bg-surface:#13151A; --color-bg-elevated:#1A1D24;
  --color-bg-glass:rgba(255,255,255,0.03); --color-bg-overlay:rgba(10,11,15,0.75);

  --color-text:#F1F5F9; --color-text-secondary:#94A3B8;
  --color-text-tertiary:#64748B;          /* a11y: 3.84:1 on surface, non-essential meta only */
  --color-text-inverse:#0A0B0F; --color-text-disabled:#475569;

  --color-border-subtle:rgba(255,255,255,0.06); --color-border-default:rgba(255,255,255,0.1);
  --color-border-focus:var(--color-accent); --color-border-strong:rgba(255,255,255,0.18);

  --color-status-ok:#6EE7B7; --color-status-ok-bright:#34D399;
  --color-status-warn:#E6FF9A; --color-status-warn-bright:#C6E85A;   /* Teglo lime */
  --color-status-danger:#F588FD; --color-status-danger-bright:#E850F5; /* Teglo pink */
  --color-status-muted:#64748B;
  /* NO FEED joins the muted FAMILY (2026-08-27, operator repro: "state colours
     must be consistent estate-wide; no-feed cards diverge").
     It used to be its own violet, and on the same screens the same state was
     ALSO painted --info blue in half a dozen places - a hue that already means
     "info note" and "Sonar provider" - so one estate fact wore three colours
     while down and churned each wore one.
     It is now one step of the SAME slate ramp churned is cut from: same
     family, plainly related, still separable side by side (churned slate-500,
     no-feed slate-400), which matters because several surfaces show both in
     one donut. Not collapsed into churned: a line that ended and a line we
     never polled are different facts and the estate must be able to say which.
     The shape language is unchanged and still carries the sharper distinction
     - a no-feed cell is HOLLOW and DASHED, a churned cell is a muted FILL. See
     dispatches/ALTITUDE-SCALE-READINESS-AUDIT-2026-08-02.md §3(iv). */
  --color-status-nofeed:#94A3B8;

  /* ACCENT (vertical-overridable) */
  --color-accent:#98E2C7; --color-accent-bright:#5FCFA7; --color-accent-soft:rgba(152,226,199,0.08);
  --accent-rgb:152,226,199; --accent-glow:rgba(var(--accent-rgb),0.3);
  --accent-bg:var(--color-accent-soft); --accent-subtle:var(--color-accent-soft);

  --shadow-sm:0 1px 2px rgba(0,0,0,0.35); --shadow-md:0 4px 12px rgba(0,0,0,0.45);
  --shadow-lg:0 10px 30px rgba(0,0,0,0.6);
  --shadow-glow-ok:0 0 24px rgba(110,231,183,0.18); --shadow-glow-accent:0 0 24px rgba(152,226,199,0.25);

  --info:#93C5FD; --info-bg:rgba(147,197,253,0.1); --info-border:rgba(147,197,253,0.25);
  --scrollbar-track:#0d1117; --scrollbar-thumb:rgba(232,234,237,0.15);

  /* Outage amber: affected-cluster colour on the Network map. A true alarm amber,
     NOT --warn (Teglo lime reads too benign for an outage); down pins stay --danger. */
  --outage-amber:#F59E0B;

  /* Semantic Layer 1.5 (compounds consume these, not colour primitives) */
  --surface-card-bg:var(--color-bg-surface); --surface-card-bg-hover:var(--color-bg-elevated);
  --surface-card-border:var(--color-border-subtle);
  --surface-input-bg:var(--color-bg-base); --surface-input-border:var(--color-border-default);
  --surface-input-border-focus:var(--color-border-focus); --surface-input-bg-disabled:var(--color-bg-surface);
  --surface-modal-bg:var(--color-bg-elevated); --surface-modal-overlay:var(--color-bg-overlay);
  --focus-ring:0 0 0 2px var(--color-accent); --focus-ring-offset:2px; --focus-ring-color:var(--color-accent);

  /* Legacy roll-up aliases */
  --bg:var(--color-bg-base); --surface:var(--color-bg-surface); --surface-hover:var(--color-bg-elevated);
  --surface-raised:var(--color-bg-elevated);
  --text:var(--color-text); --text-secondary:var(--color-text-secondary); --text-tertiary:var(--color-text-tertiary);
  --text-on-accent:var(--color-text-inverse);
  --border:var(--color-border-default); --border-subtle:var(--color-border-subtle);
  /* THE THIRD BORDER WEIGHT, AND THE THIRD SURFACE STEP (added 2026-08-30).
     Both were referenced by name across the UI and DEFINED NOWHERE. The subject
     panel alone reaches for --border-strong eight times, each with a
     `var(--border-strong,var(--border))` fallback, so every one of them has been
     silently painting the ordinary border weight: the map band, the stepper
     rail, the checkbox, the tag outlines and the small buttons were all supposed
     to sit a step above the hairline and never did. A fallback that always fires
     is not a fallback, it is a missing token.
     Derived from the same base colours as --border and --surface-raised, so a
     client's brand lock repaints them with everything else rather than pinning
     them to one palette. */
  --border-strong:color-mix(in srgb, var(--color-text) 22%, var(--color-border-default));
  --surface-soft:color-mix(in srgb, var(--color-text) 7%, var(--color-bg-elevated));
  --glass-bg:var(--color-bg-glass); --glass-border:var(--color-border-subtle);
  --ok:var(--color-status-ok); --warn:var(--color-status-warn); --danger:var(--color-status-danger);
  --nofeed:var(--color-status-nofeed);
  --ok-bg:color-mix(in srgb, var(--color-status-ok) 15%, transparent);
  --warn-bg:color-mix(in srgb, var(--color-status-warn) 15%, transparent);
  --danger-bg:color-mix(in srgb, var(--color-status-danger) 15%, transparent);
  --ok-border:color-mix(in srgb, var(--color-status-ok) 30%, transparent);
  --warn-border:color-mix(in srgb, var(--color-status-warn) 30%, transparent);
  --danger-border:color-mix(in srgb, var(--color-status-danger) 30%, transparent);

  /* Account-status semantics (subscriber lifecycle panel) - aliased to existing
     status primitives so they follow the vertical accent + theme; no new hex. */
  --status-lead:var(--color-accent); --status-lead-bg:color-mix(in srgb, var(--color-accent) 15%, transparent);
  --status-shipped:var(--color-status-warn); --status-shipped-bg:color-mix(in srgb, var(--color-status-warn) 15%, transparent);
  --status-active:var(--color-status-ok); --status-active-bg:color-mix(in srgb, var(--color-status-ok) 15%, transparent);
  --status-overdue:var(--color-status-danger); --status-overdue-bg:color-mix(in srgb, var(--color-status-danger) 15%, transparent);
  --status-churned:var(--color-status-muted); --status-churned-bg:color-mix(in srgb, var(--color-status-muted) 15%, transparent);
  /* THE ESTATE STATE SCHEME (2026-08-27). down / churned / no-feed are one
     scheme and are declared together here so a surface cannot pick a colour
     for one of them without seeing the other two. Any estate surface painting
     these states reads THESE tokens - not --danger / --info / a hex - so
     "consistent estate-wide" is a property of the token file rather than of
     everyone remembering. */
  --status-down:var(--color-status-danger); --status-down-bg:color-mix(in srgb, var(--color-status-danger) 15%, transparent);
  --status-nofeed:var(--color-status-nofeed); --status-nofeed-bg:color-mix(in srgb, var(--color-status-nofeed) 15%, transparent);

  /* Deprecation aliases (Phase 1 broken-ref fix) */
  --bg-elev:var(--color-bg-elevated); --fg-muted:var(--color-text-secondary);
  --text-primary:var(--color-text); --warning:var(--color-status-warn);

  /* Legacy provider tokens · @deprecated · integrations render in --accent */
  --color-provider-sonar:#91ACFD; --color-provider-whmcs:#22D3EE; --color-provider-stripe:#A78BFA;
  --purple-bg:color-mix(in srgb, var(--color-provider-stripe) 15%, transparent);
}

/* ============================================================
   LAYER 2 · LIGHT (tokens.teglo.light.css)
   a11y note: canonical light accent #059669 = 3.77:1 white-on-accent (FAIL AA
   for small text) and tertiary #94A3B8 = 2.56:1 (FAIL). Carried verbatim from
   canonical; fix at the upstream source, not here.
   ============================================================ */
[data-theme="light"]{
  --color-bg-base:#FAFAF9; --color-bg-surface:#FFFFFF; --color-bg-elevated:#FFFFFF;
  --color-bg-glass:rgba(0,0,0,0.02); --color-bg-overlay:rgba(10,11,15,0.4);

  --color-text:#0F172A; --color-text-secondary:#475569;
  --color-text-tertiary:#94A3B8;          /* a11y FLAG: 2.56:1 on white */
  --color-text-inverse:#FFFFFF; --color-text-disabled:#CBD5E1;

  --color-border-subtle:rgba(0,0,0,0.06); --color-border-default:rgba(0,0,0,0.12);
  --color-border-focus:var(--color-accent); --color-border-strong:rgba(0,0,0,0.2);

  --color-status-ok:#059669; --color-status-ok-bright:#10B981;
  --color-status-warn:#D97706; --color-status-warn-bright:#F59E0B;
  --color-status-danger:#DB2777; --color-status-danger-bright:#EC4899;
  --color-status-muted:#94A3B8;
  /* Same family in light theme, stepped the other way: churned is slate-400
     here, so no-feed takes slate-500 to stay legible on a light ground. See
     the dark block above for the reasoning. */
  --color-status-nofeed:#64748B;

  /* ACCENT (vertical-overridable) · a11y FLAG: #059669 white-on-accent 3.77:1 */
  --color-accent:#059669; --color-accent-bright:#10B981; --color-accent-soft:rgba(16,185,129,0.08);
  --accent-rgb:16,185,129; --accent-glow:rgba(var(--accent-rgb),0.2);
  --accent-bg:var(--color-accent-soft); --accent-subtle:var(--color-accent-soft);

  --shadow-sm:0 1px 2px rgba(15,23,42,0.06); --shadow-md:0 4px 12px rgba(15,23,42,0.08);
  --shadow-lg:0 10px 30px rgba(15,23,42,0.12);
  --shadow-glow-ok:0 0 24px rgba(16,185,129,0.15); --shadow-glow-accent:0 0 24px rgba(16,185,129,0.2);

  --info:#2563EB; --info-bg:rgba(37,99,235,0.08); --info-border:rgba(37,99,235,0.2);
  --scrollbar-track:#f5f6f8; --scrollbar-thumb:rgba(13,17,23,0.15);

  /* Outage amber: affected-cluster colour on the Network map. A true alarm amber,
     NOT --warn (Teglo lime reads too benign for an outage); down pins stay --danger. */
  --outage-amber:#F59E0B;

  --surface-card-bg:var(--color-bg-surface); --surface-card-bg-hover:var(--color-bg-elevated);
  --surface-card-border:var(--color-border-subtle);
  --surface-input-bg:var(--color-bg-base); --surface-input-border:var(--color-border-default);
  --surface-input-border-focus:var(--color-border-focus); --surface-input-bg-disabled:var(--color-bg-surface);
  --surface-modal-bg:var(--color-bg-elevated); --surface-modal-overlay:var(--color-bg-overlay);
  --focus-ring:0 0 0 2px var(--color-accent); --focus-ring-offset:2px; --focus-ring-color:var(--color-accent);

  --bg:var(--color-bg-base); --surface:var(--color-bg-surface); --surface-hover:var(--color-bg-elevated);
  --surface-raised:var(--color-bg-elevated);
  --text:var(--color-text); --text-secondary:var(--color-text-secondary); --text-tertiary:var(--color-text-tertiary);
  --text-on-accent:var(--color-text-inverse);
  --border:var(--color-border-default); --border-subtle:var(--color-border-subtle);
  /* THE THIRD BORDER WEIGHT, AND THE THIRD SURFACE STEP (added 2026-08-30).
     Both were referenced by name across the UI and DEFINED NOWHERE. The subject
     panel alone reaches for --border-strong eight times, each with a
     `var(--border-strong,var(--border))` fallback, so every one of them has been
     silently painting the ordinary border weight: the map band, the stepper
     rail, the checkbox, the tag outlines and the small buttons were all supposed
     to sit a step above the hairline and never did. A fallback that always fires
     is not a fallback, it is a missing token.
     Derived from the same base colours as --border and --surface-raised, so a
     client's brand lock repaints them with everything else rather than pinning
     them to one palette. */
  --border-strong:color-mix(in srgb, var(--color-text) 22%, var(--color-border-default));
  --surface-soft:color-mix(in srgb, var(--color-text) 7%, var(--color-bg-elevated));
  --glass-bg:var(--color-bg-glass); --glass-border:var(--color-border-subtle);
  --ok:var(--color-status-ok); --warn:var(--color-status-warn); --danger:var(--color-status-danger);
  --nofeed:var(--color-status-nofeed);
  --ok-bg:color-mix(in srgb, var(--color-status-ok) 15%, transparent);
  --warn-bg:color-mix(in srgb, var(--color-status-warn) 15%, transparent);
  --danger-bg:color-mix(in srgb, var(--color-status-danger) 15%, transparent);
  --ok-border:color-mix(in srgb, var(--color-status-ok) 30%, transparent);
  --warn-border:color-mix(in srgb, var(--color-status-warn) 30%, transparent);
  --danger-border:color-mix(in srgb, var(--color-status-danger) 30%, transparent);

  /* Account-status semantics (subscriber lifecycle panel) - aliased to existing
     status primitives so they follow the vertical accent + theme; no new hex. */
  --status-lead:var(--color-accent); --status-lead-bg:color-mix(in srgb, var(--color-accent) 15%, transparent);
  --status-shipped:var(--color-status-warn); --status-shipped-bg:color-mix(in srgb, var(--color-status-warn) 15%, transparent);
  --status-active:var(--color-status-ok); --status-active-bg:color-mix(in srgb, var(--color-status-ok) 15%, transparent);
  --status-overdue:var(--color-status-danger); --status-overdue-bg:color-mix(in srgb, var(--color-status-danger) 15%, transparent);
  --status-churned:var(--color-status-muted); --status-churned-bg:color-mix(in srgb, var(--color-status-muted) 15%, transparent);
  /* THE ESTATE STATE SCHEME (2026-08-27). down / churned / no-feed are one
     scheme and are declared together here so a surface cannot pick a colour
     for one of them without seeing the other two. Any estate surface painting
     these states reads THESE tokens - not --danger / --info / a hex - so
     "consistent estate-wide" is a property of the token file rather than of
     everyone remembering. */
  --status-down:var(--color-status-danger); --status-down-bg:color-mix(in srgb, var(--color-status-danger) 15%, transparent);
  --status-nofeed:var(--color-status-nofeed); --status-nofeed-bg:color-mix(in srgb, var(--color-status-nofeed) 15%, transparent);

  --bg-elev:var(--color-bg-elevated); --fg-muted:var(--color-text-secondary);
  --text-primary:var(--color-text); --warning:var(--color-status-warn);

  --color-provider-sonar:#6366F1; --color-provider-whmcs:#0891B2; --color-provider-stripe:#7C3AED;
  --purple-bg:color-mix(in srgb, var(--color-provider-stripe) 15%, transparent);
}

/* ============================================================
   PER-VERTICAL ACCENT (static stand-in for the Phase 3 JSONB injection)
   Values verbatim from verticals.demo.json. Override = accent + rgb only;
   bright/soft derived here for the demo. Toggle via data-vertical on <html>.
   ============================================================ */
[data-vertical="conduct-connect"]{
  --color-accent:#2BBAE8; --color-accent-bright:#5AD0F5; --color-accent-soft:rgba(43,186,232,0.08); --accent-rgb:43,186,232;
}
[data-vertical="tnt-internet"]{
  --color-accent:#731AB3; --color-accent-bright:#9B57D4; --color-accent-soft:rgba(115,26,179,0.08); --accent-rgb:115,26,179;
}

/* ============================================================
   WIREFRAME-ERA COMPATIBILITY BRIDGE · retire in the Phase 2 page migration
   The pilot wireframes were authored against pre-canonical token NAMES
   (--motion-*, --radius-sm/xs, --map-grad-*). These aliases let them link
   this file without breaking. New work uses canonical names
   (--duration-*, --radius-md, --fs-*). Slight shifts vs the old inline values:
   motion 120/180/320 -> 150/250/400, --ease feel changes, type lg/xl/2xs sizes up.
   ============================================================ */
:root{
  --motion-fast:var(--duration-fast); --motion-base:var(--duration-medium);
  --motion-slow:var(--duration-slow); --motion-celebrate:600ms;
  --radius-sm:var(--radius-md); --radius-xs:var(--radius-md);
}
[data-theme="dark"]{ --map-grad-from:#1A2336; --map-grad-to:#0F1422; }
[data-theme="light"]{ --map-grad-from:#DCE6F5; --map-grad-to:#C3D2EA; }

/* ============================================================
   ACCESSIBILITY · prefers-reduced-motion zeroes all durations
   ============================================================ */
@media (prefers-reduced-motion: reduce){
  :root{ --duration-instant:0ms; --duration-fast:0ms; --duration-medium:0ms; --duration-slow:0ms; }
}

/* ============================================================
   NUCLEAR CONSOLIDATION ADDITIONS · 2026-06-10
   --map-filter closes the drift flagged in 00-global-patterns
   HANDOVER §8 (§24 Map tile dark theme). Additive only.
   ============================================================ */
[data-theme="dark"]{ --map-filter:invert(.92) hue-rotate(180deg) brightness(.95) contrast(.9); }
[data-theme="light"]{ --map-filter:none; }

/* Pilot UI hygiene: never print render-type labels on tile faces (var-badge),
   and never show the legacy dev "wire" status ribbon. States are handled inline
   per section (skeleton / calm empty / toast). !important beats per-page inline. */
.var-badge{ display:none !important; }
#wire-status{ display:none !important; }

/* ============================================================
   CANONICAL COMPONENT SET · renderer parity Phase 2 (2026-07-27)
   ------------------------------------------------------------
   Additive only. Geometry + timing primitives mirroring the ones
   added to tokens/base.css (this file is the single-file consumer
   bundle; base.css is the layered one — they stay in step), plus
   the SEMANTIC compounds the canonical components read.

   The components never read a primitive directly: they read the
   compounds below, which resolve through the active theme. A
   vertical theme file therefore recolours skeleton, ribbon and
   frozen-empty affordances without any component change.
   ============================================================ */
:root{
  /* Render-state motion (within the 150–250ms budget; the
     prefers-reduced-motion block above zeroes the scale these read) */
  --duration-state:var(--duration-medium); --duration-mount:var(--duration-fast);
  --cc-enter-shift:6px; --cc-stagger-step:40ms;

  /* Skeleton geometry + timing */
  --skeleton-radius:var(--radius-md); --skeleton-line-h:0.75rem;
  --skeleton-value-h:1.75rem; --skeleton-shimmer-dur:1.6s;

  /* Canonical component geometry */
  --kpi-min-w:11rem; --kpi-min-w-mobile:9.5rem; --sec-min-w:16rem;
  --detail-w:22.5rem; --detail-sheet-max:85vh;
  --touch-min:44px; --touch-min-inline:24px;

  /* ── Semantic compounds ───────────────────────────────────
     Skeleton: a base plate and the highlight that sweeps it. */
  --skeleton-bg:color-mix(in srgb, var(--text-tertiary) 16%, transparent);
  --skeleton-highlight:color-mix(in srgb, var(--text-tertiary) 30%, transparent);

  /* Frozen empty (PPA-006): deliberately quieter than a real value,
     but never below the AA floor — it is content, not decoration. */
  --frozen-text:var(--text-secondary);
  --frozen-mark:var(--text-tertiary);
  --frozen-surface:color-mix(in srgb, var(--text-tertiary) 6%, transparent);
  /* The DASHED border is one of the signals that distinguishes an empty
     component from a filled one, so it is meaningful non-text content and
     owes WCAG 1.4.11's 3:1 — it cannot reuse --color-border-subtle, which
     measured 1.14:1 and was effectively invisible. */
  --frozen-border:var(--text-tertiary);

  /* Attention ribbon severity — maps onto the theme's status ramp so a
     vertical brand carries through without redefining severity. */
  --ribbon-critical:var(--color-status-danger);
  --ribbon-warn:var(--color-status-warn);
  --ribbon-info:var(--color-accent);
  --ribbon-quiet:var(--text-tertiary);
}
