@charset "utf-8";
/*
 * ═══════════════════════════════════════════════════════════════════════════
 *  COSMIX — the skin.
 *  One hand-written stylesheet. No framework, no build step, no preprocessor.
 *  Written against design/ART-DIRECTION.md and design/DESIGN-PLAN.md.
 *
 *  The concept, in one line: a warm, lit workshop with a cool instrument on
 *  the bench. Paper ground, navy ink, aurora rationed like gold leaf.
 *
 *  Layer order is declared once, here. Everything in this file is layered.
 *  As of stage 3 there are no other stylesheets: the five sheets this file
 *  replaced are gone, and nothing is imported.
 * ═══════════════════════════════════════════════════════════════════════════
 */

@layer legacy, tokens, base, components, shell, chat, panels;

/* Stage 3 cut the last two legacy imports. Nothing else is loaded: index.php
   links this file alone, and this file imports nothing. The `legacy` layer is
   kept in the order declaration as the lowest rung so that any stylesheet a
   future stage has to quarantine has a place to go that can never outrank the
   system above it. */

/* ═══════════════════════════════════════════════════════════════════════════
 *  THE MOTION CATALOGUE — closed at stage 4.
 *
 *  Nothing in this product animates that is not on this list. Every entry is
 *  transform and/or opacity, property-scoped, on a named easing and a named
 *  duration token. The single exception is M-03, and it is commented where it
 *  lives. `transition: all` is a defect. So is a fifth @keyframes.
 *
 *   ID    what                              where it lives
 *   ──    ────────────────────────────────  ────────────────────────────────
 *   M-01  auth entrance                     §5.1  .cx-auth__brand > * / __col
 *   M-02  rail-toggle glyph answer          §4.2  .cx-railtoggle__bar
 *   M-03  rail collapse / expand            §4.1  .cx-appshell grid track
 *   M-04  rail active indicator (FADES)     §4.4  .cx-rail__ind
 *   M-05  rail boot stagger                 §4.7b .cx-rail__group > *
 *   M-06  rail scrim                        §4.6  .cx-railscrim
 *   M-07  selector brand mark rotation      §4.5  .cx-selector__mark
 *   M-08  menu / popover open               §3.10 .cx-menu
 *   M-09  first-run creator entrance        §5.2  .cx-creator > *
 *   M-10  icon-button press                 §3.2  .cx-iconbtn:active
 *   M-11  section-tab underline (TRAVELS)   §4.4  .cx-secbar__underline
 *   M-12  breathing dot                     §3.5  .cx-dot--breathe
 *   M-13  reload glyph, once per press      §6.4  .cx-ws__reload svg
 *   M-14  jump-to-latest pill               §7.2  .cx-chat__jump
 *   M-15  prose link underline draw         §2.1  .cx-prose a / .cx-msg__body a
 *   M-16  copy confirm (label swap, 1.5s)   §7.4  .cx-md__copy
 *   M-17  tool chip                         §7.5  .cx-toolchip
 *   M-18  inline notice                     §3.14 .cx-notice
 *   M-19  chip / badge appear               §7.7  .cx-refchip · §8.7 .cx-pv__diag
 *   M-20  drop bar                          §9    .cx-dropbar
 *   M-21  first-run welcome                 §7.8  .cx-welcome
 *   M-22  message entrance, ONCE per msg    §7.3  .cx-msg--enter
 *   M-23  the working sweep                 §3.15 .cx-rule__sweep
 *   M-24  preview plate crossfade           §8.2  .cx-plate
 *   M-25  pane swap                         §6.7  .cx-ws__pane--enter
 *   M-26  select-mode hint pill             §8.3  .cx-pv__hint
 *   M-27  element-highlight glide           the injected inspector, 60ms
 *   M-28  list-row hover bar                §3.7 / §9.6 ::before scaleX
 *   M-29  media hover tint                  §9.6 / §9.7 filter
 *   M-30  settings group stagger            §9.11 .cx-settings__panel
 *   M-31  directional glyph                 §3.2b svg[data-icon]
 *   M-32  switch knob                       §3.17 / §9.17 track::after
 *   M-33  drawer                            §3.11 .cx-drawer
 *   M-34  modal                             §3.12 .cx-modal / __panel
 *   M-35  toast                             §3.13 .cx-toast
 *   M-36  ambient wash drift                §2.4  .cx-wash
 *
 *  The laws behind the list: chrome answers in ≤140ms and content performs in
 *  220–420ms; nothing interactive exceeds 420ms; nothing eases IN; the
 *  background moves and the type never does; press compresses (.94) harder
 *  than hover expands (nothing); no hover raises, shadows or glows; elastic
 *  and bounce do not exist; a menu is never scaled.
 * ═══════════════════════════════════════════════════════════════════════════
 */

/* ═══════════════════════════ 1. TOKENS ═══════════════════════════ */

@layer tokens {

  /* ── 1.1 The three faces, self-hosted (CSP: font-src 'self') ── */

  @font-face {
    font-family: "Plus Jakarta Sans";
    src: url(../fonts/PlusJakartaSans-var-latin.woff2) format("woff2");
    font-weight: 200 800; font-style: normal; font-display: swap;
  }
  @font-face {
    font-family: "Plus Jakarta Sans";
    src: url(../fonts/PlusJakartaSans-var-latin-italic.woff2) format("woff2");
    font-weight: 200 800; font-style: italic; font-display: swap;
  }
  @font-face {
    font-family: "IBM Plex Mono";
    src: url(../fonts/IBMPlexMono-400-latin.woff2) format("woff2");
    font-weight: 400; font-display: swap;
  }
  @font-face {
    font-family: "IBM Plex Mono";
    src: url(../fonts/IBMPlexMono-500-latin.woff2) format("woff2");
    font-weight: 500; font-display: swap;
  }
  @font-face {
    font-family: "IBM Plex Mono";
    src: url(../fonts/IBMPlexMono-600-latin.woff2) format("woff2");
    font-weight: 600; font-display: swap;
  }
  @font-face {
    font-family: "Fraunces";
    src: url(../fonts/Fraunces-display-latin.woff2) format("woff2");
    font-weight: 500; font-display: swap;
  }

  /* Metric-matched fallbacks: the swap must not move a single line. */
  @font-face {
    font-family: "Jakarta Fallback"; src: local("Arial");
    size-adjust: 104%; ascent-override: 96%; descent-override: 24%; line-gap-override: 0%;
  }
  @font-face {
    font-family: "Plex Fallback"; src: local("Courier New");
    size-adjust: 104%; ascent-override: 92%; descent-override: 24%; line-gap-override: 0%;
  }

  /* ── 1.2 Light theme — warm paper, cool ink.
         Every neutral sits on a 37–40° hue axis with R > G > B.
         There is not one grey in the system.
         LIGHT IS THE BARE :root DEFINITION. Every colour's FIRST definition
         is here, never inside a media query or a [data-theme] block. ── */

  :root {
    /* surfaces: lighter = higher */
    --s-inset: #E7E2D9;
    --s-0:     #EFEBE4;
    --s-1:     #F5F2EC;
    --s-2:     #FAF8F4;
    --s-3:     #FFFDFA;

    /* lines: warm, derived from the paper */
    --line-1: #E4DFD6;
    --line-2: #DAD3C7;
    --line-3: #C6BDAE;

    /* ink: cool navy on warm paper */
    --text-display: #10213E;
    --text-1: #1D2E4A;
    --text-2: #4A5A73;
    --text-3: #5A697F;
    --text-4: #8A97A9;   /* disabled / decorative ONLY — never carries information */

    /* accent */
    --accent:        #2E62D8;
    --accent-strong: #23509E;
    --accent-hover:  #2450BF;
    --accent-rgb:    46, 98, 216;
    --accent-tint:       rgba(46, 98, 216, 0.08);
    --accent-tint-hover: rgba(46, 98, 216, 0.14);
    /* The ONE foreground certified to sit on a SOLID accent or danger fill.
       Measured: 5.45 on --accent, 5.90 on --danger. Every tinted control
       keeps --accent itself as its ink; this token exists only for the
       composer's filled send/stop corner. */
    --accent-ink:    #FFFFFF;

    /* semantics */
    --ok:     #217A57;  --ok-tint:     rgba(33, 122, 87, 0.10);
    --warn:   #8F5D0D;  --warn-tint:   rgba(143, 93, 13, 0.10);
    --danger: #B33A2E;  --danger-tint: rgba(179, 58, 46, 0.10);
    /* The exact analogue of --accent-strong, for the same reason: a label
       sitting ON its own tint loses contrast against the composite. Measured
       against the tint over --s-0, the worst case: 4.66 / 4.66 / 4.60. */
    --ok-strong:     #1D6D4D;
    --warn-strong:   #86570C;
    --danger-strong: #AA372C;

    /* the two blurred shadows that exist — overlays only */
    --shadow-pop:   0 1px 2px rgba(16,33,62,.05), 0 12px 32px -8px rgba(16,33,62,.14);
    --shadow-modal: 0 2px 4px rgba(16,33,62,.06), 0 32px 64px -16px rgba(16,33,62,.20);

    /* light from above on a raised sheet; a well for a recess */
    --lit:  inset 0 1px 0 rgba(255,255,255,.75);
    --well: inset 0 1px 2px rgba(16,33,62,.05);

    /* the brand aurora — stops 0 / 45% / 100%, vector 48°, exactly as the SVG */
    --aurora:      linear-gradient(48deg, #079FC9 0%, #356DE0 45%, #8D2BC9 100%);
    --aurora-rule: linear-gradient(90deg, #079FC9 0%, #356DE0 45%, #8D2BC9 100%);
    --aurora-cyan:   #079FC9;
    --aurora-blue:   #356DE0;
    --aurora-violet: #8D2BC9;

    /* The ambient wash paint (light: a warm sun tone through a window).
       The opacity is NOT the .05 first written down: composited on --s-1 that
       produced a peak delta of (0,-1,-2) out of 255 — below the 8-bit
       quantisation floor, i.e. the light theme had no light falling on it at
       all. At .16 the peak is (+1,-3,-6): a fall of light you can see, still
       far below anything that reads as a tint. Dark stays at .06 because a
       saturated blue-violet wash on a near-black ground carries further.
       Do not "restore" this to .05. */
    --wash: radial-gradient(closest-side, rgba(255,214,170,.55) 0%, rgba(255,214,170,.50) 18%, rgba(255,214,170,.38) 36%, rgba(255,214,170,.24) 54%, rgba(255,214,170,.12) 72%, rgba(255,214,170,.04) 88%, rgba(255,214,170,0) 100%);
    --wash-opacity: .55;

    --scrim: color-mix(in srgb, var(--s-inset) 55%, transparent);

    /* ── families ── */
    --font-sans: "Plus Jakarta Sans", "Jakarta Fallback", ui-sans-serif, system-ui,
                 -apple-system, "Segoe UI", Roboto, Arial, sans-serif;
    --font-mono: "IBM Plex Mono", "Plex Fallback", ui-monospace, SFMono-Regular,
                 "Cascadia Mono", Menlo, Consolas, monospace;
    --font-display: "Fraunces", "Plus Jakarta Sans", Georgia, serif;

    /* ── spacing: 4px base, ten steps, narrow usage ── */
    --sp-1: 2px;  --sp-2: 4px;  --sp-3: 6px;  --sp-4: 8px;  --sp-5: 12px;
    --sp-6: 16px; --sp-7: 24px; --sp-8: 32px; --sp-9: 48px; --sp-10: 64px;

    /* fluid where the container is fluid — never for type */
    --pad-panel:   clamp(12px, 1.4vw, 24px);
    --pad-section: clamp(16px, 2.2vw, 32px);
    --gutter:      clamp(8px, 0.9vw, 16px);

    /* ── radius ── */
    --r-1: 3px; --r-2: 8px; --r-3: 12px; --r-4: 16px; --r-full: 999px;

    /* ── structural constants — the layout contract. Do not change. ── */
    --rail-w: 56px;
    --rail-item-h: 48px;
    --rail-top: 56px;
    --toggle: 36px;
    --topbar-h: 48px;
    --rule-h: 2px;
    --indicator-w: 3px;
    --indicator-h: 20px;
    --tap: 44px;

    /* component heights, on the 4px grid */
    --h-xs: 28px; --h-sm: 32px; --h-md: 36px; --h-lg: 44px;

    /* icon sizes */
    --icon-rail: 20px; --icon-ui: 18px; --icon-sm: 16px; --icon-chip: 14px;

    /* columns */
    --chat-w: clamp(360px, 34%, 560px);
    --chat-w-wide: 640px;
    --drawer-w: min(100%, 380px);
    --drawer-w-wide: 400px;

    /* safe areas + on-screen keyboard (JS owns --kb-inset and nothing else) */
    --safe-t: env(safe-area-inset-top, 0px);
    --safe-b: env(safe-area-inset-bottom, 0px);
    --safe-l: env(safe-area-inset-left, 0px);
    --safe-r: env(safe-area-inset-right, 0px);
    --kb-inset: 0px;
    --comp-pad: max(calc(var(--safe-b) - var(--kb-inset)), 8px);

    /* ── motion. Nothing eases IN on its own; everything departs at full
         speed and settles. Elastic and bounce do not exist here. ── */
    --ease-settle: cubic-bezier(0.33, 1, 0.68, 1);
    --ease-glide:  cubic-bezier(0.19, 1, 0.22, 1);
    --ease-travel: cubic-bezier(0.65, 0, 0.35, 1);
    /* The one curve with overshoot. Hover-IN only, on a childless element
       under 44px (AD §5.1/§5.4). Nothing in the product currently qualifies,
       so it is declared and deliberately unspent — using it on anything that
       contains type is a defect. */
    --ease-engage: cubic-bezier(0.175, 0.885, 0.32, 1.275);
    --ease-linear: linear;

    --t-0: 90ms;    /* chrome colour + opacity */
    --t-1: 140ms;   /* small transforms, indicator fades, tooltips */
    --t-2: 220ms;   /* menus, popovers, message entrance, rail collapse */
    --t-3: 420ms;   /* panel + drawer slide, preview swap, modal */
    --t-4: 900ms;   /* first-paint choreography only */
    --t-amb: 90s;   /* ambient light drift */

    /* ── z: a governed named scale, never an ad-hoc number ── */
    --z-content: 10; --z-hover: 20; --z-popover: 30; --z-overlay: 40;
    --z-modal: 50;   --z-modal-focus: 60; --z-toast: 100; --z-drag: 110;
    /* R5-09 — a popover is always spawned BY something on screen and must
       never rank below its host. Menus opened from inside the overlay rail
       (40, its scrim 39) or over a modal surface (50) sit here: above both
       hosts, still below the modal focus layer (60), toasts (100) and the
       drag layer (110). No modal conflict: 55 paints a menu ABOVE its own
       spawning modal, which is the point, and nothing between 50 and 60
       but this rung. */
    --z-popover-top: 55;
  }

  /* ── 1.3 Dark theme — violet-black instrument, warm off-white ink.
         R stays above G on every step; the R−G gap widens one point per rung
         so surfaces get lighter AND warmer as they lift. ── */

  @media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) {
      --s-inset: #08070C;
      --s-0:     #0C0A12;
      --s-1:     #131019;
      --s-2:     #191622;
      --s-3:     #221E2C;

      --line-1: #221E2B;
      --line-2: #2C2736;
      --line-3: #3A3446;

      --text-display: #FCF9F3;
      --text-1: #F0ECE4;
      --text-2: #B9B2A8;
      --text-3: #8B8479;
      --text-4: #625C54;

      --accent:        #4C82FF;
      --accent-strong: #6E9BFF;
      --accent-hover:  #6E9BFF;
      --accent-rgb:    76, 130, 255;
      --accent-tint:       rgba(76, 130, 255, 0.12);
      --accent-tint-hover: rgba(76, 130, 255, 0.20);
      /* dark ground as ink on the filled corner: 5.57 on --accent,
         7.09 on --danger. White would measure 3.53 and fail. */
      --accent-ink:    #0C0A12;

      --ok:     #4FD6A0;  --ok-tint:     rgba(79, 214, 160, 0.14);
      --warn:   #F2B44C;  --warn-tint:   rgba(242, 180, 76, 0.14);
      --danger: #FF6B6B;  --danger-tint: rgba(255, 107, 107, 0.14);
      --ok-strong: #4FD6A0; --warn-strong: #F2B44C; --danger-strong: #FF6B6B;

      --shadow-pop:   0 1px 2px rgba(0,0,0,.5), 0 16px 40px -12px rgba(0,0,0,.70);
      --shadow-modal: 0 2px 6px rgba(0,0,0,.55), 0 40px 80px -20px rgba(0,0,0,.80);

      --lit:  inset 0 1px 0 rgba(255,255,255,.045);
      --well: inset 0 1px 2px rgba(0,0,0,.40);

      --aurora:      linear-gradient(48deg, #00BCEA 0%, #3877FF 45%, #A329E8 100%);
      --aurora-rule: linear-gradient(90deg, #00BCEA 0%, #3877FF 45%, #A329E8 100%);
      --aurora-cyan:   #00BCEA;
      --aurora-blue:   #3877FF;
      --aurora-violet: #A329E8;

      --wash: radial-gradient(closest-side, rgba(56,119,255,.60) 0%, rgba(70,100,255,.52) 20%, rgba(110,74,246,.42) 40%, rgba(163,41,232,.28) 60%, rgba(163,41,232,.14) 78%, rgba(163,41,232,.04) 90%, rgba(163,41,232,0) 100%);
      --wash-opacity: .06;

      --scrim: color-mix(in srgb, #000 62%, transparent);
    }
  }

  /* Explicit dark wins in both directions. */
  :root[data-theme="dark"] {
    --s-inset: #08070C;
    --s-0:     #0C0A12;
    --s-1:     #131019;
    --s-2:     #191622;
    --s-3:     #221E2C;

    --line-1: #221E2B;
    --line-2: #2C2736;
    --line-3: #3A3446;

    --text-display: #FCF9F3;
    --text-1: #F0ECE4;
    --text-2: #B9B2A8;
    --text-3: #8B8479;
    --text-4: #625C54;

    --accent:        #4C82FF;
    --accent-strong: #6E9BFF;
    --accent-hover:  #6E9BFF;
    --accent-rgb:    76, 130, 255;
    --accent-tint:       rgba(76, 130, 255, 0.12);
    --accent-tint-hover: rgba(76, 130, 255, 0.20);
    /* dark ground as ink on the filled corner: 5.57 on --accent,
       7.09 on --danger. White would measure 3.53 and fail. */
    --accent-ink:    #0C0A12;

    --ok:     #4FD6A0;  --ok-tint:     rgba(79, 214, 160, 0.14);
    --warn:   #F2B44C;  --warn-tint:   rgba(242, 180, 76, 0.14);
    --danger: #FF6B6B;  --danger-tint: rgba(255, 107, 107, 0.14);
    --ok-strong: #4FD6A0; --warn-strong: #F2B44C; --danger-strong: #FF6B6B;

    --shadow-pop:   0 1px 2px rgba(0,0,0,.5), 0 16px 40px -12px rgba(0,0,0,.70);
    --shadow-modal: 0 2px 6px rgba(0,0,0,.55), 0 40px 80px -20px rgba(0,0,0,.80);

    --lit:  inset 0 1px 0 rgba(255,255,255,.045);
    --well: inset 0 1px 2px rgba(0,0,0,.40);

    --aurora:      linear-gradient(48deg, #00BCEA 0%, #3877FF 45%, #A329E8 100%);
    --aurora-rule: linear-gradient(90deg, #00BCEA 0%, #3877FF 45%, #A329E8 100%);
    --aurora-cyan:   #00BCEA;
    --aurora-blue:   #3877FF;
    --aurora-violet: #A329E8;

    --wash: radial-gradient(closest-side, rgba(56,119,255,.60) 0%, rgba(70,100,255,.52) 20%, rgba(110,74,246,.42) 40%, rgba(163,41,232,.28) 60%, rgba(163,41,232,.14) 78%, rgba(163,41,232,.04) 90%, rgba(163,41,232,0) 100%);
    --wash-opacity: .06;

    --scrim: color-mix(in srgb, #000 62%, transparent);
  }

  /* ── 1.4 Legacy bridge.
       The screens stages 2 and 3 still own read the old `--cx-*` names. They
       are re-pointed at the new system here, so no old colour value, radius or
       duration survives anywhere on screen — only the old geometry does, and
       only until its stage lands. Delete this block with the last legacy
       stylesheet. ── */
  :root {
    --cx-surface-0: var(--s-0);
    --cx-surface-1: var(--s-1);
    --cx-surface-2: var(--s-2);
    --cx-surface-3: var(--s-3);
    --cx-surface-4: var(--s-3);
    --cx-text: var(--text-1);
    --cx-text-muted: var(--text-2);
    --cx-text-faint: var(--text-3);
    --cx-border: var(--line-2);
    --cx-border-strong: var(--line-3);
    --cx-border-control: var(--line-2);
    --cx-border-control-hover: var(--line-3);
    --cx-hairline: var(--line-1);
    --cx-accent: var(--accent);
    --cx-accent-vivid: var(--accent-hover);
    --cx-accent-rgb: var(--accent-rgb);
    --cx-garnish: var(--accent);
    --cx-garnish-rgb: var(--accent-rgb);
    --cx-spark: var(--accent);
    --cx-danger: var(--danger);
    --cx-danger-rgb: 179, 58, 46;
    --cx-success: var(--ok);
    --cx-success-rgb: 33, 122, 87;
    --cx-warn: var(--warn);
    --cx-warn-rgb: 143, 93, 13;
    --cx-aurora-cyan: var(--aurora-cyan);
    --cx-aurora-violet: var(--aurora-violet);
    --cx-scrim: var(--scrim);
    --cx-selection: var(--accent-tint-hover);
    --cx-dot: var(--text-3);
    --cx-indicator: var(--accent);
    --cx-lift: none;
    --cx-glow: none;
    --cx-gradient-glow: var(--aurora);
    --cx-gradient-progress: var(--accent);
    --cx-shadow-popover: var(--shadow-pop);
    --cx-shadow-modal: var(--shadow-modal);
    --cx-shadow-drag: var(--shadow-pop);
    --cx-radius-sm: var(--r-1);
    --cx-radius-md: var(--r-2);
    --cx-radius-lg: var(--r-3);
    --cx-radius-xl: var(--r-4);
    --cx-radius-full: var(--r-full);
    --cx-space-0: 0px; --cx-space-1: var(--sp-2); --cx-space-2: var(--sp-3);
    --cx-space-3: var(--sp-4); --cx-space-4: var(--sp-5); --cx-space-5: var(--sp-6);
    --cx-space-6: var(--sp-7); --cx-space-7: var(--sp-8); --cx-space-8: var(--sp-9);
    --cx-space-10: var(--sp-10); --cx-space-12: 80px; --cx-space-16: 96px;
    --cx-control-xs: var(--h-xs); --cx-control-sm: var(--h-sm);
    --cx-control-md: var(--h-md); --cx-control-lg: var(--h-lg);
    --cx-touch-min: var(--tap);
    --cx-font-sans: var(--font-sans);
    --cx-font-mono: var(--font-mono);
    --cx-text-xs: 11px; --cx-text-sm: 13px; --cx-text-base: 14px;
    --cx-text-md: 15px; --cx-text-lg: 18px; --cx-text-xl: 24px; --cx-text-2xl: 32px;
    --cx-weight-normal: 400; --cx-weight-medium: 500; --cx-weight-semibold: 600;
    --cx-leading-tight: 1.2; --cx-leading-base: 1.55;
    --cx-tracking-tight: -.012em; --cx-tracking-micro: .08em;
    --cx-dur-instant: var(--t-0); --cx-dur-hover: var(--t-0);
    --cx-dur-state: var(--t-1); --cx-dur-panel: var(--t-2);
    --cx-dur-overlay: var(--t-2); --cx-dur-backdrop: var(--t-2);
    --cx-ease-standard: var(--ease-settle); --cx-ease-out: var(--ease-settle);
    --cx-tint-bg: .08; --cx-tint-bg-hover: .14; --cx-tint-bg-press: .18;
    --cx-tint-bg-quiet: .05; --cx-tint-border: .40; --cx-tint-border-loud: .70;
    --cx-tint-border-quiet: .20; --cx-tint-ring: .45;
    --cx-scrollbar-thumb: var(--line-3);
    --cx-scrollbar-thumb-hover: var(--text-4);
    --cx-z-ambient: 9; --cx-z-content: var(--z-content); --cx-z-hover: var(--z-hover);
    --cx-z-popover: var(--z-popover); --cx-z-overlay: var(--z-overlay);
    --cx-z-modal: var(--z-modal); --cx-z-modal-focused: var(--z-modal-focus);
    --cx-z-toast: var(--z-toast); --cx-z-dragdrop: var(--z-drag);
  }
}

/* ═══════════════════════════ 2. BASE ═══════════════════════════ */

@layer base {

  *, *::before, *::after { box-sizing: border-box; }

  html {
    height: 100%;
    overflow: hidden;
    overscroll-behavior: none;
    color-scheme: light dark;
    accent-color: var(--accent);
    background: var(--s-0);
    -webkit-text-size-adjust: 100%;
  }

  body {
    height: 100%;
    overflow: hidden;
    overscroll-behavior: none;
    margin: 0;
    background: var(--s-0);
    color: var(--text-1);
    font: 400 15px/1.65 var(--font-sans);
    letter-spacing: -.003em;
    text-wrap: pretty;
    overflow-wrap: break-word;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
  }

  /* Root never scrolls; panels own their scroll. */
  #app {
    height: 100dvh;
    height: calc(100dvh - var(--kb-inset));
    display: grid;
    overflow: hidden;
  }

  h1, h2, h3, h4, p, ul, ol, figure, pre, dl, dd { margin: 0; }
  ul, ol { padding: 0; list-style: none; }
  button, input, select, textarea { font: inherit; color: inherit; }
  button { background: none; border: 0; padding: 0; cursor: pointer; }
  img, svg, video, canvas { display: block; max-inline-size: 100%; }

  /* Brand artwork swap. The mark ships as two files; exactly one is ever
     shown. It lives in `base` because the element reset above it would
     otherwise re-display the hidden twin. */
  .cx-brandmark--dark { display: none; }
  :root[data-theme="dark"] .cx-brandmark--light { display: none; }
  :root[data-theme="dark"] .cx-brandmark--dark  { display: block; }
  @media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) .cx-brandmark--light { display: none; }
    :root:not([data-theme="light"]) .cx-brandmark--dark  { display: block; }
  }
  a { color: var(--accent); text-decoration: none; }
  hr { border: 0; border-top: 1px solid var(--line-1); margin: 0; }

  ::selection { background: var(--accent-tint-hover); color: var(--text-display); }

  /* Every counter, timer, size and id keeps tabular figures so digits never
     reflow. IBM Plex Mono's zero is slashed by default — it is left alone. */
  .num, .cx-mono, .mono, code, kbd, time, .cx-timer { font-variant-numeric: tabular-nums; }

  .cx-sr, .visually-hidden {
    position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
    overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0;
  }
  [hidden] { display: none !important; }
  /* The JS state class the chat and the panels toggle. It lived in the legacy
     sheet; it is owned here now so unlinking that sheet cannot strand it. */
  .hidden { display: none !important; }

  /* ── 2.1 Type. One class per token. No component sets its own font-size. ──
     Rule A — weight falls as size rises: 600 at 18–24, 500 at 32–44, never 700.
     Rule B — UPPERCASE is reserved for .t-11 mono micro-labels and therefore
     MEANS "this is chrome, not content". Plus Jakarta Sans is never uppercased,
     Title Case appears nowhere, prose is never mono and a path is never sans. */
  .t-11 { font: 500 11px/1.25 var(--font-mono); letter-spacing: .08em; text-transform: uppercase; }
  .t-12 { font: 400 12px/1.40 var(--font-sans); letter-spacing: .005em; }
  .t-13 { font: 400 13px/1.50 var(--font-sans); }
  .t-13-m { font: 500 13px/1.50 var(--font-sans); }
  .t-14 { font: 500 14px/1.55 var(--font-sans); }
  .t-15 { font: 400 15px/1.65 var(--font-sans); letter-spacing: -.003em; }
  .t-18 { font: 600 18px/1.35 var(--font-sans); letter-spacing: -.012em; }
  .t-24 { font: 600 24px/1.20 var(--font-sans); letter-spacing: -.022em; }
  .t-32 { font: 500 32px/1.12 var(--font-sans); letter-spacing: -.030em; }
  .t-44 { font: 500 44px/1.05 var(--font-sans); letter-spacing: -.035em; }
  .t-mono-12 { font: 400 12px/1.45 var(--font-mono); }
  .t-code-13 { font: 400 13px/1.60 var(--font-mono); }
  /* Fraunces, pinned. Three permitted uses, and no fourth: the first-run
     welcome, a panel empty-state headline, the post-publish milestone line. */
  .t-display {
    font-family: var(--font-display); font-weight: 500;
    font-variation-settings: "opsz" 144, "SOFT" 100, "WONK" 1;
  }

  .cx-ink       { color: var(--text-display); }
  .cx-text-2, .cx-muted { color: var(--text-2); }
  .cx-text-3, .cx-faint { color: var(--text-3); }

  /* Emphasis inside prose is a drawn 1px underline or the display ink —
     never bold, never italic, never recoloured to the accent. */
  .cx-prose { max-inline-size: 68ch; }
  .cx-prose p, .cx-prose li { max-inline-size: 68ch; }
  .cx-prose em { font-style: normal; color: var(--text-display); }
  .cx-prose strong { font-weight: 400; color: var(--text-display); }
  .cx-prose a, .cx-link {
    color: var(--accent);
    background-image: linear-gradient(currentColor, currentColor);
    background-repeat: no-repeat;
    background-position: 0 100%;
    background-size: 0 1px;
    transition: background-size var(--t-2) var(--ease-glide);
  }
  .cx-prose a:hover, .cx-link:hover { background-size: 100% 1px; }

  /* ── 2.2 Focus. Never a native outline, never suppressed without a
     replacement. --accent clears 3:1 on every surface in both themes. ── */
  /* A theme change swaps in ONE frame. It is never animated: the JS stamps
     data-theme-swap for a frame and every transition in the document is
     suppressed while the new tokens land. */
  html[data-theme-swap] *, html[data-theme-swap] *::before, html[data-theme-swap] *::after {
    transition: none !important;
    animation-play-state: paused !important;
  }

  :focus { outline: none; }
  :focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
    border-radius: inherit;
  }

  /* ── 2.3 Scrollbars: themed and thin, both syntaxes. ── */
  .cx-scroll { scrollbar-width: thin; scrollbar-color: var(--line-3) transparent; overscroll-behavior: contain; }
  .cx-scroll::-webkit-scrollbar { width: 8px; height: 8px; }
  .cx-scroll::-webkit-scrollbar-track { background: transparent; }
  .cx-scroll::-webkit-scrollbar-thumb {
    background: var(--line-3); border-radius: var(--r-full);
    border: 2px solid transparent; background-clip: content-box;
  }
  .cx-scroll::-webkit-scrollbar-thumb:hover { background: var(--text-4); background-clip: content-box; }
  .cx-scroll--silent { scrollbar-width: none; }
  .cx-scroll--silent::-webkit-scrollbar { display: none; }
  .cx-scroll--x { overflow-x: auto; overflow-y: hidden; }

  /* ── 2.4 The ambient wash.
     Floema's implied window, translated to CSS. One shell-owned element behind
     everything at z-index 9, drifting 40px over 90s. It is atmosphere, not
     animation: reduced motion parks it at its midpoint, never deletes it. It
     is the cheapest source of warmth in the product. ── */
  .cx-wash {
    position: fixed;
    inset-block-start: -8dvh;
    inset-inline-start: calc(var(--rail-w) - 6vw);
    inline-size: min(82vw, 1120px);
    block-size: min(72dvh, 840px);
    /* The 90px blur is what makes the field read as light rather than as a
       drawn shape: at 8 bits a bare radial ramp over 400px bands into visible
       rings. The blur costs the peak, which is why --wash-opacity is high —
       the two numbers are a pair and neither is meaningful alone. */
    filter: blur(90px);
    pointer-events: none;
    z-index: 9;
    background: var(--wash);
    opacity: var(--wash-opacity);
    will-change: transform;
    animation: cx-drift var(--t-amb) var(--ease-travel) infinite alternate;
  }
  @keyframes cx-drift {
    from { transform: translate3d(0, 0, 0); }
    to   { transform: translate3d(28px, -30px, 0); }
  }

  /* ── 2.5 THE FOUR ANIMATIONS.
     Stage 4 closed the catalogue. This product runs exactly four @keyframes
     and no others. Everything else that moves is a property-scoped
     transition. A fifth keyframe is a defect.

       cx-enter    the ONE shared entrance, parameterised by --enter-x,
                   --enter-y and --enter-s. A menu, a message, a toast, a
                   chip, a pane and a bottom sheet are the same motion at
                   different amplitudes, which is why they feel like one
                   product rather than six components.
       cx-travel   the working sweep. There is no spinner in this product.
       cx-breathe  the working dot.
       cx-drift    the ambient wash.

     No shimmer, no spin, no bounce, no pulse, no skeleton, no indeterminate
     bar — none of them exist in any file.

     Why opacity lands at 64% of the run: 64% of --t-2 is 140ms, which IS
     M-08's "opacity --t-1, translate --t-2" split, generalised. The ink is
     fully settled before the object stops moving, so type never paints
     mid-travel and nothing ever reads blurred. ── */
  /* The amplitudes are reset on EVERY element, not inherited. A custom
     property inherits by default, so without this a message inside a chat
     column that set --enter-y would silently enter at the column's amplitude
     instead of its own. This universal reset is the no-build-step equivalent
     of `@property { inherits: false }` and it is three declarations wide. */
  *, *::before, *::after {
    --enter-x: 0px;
    --enter-y: 8px;
    --enter-s: 1;
  }
  @keyframes cx-enter {
    from {
      opacity: 0;
      transform: translate3d(var(--enter-x), var(--enter-y), 0) scale(var(--enter-s));
    }
    64% { opacity: 1; }
    to {
      opacity: 1;
      transform: translate3d(0, 0, 0) scale(1);
    }
  }

  /* The travelling aurora. One 240px segment, masked at both ends, crossing
     a 2px rule on one compositor layer: zero layout, zero paint per frame. */
  @keyframes cx-travel {
    0%   { transform: translate3d(-260px, 0, 0); }
    80%  { transform: translate3d(100cqw, 0, 0); }
    100% { transform: translate3d(100cqw, 0, 0); }  /* the 400ms gap between passes */
  }
  @keyframes cx-breathe {
    from { opacity: .35; }
    to   { opacity: 1; }
  }

  /* ── 2.6 REDUCED MOTION — the full pass.
     Four laws, and they are the reason this block is not simply
     "animation: none". Nothing here is disabled into a jump.

       1. EVERY STATE CHANGE STILL OCCURS. Panes swap, drawers open, menus
          open, the rail collapses, the sheet rises — every transition still
          runs, at 1ms. Nothing anywhere becomes unreachable.
       2. EVERY TRANSLATE ENTRANCE DEGRADES TO OPACITY ONLY. Because the one
          shared entrance is parameterised, this is three declarations: the
          amplitudes go to zero and cx-enter becomes a pure 90ms crossfade.
          An INSTANT opacity flip reads as flicker, which is why 90ms and not
          1ms — the same reason the transition exceptions below keep 90ms.
       3. EVERY INFINITE ANIMATION BECOMES ITS STATIC END STATE. The sweep
          becomes the full rule filled with the aurora at 55%; the breathing
          dot holds lit; the wash parks at the midpoint of its drift. The
          wash is NEVER removed — it is atmosphere, not animation, and it is
          the cheapest warmth in the product.
       4. THE ELAPSED TIMER KEEPS COUNTING. It is owned by JS, it is
          information rather than decoration, and nothing here touches it. ── */
  @media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
      /* law 2 — !important because a component's own --enter-y outranks a
         universal selector on specificity, and here the reader wins */
      --enter-x: 0px !important;
      --enter-y: 0px !important;
      --enter-s: 1 !important;
      animation-duration: 90ms !important;
      animation-iteration-count: 1 !important;
      transition-duration: .001ms !important;
      scroll-behavior: auto !important;
    }
    /* an instant opacity flip reads as flicker, so these keep 90ms */
    .cx-fade, .cx-menu, .cx-toast, .cx-plate, .cx-drawer, .cx-modal { transition-duration: 90ms !important; }

    /* law 3 — the static end states, one per infinite animation */
    .cx-wash { animation: none !important; transform: translate3d(14px, -15px, 0); }
    .cx-dot--breathe { animation: none !important; opacity: 1; }
    .cx-rule__sweep,
    .cx-hist__row[data-busy="1"]::after,
    .cr-preview[data-working="1"]::after {
      animation: none !important;
      transform: none !important;
      inline-size: 100% !important;
      -webkit-mask-image: none !important;
      mask-image: none !important;
      opacity: .55;
    }
  }
}

/* ═══════════════════════════ 3. COMPONENTS ═══════════════════════════ */

@layer components {

  /* Ghost reset — same reason as the shell layer's. */
  .cx-btn, .cx-iconbtn, .cx-input, .cx-field, .cx-form, .cx-form__actions,
  .cx-label, .cx-field-hint, .cx-field-error, .cx-panel, .cx-panel__header,
  .cx-panel__body, .cx-card, .cx-row, .cx-menu, .cx-menu__group, .cx-menu-item,
  .cx-pill, .cx-chip, .cx-dot, .cx-code, .cx-empty, .cx-empty__title,
  .cx-empty__hint, .cx-empty__name, .cx-notice, .cx-toast, .cx-modal, .cx-modal__panel,
  .cx-drawer, .cx-microlabel, .cx-heading, .cx-subheading, .cx-loading,
  .cx-segment, .cx-segment__half, .cx-switch, .cx-group, .cx-group__rule,
  .cx-group__label, .cx-rule {
    margin: 0; padding: 0; inset: auto; box-shadow: none; float: none;
    min-inline-size: 0; text-transform: none; letter-spacing: normal;
  }


  /* ── 3.1 Buttons. One formula, three variants. There is NO solid filled
     button in this product. Heights sit on the 4px grid. ── */
  .cx-btn, .cx-btn--md {
    display: inline-flex; align-items: center; justify-content: center;
    gap: var(--sp-3);
    block-size: var(--h-md); min-block-size: var(--h-md);
    padding-inline: var(--sp-5);
    border: 1px solid var(--line-2);
    border-radius: var(--r-2);
    background: var(--s-2);
    color: var(--text-1);
    font: 500 14px/1 var(--font-sans);
    white-space: nowrap;
    box-shadow: var(--lit);
    transition: background-color var(--t-0) var(--ease-settle),
                border-color var(--t-0) var(--ease-settle),
                color var(--t-0) var(--ease-settle),
                transform var(--t-0) var(--ease-settle);
  }
  .cx-btn:hover { background: var(--s-3); border-color: var(--line-3); }
  /* press compresses harder than hover expands, so an object feels squeezable */
  .cx-btn:active { transform: scale(.98); }
  .cx-btn svg { inline-size: var(--icon-sm); block-size: var(--icon-sm); }

  .cx-btn--xs { block-size: var(--h-xs); min-block-size: var(--h-xs); padding-inline: var(--sp-4); font-size: 12px; }
  .cx-btn--sm { block-size: var(--h-sm); min-block-size: var(--h-sm); padding-inline: var(--sp-5); font-size: 13px; }
  .cx-btn--lg { block-size: var(--h-lg); min-block-size: var(--h-lg); padding-inline: var(--sp-6); }
  /* 44px is the floor on a coarse pointer at every viewport — the hit box
     grows, the type does not. */
  @media (pointer: coarse) {
    .cx-btn { min-block-size: var(--tap); }
  }
  .cx-btn--block { inline-size: 100%; }

  .cx-btn--accent {
    background: var(--accent-tint);
    border-color: rgba(var(--accent-rgb), .40);
    color: var(--accent-strong);
  }
  .cx-btn--accent:hover {
    background: var(--accent-tint-hover);
    border-color: rgba(var(--accent-rgb), .70);
  }

  .cx-btn--danger {
    background: var(--danger-tint);
    border-color: color-mix(in srgb, var(--danger) 40%, transparent);
    color: var(--danger-strong);
  }
  .cx-btn--danger:hover { border-color: color-mix(in srgb, var(--danger) 70%, transparent); }

  .cx-btn--quiet {
    background: transparent; border-color: transparent; color: var(--text-2);
    box-shadow: none;
  }
  .cx-btn--quiet:hover { background: var(--s-2); border-color: var(--line-2); color: var(--text-1); }

  .cx-btn[disabled], .cx-btn[aria-disabled="true"] {
    opacity: .45; cursor: not-allowed; transform: none;
  }
  .cx-btn[aria-busy="true"] { opacity: .75; pointer-events: none; }

  /* ── 3.2 Icon button. Rest is bare: no border, no surface. ── */
  .cx-iconbtn {
    position: relative;
    display: inline-flex; align-items: center; justify-content: center;
    inline-size: var(--h-md); block-size: var(--h-md);
    border: 1px solid transparent;
    border-radius: var(--r-2);
    background: transparent;
    color: var(--text-3);
    transition: background-color var(--t-0) var(--ease-settle),
                border-color var(--t-0) var(--ease-settle),
                color var(--t-0) var(--ease-settle),
                transform var(--t-0) var(--ease-settle);
  }
  .cx-iconbtn svg { inline-size: var(--icon-ui); block-size: var(--icon-ui); }
  .cx-iconbtn:hover { color: var(--text-1); background: var(--s-2); border-color: var(--line-2); }
  .cx-iconbtn:active { transform: scale(.94); }
  .cx-iconbtn--danger:hover { color: var(--danger); border-color: color-mix(in srgb, var(--danger) 40%, transparent); }
  .cx-iconbtn[disabled], .cx-iconbtn[aria-disabled="true"] { opacity: .45; cursor: not-allowed; }
  /* The hit box is padded out to 44px; the visual box never inflates.
     Under a coarse pointer EVERY icon button gets it, with or without the
     modifier — a 36px target is not reachable with a thumb. */
  .cx-iconbtn--touch::after,
  .cx-iconbtn::after {
    content: ""; position: absolute; inset: 50% auto auto 50%;
    inline-size: var(--tap); block-size: var(--tap);
    translate: -50% -50%;
  }
  @media (pointer: fine) {
    .cx-iconbtn:not(.cx-iconbtn--touch)::after { content: none; }
  }

  /* ── 3.2b M-31 — THE DIRECTIONAL GLYPH.
     An arrow, a chevron or an external-link mark travels 3px ALONG ITS OWN
     AXIS on hover. Only the glyph moves: the label beside it is perfectly
     still, because type that shifts under the cursor reads cheap.

     Every glyph names itself with data-icon (see icon() in app.js), so this
     is owned entirely by the stylesheet — no call site has to opt in, and a
     glyph added next year gets the behaviour for free. Non-directional
     glyphs are absent from this list on purpose: a folder that slides is
     noise, and the move only means something when the glyph points. ── */
  svg[data-icon="external"], svg[data-icon="download"], svg[data-icon="upload"],
  svg[data-icon="send"], svg[data-icon="chevRight"], svg[data-icon="chevLeft"],
  svg[data-icon="chevUp"], svg[data-icon="closeBack"],
  svg[data-icon="jumpDown"] {
    transition: transform var(--t-1) var(--ease-settle);
  }
  /* the trigger is any hovered control, so a link, a button and an icon
     button all behave the same way */
  :is(a, button, .cx-row, .cx-menu-item):hover svg[data-icon="external"],
  :is(a, button):hover svg[data-icon="send"] { transform: translate(3px, -3px); }
  :is(a, button, .cx-row, .cx-menu-item):hover svg[data-icon="download"],
  :is(a, button):hover svg[data-icon="jumpDown"] { transform: translateY(3px); }
  :is(a, button, .cx-row, .cx-menu-item):hover svg[data-icon="upload"],
  :is(a, button):hover svg[data-icon="chevUp"] { transform: translateY(-3px); }
  :is(a, button, .cx-row, .cx-menu-item):hover svg[data-icon="chevRight"] { transform: translateX(3px); }
  :is(a, button):hover svg[data-icon="chevLeft"],
  :is(a, button):hover svg[data-icon="closeBack"] { transform: translateX(-3px); }
  /* The explicit per-call-site hooks are gone with the publish pill's visit
     link, which was their last application: the data-icon rules above are the
     one expression of M-31 now, and every directional glyph gets it without
     its call site knowing. */

  /* ── 3.3 Inputs and fields ── */
  .cx-input, textarea.cx-input, select.cx-input {
    inline-size: 100%;
    block-size: var(--h-md); min-block-size: var(--h-md);
    padding: 0 var(--sp-5);
    border: 1px solid var(--line-2);
    border-radius: var(--r-2);
    background: var(--s-2);
    color: var(--text-1);
    font: 500 14px/1.55 var(--font-sans);
    box-shadow: var(--lit);
    transition: border-color var(--t-0) var(--ease-settle),
                background-color var(--t-0) var(--ease-settle);
  }
  textarea.cx-input { block-size: auto; padding: var(--sp-4) var(--sp-5); resize: vertical; line-height: 1.6; }
  select.cx-input { padding-inline-end: var(--sp-6); }
  .cx-input:hover { border-color: var(--line-3); }
  .cx-input:focus { border-color: var(--accent); background: var(--s-3); }
  /* a placeholder is --text-3, never --text-4: --text-4 may not carry information */
  .cx-input::placeholder { color: var(--text-3); }
  .cx-input[aria-invalid="true"] { border-color: var(--danger); }
  .cx-input--mono { font: 400 12px/1.45 var(--font-mono); }

  .cx-field { display: flex; flex-direction: column; gap: var(--sp-4); }
  .cx-label {
    font: 500 11px/1.25 var(--font-mono); letter-spacing: .08em;
    text-transform: uppercase; color: var(--text-3);
  }
  .cx-field-hint { font: 400 12px/1.40 var(--font-sans); letter-spacing: .005em; color: var(--text-3); }
  .cx-field-error { font: 400 12px/1.40 var(--font-sans); color: var(--danger); }
  .cx-field-error:empty { display: none; }
  .cx-form { display: flex; flex-direction: column; gap: var(--pad-section); }
  .cx-form__actions { display: flex; align-items: center; gap: var(--sp-5); flex-wrap: wrap; }

  /* ── 3.4 Segmented control. A native <select> cannot be themed across both
     themes, and a binary load-bearing choice deserves both options visible. ── */
  .cx-segment {
    display: inline-flex; align-self: flex-start; padding: 3px; gap: 2px;
    border: 1px solid var(--line-2); border-radius: var(--r-2);
    background: var(--s-inset); box-shadow: var(--well);
  }
  .cx-segment__half {
    display: inline-flex; align-items: center; justify-content: center; gap: var(--sp-3);
    min-inline-size: 96px; block-size: var(--h-md);
    padding-inline: var(--sp-5);
    border-radius: 6px;
    color: var(--text-2);
    font: 500 14px/1 var(--font-sans);
    transition: background-color var(--t-0) var(--ease-settle),
                color var(--t-0) var(--ease-settle);
  }
  .cx-segment__half:hover { color: var(--text-1); }
  .cx-segment__half[aria-pressed="true"] {
    background: var(--s-3); color: var(--text-display);
    box-shadow: var(--lit); border: 1px solid var(--line-2);
  }
  .cx-segment__half svg { inline-size: var(--icon-sm); block-size: var(--icon-sm); }

  /* ── 3.5 Pills, dots, chips ── */
  .cx-pill {
    display: inline-flex; align-items: center; gap: var(--sp-3);
    block-size: 22px; padding-inline: var(--sp-4);
    border: 1px solid var(--line-2); border-radius: var(--r-full);
    background: var(--s-2); color: var(--text-2);
    font: 500 11px/1 var(--font-mono); letter-spacing: .08em; text-transform: uppercase;
  }
  .cx-pill--accent  { border-color: rgba(var(--accent-rgb), .40); background: var(--accent-tint); color: var(--accent-strong); }
  .cx-pill--success { border-color: color-mix(in srgb, var(--ok) 40%, transparent);     background: var(--ok-tint);     color: var(--ok-strong); }
  .cx-pill--warn    { border-color: color-mix(in srgb, var(--warn) 40%, transparent);   background: var(--warn-tint);   color: var(--warn-strong); }
  .cx-pill--danger  { border-color: color-mix(in srgb, var(--danger) 40%, transparent); background: var(--danger-tint); color: var(--danger-strong); }
  .cx-pill--garnish { border-color: rgba(var(--accent-rgb), .40); background: var(--accent-tint); color: var(--accent-strong); }

  /* Binary state gets a bare 6px dot, never a pill. */
  .cx-dot { inline-size: 6px; block-size: 6px; border-radius: var(--r-full); background: var(--text-4); flex: none; }
  .cx-dot--accent { background: var(--accent); }
  .cx-dot--live { background: var(--ok); }
  .cx-dot--error { background: var(--danger); }
  .cx-dot--unsaved { background: var(--warn); }
  .cx-dot--idle { background: var(--text-4); }
  .cx-dot--breathe { animation: cx-breathe 1400ms var(--ease-travel) infinite alternate; }

  .cx-chip {
    display: inline-flex; align-items: center; gap: var(--sp-3);
    block-size: var(--h-xs); padding-inline: var(--sp-4);
    border: 1px solid var(--line-2); border-radius: var(--r-1);
    background: var(--s-2); color: var(--text-2);
    font: 400 12px/1 var(--font-mono);
    max-inline-size: 240px;
  }
  .cx-chip__text { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .cx-chip svg { inline-size: var(--icon-chip); block-size: var(--icon-chip); }

  /* ── 3.6 Grouping is a rule and a label, never a box.
     No boxed group headers, no card inside a card, no nested borders. ── */
  .cx-group { display: block; }
  .cx-group + .cx-group { margin-block-start: var(--pad-section); }
  .cx-group__rule { block-size: 1px; background: var(--line-1); inline-size: 100%; }
  .cx-microlabel, .cx-group__label {
    display: block;
    margin-block-start: var(--sp-4);
    font: 500 11px/1.25 var(--font-mono); letter-spacing: .08em;
    text-transform: uppercase; color: var(--text-3);
  }
  .cx-group__body { margin-block-start: var(--sp-5); }

  .cx-heading, .cx-subheading {
    font: 600 18px/1.35 var(--font-sans); letter-spacing: -.012em; color: var(--text-display);
  }
  .cx-divider { block-size: 1px; background: var(--line-1); border: 0; }

  /* ── 3.7 Rows. A card never has a shadow; a row lifts by one rung. ── */
  .cx-row {
    position: relative;
    display: flex; align-items: center; gap: var(--sp-5);
    min-block-size: var(--h-lg);
    padding: var(--sp-4) var(--sp-5);
    border-radius: var(--r-2);
    color: var(--text-1);
    transition: background-color var(--t-0) var(--ease-settle);
  }
  .cx-row::before {
    content: ""; position: absolute; inset-block: 4px; inset-inline-start: 0;
    inline-size: 2px; border-radius: 2px;
    background: var(--aurora);
    transform: scaleX(0); transform-origin: left center;
    transition: transform var(--t-1) var(--ease-settle);
  }
  .cx-row:hover { background: var(--s-2); }
  .cx-row:hover::before { transform: scaleX(1); }
  .cx-row__label { color: var(--text-2); font: 400 13px/1.5 var(--font-sans); }
  .cx-row__value { color: var(--text-1); font: 400 12px/1.45 var(--font-mono); }

  /* ── 3.8 Panels and cards. Elevation is a rung on the ladder plus one
     1px edge plus --lit. Never a blurred shadow on in-flow content. ── */
  .cx-panel, .cx-card {
    border: 1px solid var(--line-2);
    border-radius: var(--r-3);
    background: var(--s-2);
    box-shadow: var(--lit);
  }
  .cx-panel__header {
    display: flex; align-items: center; justify-content: space-between; gap: var(--sp-5);
    padding: var(--sp-5) var(--pad-panel);
    border-block-end: 1px solid var(--line-1);
  }
  .cx-panel__body { padding: var(--pad-panel); }
  .cx-pane { min-inline-size: 0; min-block-size: 0; }

  /* ── 3.9 Code. A recess, with the label-under-rule furniture above it. ── */
  .cx-code {
    padding: var(--sp-5);
    border-radius: var(--r-2);
    background: var(--s-inset);
    box-shadow: var(--well);
    color: var(--text-1);
    font: 400 13px/1.60 var(--font-mono);
    overflow-x: auto;
    white-space: pre;
  }
  /* ── 3.10 Menus and popovers. --s-3, one of exactly two blurred shadows.
     Held invisible until clamped so they never flash at the wrong position.
     NEVER scaled: scale fights translate and produces sub-pixel jitter. This
     is a recorded failure, not a preference. ── */
  .cx-menu {
    position: fixed;
    /* R5-09 — popover-top (55), not popover (30): this menu is regularly
       spawned by hosts that already sit high (the selector over the mobile
       overlay rail at 40, rows inside drawers/modals), and a popover below
       its host is unreachable. See the z ladder for the ordering contract. */
    z-index: var(--z-popover-top);
    inline-size: min(320px, 92vw);
    max-block-size: min(560px, 72dvh);
    padding: var(--sp-3);
    border: 1px solid var(--line-2);
    border-radius: var(--r-3);
    background: var(--s-3);
    box-shadow: var(--shadow-pop);
    overflow-y: auto;
    overscroll-behavior: contain;
    scrollbar-width: thin; scrollbar-color: var(--line-3) transparent;
    /* M-08 — opacity over --t-1, translateY(-6px) over --t-2, expressed as
       one shared entrance whose opacity ramp completes at 64%. Close is
       opacity only: the JS removes the node, it never plays this in reverse.
       A menu is NEVER scaled — scale fights translate and produces sub-pixel
       jitter. That is a recorded failure, not a preference. */
    --enter-y: -6px;
    animation: cx-enter var(--t-2) var(--ease-settle) backwards;
  }
  .cx-menu[data-placing="true"] { opacity: 0; pointer-events: none; }
  .cx-menu__group + .cx-menu__group { margin-block-start: var(--sp-5); }
  .cx-menu__group > .cx-microlabel { padding-inline: var(--sp-4); }
  .cx-menu__group > .cx-microlabel::before {
    content: ""; display: block;
    margin: 0 calc(var(--sp-4) * -1) var(--sp-4);
    block-size: 1px; background: var(--line-1);
  }
  /* AA on the highest surface: #8B8479 measures 4.40 on --s-3, so the popover
     raises its own tertiary ink one step. The token itself is unchanged. */
  @media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) .cx-menu { --text-3: #9A9387; }
  }
  :root[data-theme="dark"] .cx-menu { --text-3: #9A9387; }

  .cx-menu__note {
    padding: var(--sp-4);
    font: 400 13px/1.5 var(--font-sans); color: var(--text-3);
  }
  .cx-menu-item {
    position: relative;
    display: flex; align-items: center; gap: var(--sp-4);
    inline-size: 100%; min-block-size: var(--tap);
    padding: var(--sp-4);
    border-radius: var(--r-2);
    color: var(--text-1);
    text-align: start;
    font: 500 13px/1.5 var(--font-sans);
    transition: background-color var(--t-0) var(--ease-settle), color var(--t-0) var(--ease-settle);
  }
  .cx-menu-item::before {
    content: ""; position: absolute; inset-block: 6px; inset-inline-start: 0;
    inline-size: 2px; border-radius: 2px; background: var(--aurora);
    transform: scaleX(0); transform-origin: left center;
    transition: transform var(--t-1) var(--ease-settle);
  }
  .cx-menu-item:hover { background: var(--s-2); }
  .cx-menu-item:hover::before { transform: scaleX(1); }
  .cx-menu-item[aria-selected="true"] { color: var(--accent-strong); }
  .cx-menu-item--danger { color: var(--danger); }
  .cx-menu-item--danger:hover { background: var(--danger-tint); }
  .cx-menu-item__text { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .cx-menu-item svg { inline-size: var(--icon-sm); block-size: var(--icon-sm); flex: none; }
  @media (pointer: fine) {
    .cx-menu-item { min-block-size: var(--h-md); }
  }

  /* ── 3.11 Drawers. Right-anchored, always: they enter from the right edge
     and the close control is a RIGHT-pointing chevron aimed at that same
     edge — never a close cross, never a leftward glyph on a rightward panel.
     This block is the ONE expression of that rightness (inset-inline-end + a
     positive translateX); nothing downstream restates it. ── */
  .cx-drawer {
    position: fixed; inset-block: 0; inset-inline-end: 0;
    inline-size: var(--drawer-w);
    z-index: var(--z-overlay);
    display: flex; flex-direction: column;
    border-inline-start: 1px solid var(--line-2);
    background: var(--s-1);
    transform: translateX(100%);
    transition: transform var(--t-3) var(--ease-glide);
  }
  .cx-drawer[data-open="true"] { transform: translateX(0); }
  .cx-drawer__header {
    flex: none; display: flex; align-items: center; gap: var(--sp-5);
    min-block-size: calc(var(--topbar-h) + var(--safe-t));
    padding-inline: var(--pad-panel);
    /* notched phones: the drawer is inset-block:0, so its header would sit
       under the status bar / camera cutout without this inset */
    padding-block-start: var(--safe-t);
    border-block-end: 1px solid var(--line-2);
  }
  /* The twin of the header's --safe-t: the drawer is inset-block:0, so its
     last row would sit under the home indicator without this. */
  .cx-drawer__body {
    flex: 1 1 auto; min-block-size: 0; overflow-y: auto;
    padding: var(--pad-panel);
    padding-block-end: calc(var(--pad-panel) + var(--safe-b));
  }

  /* ── 3.12 Modals ── */
  .cx-scrim, .cx-modal {
    position: fixed; inset: 0; z-index: var(--z-modal);
    display: flex; align-items: center; justify-content: center;
    padding: var(--pad-section);
    background: var(--scrim);
    -webkit-backdrop-filter: blur(10px);
    backdrop-filter: blur(10px);
    /* M-34 / M-33 — the backdrop is opacity only; nothing about it travels.
       This is THE modal backdrop component: the modal wears it, and a drawer
       (which is a modal that arrives from the edge) wears the same one. That
       is why the second and last permitted backdrop-filter in the product is
       here and not duplicated. */
    --enter-y: 0px;
    animation: cx-enter var(--t-2) var(--ease-settle) backwards;
  }
  /* A drawer sits at --z-overlay, so its backdrop must sit one below it —
     not at --z-modal, which would paint the scrim OVER the drawer it belongs
     to. The modal keeps the modal rung. */
  .cx-scrim { z-index: calc(var(--z-overlay) - 1); }
  /* The scrim ACCEPTS the product's own reference drag (C1/C2): while one is
     over it, it says so once, in the centre, in the microlabel voice — and
     otherwise it is the same silent backdrop. Gesture-scoped: the class is
     added on dragenter and gone on drop or dragend, so nothing here is ever
     furniture. */
  .cx-scrim__drop {
    display: none;
    padding: var(--sp-4) var(--sp-6);
    border: 1px solid rgba(var(--accent-rgb), .45); border-radius: var(--r-full);
    background: var(--s-1);
    font: 500 11px/1.25 var(--font-mono); letter-spacing: .08em; text-transform: uppercase;
    color: var(--accent-strong);
    pointer-events: none;
  }
  .cx-scrim--drop {
    background: color-mix(in srgb, var(--accent) 12%, var(--scrim));
    box-shadow: inset 0 0 0 2px rgba(var(--accent-rgb), .40);
  }
  .cx-scrim--drop .cx-scrim__drop { display: inline-flex; }
  .cx-modal__panel {
    inline-size: min(520px, 100%);
    max-block-size: 86dvh;
    display: flex; flex-direction: column;
    border: 1px solid var(--line-2);
    border-radius: var(--r-4);
    background: var(--s-3);
    box-shadow: var(--shadow-modal);
    /* M-34 — the panel rises 8px. It is never scaled. */
    animation: cx-enter var(--t-3) var(--ease-glide) backwards;
  }
  .cx-modal__header { padding: var(--pad-panel); border-block-end: 1px solid var(--line-1); }
  .cx-modal__header h2, .cx-modal__header .cx-heading {
    font: 600 18px/1.35 var(--font-sans); letter-spacing: -.012em; color: var(--text-display);
  }
  .cx-modal__body { padding: var(--pad-panel); overflow-y: auto; color: var(--text-2); font: 400 14px/1.6 var(--font-sans); }
  .cx-modal__footer {
    display: flex; justify-content: flex-end; gap: var(--sp-5);
    padding: var(--pad-panel); border-block-start: 1px solid var(--line-1);
  }
  /* A destructive confirm enumerates what is lost. Never "Cancel / Delete". */
  .cx-losslist { display: flex; flex-direction: column; gap: var(--sp-4); margin-block-start: var(--sp-5); }
  .cx-losslist li {
    position: relative; padding-inline-start: var(--sp-6);
    font: 400 13px/1.5 var(--font-sans); color: var(--text-1);
  }
  .cx-losslist li::before {
    content: ""; position: absolute; inset-inline-start: 0; inset-block-start: .55em;
    inline-size: 6px; block-size: 6px; border-radius: var(--r-full); background: var(--danger);
  }

  /* ── 3.13 Toast. One pill, bottom-centre, 1.9s, never stacked, no dismiss
     control. Everything contextual is an inline prose error instead. ── */
  .cx-toast {
    position: fixed;
    inset-block-end: calc(var(--sp-7) + var(--safe-b));
    inset-inline: 0;
    margin-inline: auto;
    inline-size: fit-content; max-inline-size: min(420px, 92vw);
    z-index: var(--z-toast);
    padding: var(--sp-5) var(--sp-6);
    border: 1px solid var(--line-2); border-radius: var(--r-full);
    background: var(--s-3); color: var(--text-1);
    box-shadow: var(--shadow-pop);
    font: 500 13px/1.5 var(--font-sans);
    /* M-35 — in on opacity + translateY(8px); out is opacity only, and the
       JS clears it at 1.9s. One pill, never stacked, no dismiss control. */
    animation: cx-enter var(--t-1) var(--ease-settle) backwards;
  }
  .cx-toast--error { border-color: color-mix(in srgb, var(--danger) 40%, transparent); color: var(--danger); }
  .cx-toast--success { border-color: color-mix(in srgb, var(--ok) 40%, transparent); color: var(--ok); }
  /* The third tone: not a failure, but the thing asked for cannot happen yet
     (a drawer deep link with no project). --warn-strong is the on-ground
     value of this hue, which is what a toast sits on. */
  .cx-toast--warn { border-color: color-mix(in srgb, var(--warn) 40%, transparent); color: var(--warn-strong); }

  /* ── 3.14 Inline notice. The contextual error, worded as an instruction,
     rendered where it happened. Never a toast, never a code. ── */
  .cx-notice {
    display: flex; align-items: flex-start; gap: var(--sp-4);
    padding: var(--sp-5);
    border: 1px solid var(--line-2); border-radius: var(--r-2);
    background: var(--s-2); color: var(--text-1);
    font: 400 13px/1.5 var(--font-sans);
  }
  .cx-notice svg { inline-size: var(--icon-sm); block-size: var(--icon-sm); flex: none; margin-block-start: 1px; }
  .cx-notice--danger { border-color: color-mix(in srgb, var(--danger) 40%, transparent); background: var(--danger-tint); color: var(--danger-strong); }
  .cx-notice--warn   { border-color: color-mix(in srgb, var(--warn) 40%, transparent);   background: var(--warn-tint);   color: var(--warn-strong); }
  .cx-notice--ok     { border-color: color-mix(in srgb, var(--ok) 40%, transparent);     background: var(--ok-tint);     color: var(--ok-strong); }
  /* the cap notice: the accent tone, because a rate limit is information the
     product is telling you about itself, not a failure you caused */
  .cx-notice--accent { border-color: rgba(var(--accent-rgb), .35); background: var(--accent-tint); color: var(--accent-strong); }
  .cx-notice--note   { border-color: var(--line-2); background: var(--s-2); color: var(--text-2); }
  .cx-notice--note svg { color: var(--text-3); }
  .cx-notice__text { flex: 1 1 auto; min-inline-size: 0; }
  /* M-18 — a notice arrives; it never slides away under the cursor. */
  .cx-notice { --enter-y: 4px; animation: cx-enter var(--t-2) var(--ease-settle) backwards; }

  /* ── 3.15 The loading vocabulary. There is NO spinner, NO skeleton and NO
     shimmer in this product. A pending state is a breathing dot, a mono
     micro-label and — where a run is timed — a tabular counter. ── */
  .cx-loading {
    display: inline-flex; align-items: center; gap: var(--sp-4);
    color: var(--text-3);
    font: 500 11px/1.25 var(--font-mono); letter-spacing: .08em; text-transform: uppercase;
  }
  .cx-timer { font: 400 12px/1.45 var(--font-mono); color: var(--text-3); font-variant-numeric: tabular-nums; }

  /* The working rule: a 2px lane, present at all times, that the aurora
     crosses while CosmixAgent works. */
  .cx-rule {
    position: relative; flex: none;
    block-size: var(--rule-h);
    background: var(--line-2);
    opacity: .6;
    overflow: hidden;
    transition: opacity var(--t-1) var(--ease-settle), background-color var(--t-1) var(--ease-settle);
  }
  .cx-rule[data-working="1"] { background: var(--line-3); opacity: 1; }
  .cx-rule__sweep {
    position: absolute; inset-block: 0; inset-inline-start: 0;
    inline-size: 240px;
    background: var(--aurora-rule);
    -webkit-mask-image: linear-gradient(90deg, transparent, #000 22%, #000 78%, transparent);
    mask-image: linear-gradient(90deg, transparent, #000 22%, #000 78%, transparent);
    opacity: 0;
  }
  /* will-change is promised ONLY while the sweep is actually running. A
     permanent promise is a permanent compositor layer on a rule that is idle
     99% of the time, and the heap budget is not a rounding error. */
  .cx-rule[data-working="1"] .cx-rule__sweep {
    opacity: 1;
    will-change: transform;
    animation: cx-travel 2000ms var(--ease-travel) infinite;
  }

  /* ── 3.16 Empty states. A Fraunces headline, one sentence, one button,
     nothing else — sitting at ~40% height, not true centre. ── */
  .cx-empty {
    display: flex; flex-direction: column; align-items: flex-start; gap: var(--sp-5);
    max-inline-size: 460px;
    padding: var(--pad-section);
    margin-block: 12% auto;
  }
  .cx-empty__title {
    font-family: var(--font-display); font-weight: 500;
    font-variation-settings: "opsz" 144, "SOFT" 100, "WONK" 1;
    font-size: 32px; line-height: 1.12; letter-spacing: -.030em;
    color: var(--text-display);
  }
  .cx-empty__hint { font: 500 14px/1.55 var(--font-sans); color: var(--text-2); max-inline-size: 46ch; }
  /* The same centred frame used for a POPULATED identity (an Office document
     that has no in-browser renderer). It is not an empty state, so it does NOT
     take Fraunces — Fraunces has exactly three uses (AD §3.5) — and a filename
     is machine-adjacent, so it is mono (AD §3.4). */
  .cx-empty__name {
    font: 600 18px/1.35 var(--font-mono); letter-spacing: -.012em;
    color: var(--text-1); overflow-wrap: anywhere;
  }

  /* ── 3.17 Switch ── */
  .cx-switch { display: inline-flex; align-items: center; gap: var(--sp-5); }
  .cx-switch__track {
    position: relative; inline-size: 40px; block-size: 22px; flex: none;
    border: 1px solid var(--line-2); border-radius: var(--r-full);
    background: var(--s-inset); box-shadow: var(--well);
    transition: background-color var(--t-1) var(--ease-settle), border-color var(--t-1) var(--ease-settle);
  }
  .cx-switch__track::after {
    content: ""; position: absolute; inset-block-start: 3px; inset-inline-start: 3px;
    inline-size: 14px; block-size: 14px; border-radius: var(--r-full);
    background: var(--s-3); border: 1px solid var(--line-3); box-shadow: var(--lit);
    /* M-32 — the knob travels; the track only changes colour. */
    transition: transform var(--t-1) var(--ease-settle),
                background-color var(--t-1) var(--ease-settle),
                border-color var(--t-1) var(--ease-settle);
  }
  .cx-switch input:checked + .cx-switch__track { background: var(--accent-tint); border-color: rgba(var(--accent-rgb), .70); }
  /* On, the KNOB carries the accent — a 14px dot, not a surface. It is the
     cheapest way to make the state legible in both themes without tinting
     anything larger. §9.10 and §9.17 carry the identical geometry, so the
     product has exactly ONE switch wherever it appears. */
  .cx-switch input:checked + .cx-switch__track::after {
    transform: translateX(18px);
    background: var(--accent); border-color: transparent;
  }
}

/* ═══════════════════════════ 4. SHELL ═══════════════════════════ */

@layer shell {

  /* Ghost reset. The two legacy sheets still sit in the `legacy` layer and
     carry padding, insets and shadows for these same class names. A layered
     rule only outranks them on properties it actually declares, so the shell
     primitives are zeroed here first and then re-specified below. Delete this
     block with the last legacy stylesheet. */
  .cx-shell, .cx-appshell, .cx-rail, .cx-rail__head, .cx-rail__list,
  .cx-rail__group, .cx-rail__foot, .cx-rail__item, .cx-selector__trigger,
  .cx-workspace, .cx-view, .cx-viewpad, .cx-chatrow, .cx-chatrow__lines,
  .cx-chatrow__open, .cx-railtoggle, .cx-railscrim, .cx-skip, .cx-live {
    margin: 0; padding: 0; inset: auto; box-shadow: none;
    text-align: start; text-transform: none; letter-spacing: normal;
    min-inline-size: 0; max-inline-size: none; float: none;
  }


  /* ── 4.1 The frame.
     Grid: [rail track][workspace]. The rail collapses 56 → 0; there is no
     mini state. Root never scrolls. ── */
  .cx-shell, .cx-appshell {
    position: relative;
    display: grid;
    grid-template-columns: var(--rail-w) minmax(0, 1fr);
    block-size: 100dvh;
    block-size: calc(100dvh - var(--kb-inset));
    overflow: hidden;
    background: var(--s-0);
    color: var(--text-1);
    /* M-03. A grid track is not a transform, so this is the ONE sanctioned
       exception to the transform-only rule: a single property, on a single
       element, animating the rail's width in lockstep with the workspace.
       Anything else animating a size is a defect. */
    transition: grid-template-columns var(--t-2) var(--ease-settle);
  }
  /* Collapsing: the icons fade FIRST (--t-0), then the track closes. You must
     never see a frame of squashed icons. Opening reverses the priority — the
     track leads and the icons fade in 40ms behind it. */
  html[data-rail="closed"] .cx-appshell { grid-template-columns: 0px minmax(0, 1fr); transition-delay: var(--t-0); }

  .cx-skip {
    position: fixed; inset-block-start: -100px; inset-inline-start: calc(var(--rail-w) + 12px);
    z-index: var(--z-toast);
    display: inline-flex; align-items: center;
    block-size: var(--h-md); padding-inline: var(--sp-6);
    border: 1px solid var(--line-2); border-radius: var(--r-full);
    background: var(--s-3); color: var(--text-1);
    box-shadow: var(--shadow-pop);
    font: 500 13px/1 var(--font-sans);
    transition: transform var(--t-1) var(--ease-settle);
  }
  .cx-skip:focus-visible { inset-block-start: 12px; }

  .cx-live { position: absolute; inline-size: 1px; block-size: 1px; overflow: hidden; clip-path: inset(50%); }

  /* ── 4.2 The floating rail toggle.
     It never moves and never changes size, in any state, at any viewport.
     It is the always-present anchor — and the collapsed home of the rail's
     working indicator. ── */
  .cx-railtoggle {
    position: fixed;
    /* R4-U6: the toggle floats OVER the top bar, so it must share the bar's
       optical centre line. The bar is --topbar-h tall starting at --safe-t,
       so its controls centre at safe-t + topbar-h/2; centring a --toggle box
       on that line means an inset of (topbar-h - toggle) / 2. Previously a
       flat 12px put it at safe-t+30 against the bar's safe-t+24. */
    inset-block-start: calc(var(--safe-t) + (var(--topbar-h) - var(--toggle)) / 2);
    inset-inline-start: calc(var(--safe-l) + 12px);
    z-index: var(--z-popover);
    display: flex; align-items: center; justify-content: center;
    inline-size: var(--toggle); block-size: var(--toggle);
    padding: 0;
    border: 1px solid var(--line-2);
    border-radius: var(--r-2);
    /* one of exactly TWO permitted backdrop-filters, and it is paired with a
       real opaque-ish colour beneath — never a transparent-only pane */
    background: color-mix(in srgb, var(--s-3) 82%, transparent);
    -webkit-backdrop-filter: blur(10px);
    backdrop-filter: blur(10px);
    color: var(--text-2);
    transition: background-color var(--t-0) var(--ease-settle),
                border-color var(--t-0) var(--ease-settle),
                color var(--t-0) var(--ease-settle),
                transform var(--t-0) var(--ease-settle);
  }
  .cx-railtoggle::after {
    content: ""; position: absolute; inset: 50% auto auto 50%;
    inline-size: var(--tap); block-size: var(--tap); translate: -50% -50%;
  }
  .cx-railtoggle svg { inline-size: var(--icon-ui); block-size: var(--icon-ui); }
  .cx-railtoggle:hover { color: var(--text-1); background: var(--s-3); border-color: var(--line-3); }
  .cx-railtoggle:active { transform: scale(.94); }
  html[data-rail="open"] .cx-railtoggle { color: var(--text-1); }
  /* M-02: only the icon's internal rule shifts 2px toward the closed side. */
  .cx-railtoggle__bar { transition: transform var(--t-1) var(--ease-settle); }
  html[data-rail="closed"] .cx-railtoggle__bar { transform: translateX(-2px); }

  .cx-railtoggle__dot {
    position: absolute; inset-block-start: 4px; inset-inline-end: 4px;
    inline-size: 6px; block-size: 6px; border-radius: var(--r-full);
    background: var(--warn); opacity: 0;
    transition: opacity var(--t-1) var(--ease-settle);
  }
  .cx-railtoggle[data-working="1"] .cx-railtoggle__dot { opacity: 1; }
  /* The aurora ring while work runs elsewhere: a gradient BORDER via two
     backgrounds and two clips — never a gradient fill. */
  .cx-railtoggle__ring {
    position: absolute; inset: -3px;
    /* --r-3, not calc(--r-2 + 3px): 11px is off the radius scale, and this is
       the one control that is on screen at every moment. */
    border-radius: var(--r-3);
    border: 2px solid transparent;
    background: linear-gradient(var(--s-0), var(--s-0)) padding-box,
                var(--aurora) border-box;
    opacity: 0;
    transition: opacity var(--t-2) var(--ease-settle);
    pointer-events: none;
  }
  .cx-railtoggle[data-working="1"] .cx-railtoggle__ring { opacity: .85; }

  /* ── 4.3 The rail. 56px, icon-only, exactly seven things.
     Its outer edge is permanent page furniture: the 1px line exists at every
     viewport and is never hidden. ── */
  .cx-rail {
    position: relative;
    grid-column: 1;
    display: grid;
    /* head (cleared past the floating toggle) · the icon column · Settings,
       pinned at the foot. The 1fr spacer between them stays EMPTY: that
       emptiness is the design, not a gap to fill. */
    grid-template-rows: auto minmax(0, 1fr) auto;
    inline-size: var(--rail-w);
    block-size: 100%;
    overflow: hidden;
    border-inline-end: 1px solid var(--line-2);
    background: var(--s-0);
    /* icons out first on close, in 40ms late on open */
    transition: opacity var(--t-0) var(--ease-settle) 40ms;
  }
  html[data-rail="closed"] .cx-rail { opacity: 0; transition-delay: 0ms; pointer-events: none; }

  .cx-rail__head {
    position: relative;
    display: flex; justify-content: center;
    padding-block-start: var(--rail-top);
  }
  .cx-rail__head::after {
    content: ""; position: absolute; inset-block-end: 0; inset-inline: var(--sp-4);
    block-size: 1px; background: var(--line-1);
  }
  .cx-rail__list {
    min-block-size: 0;
    padding-block-start: var(--sp-6);
    /* R5-93 — the list hides its scrollbars, so a long rail gets the
       mask-fade affordance the section bar already owns (§6.3): the same
       16px fade, turned to the rail's vertical axis. The fades live inside
       the list's own padding at both ends (--sp-6 = 16px top, and the
       padding-block-end added below), so at rest the first and last items
       never fade — only content travelling under an edge does, which is
       exactly the "there is more" signal. As with the secbar, the mask
       creates a stacking context; nothing in this list needs to escape it
       (plates, indicators and working bars all paint inside). */
    padding-block-end: var(--sp-6);
    overflow-y: auto;
    overflow-x: hidden;
    overscroll-behavior: contain;
    scrollbar-width: none;
    -webkit-mask-image: linear-gradient(180deg, transparent, #000 16px, #000 calc(100% - 16px), transparent);
    mask-image: linear-gradient(180deg, transparent, #000 16px, #000 calc(100% - 16px), transparent);
  }
  .cx-rail__list::-webkit-scrollbar { display: none; }
  .cx-rail__group { display: flex; flex-direction: column; gap: var(--sp-1); }
  .cx-rail__foot {
    position: relative;
    padding-block: var(--sp-4) calc(var(--safe-b) + var(--sp-4));
  }
  .cx-rail__foot::before {
    content: ""; position: absolute; inset-block-start: 0; inset-inline: var(--sp-4);
    block-size: 1px; background: var(--line-1);
  }

  /* ── 4.4 A rail item. 56 × 48, glyph at 20px, hit box padded to 44px. ── */
  .cx-rail__item, .cx-selector__trigger {
    position: relative;
    display: flex; align-items: center; justify-content: center;
    inline-size: 100%;
    block-size: var(--rail-item-h);
    padding: 0; border: 0;
    color: var(--text-3);
    transition: color var(--t-0) var(--ease-settle);
  }
  .cx-selector__trigger { block-size: var(--rail-w); }
  /* the hover plate is a ::before, inset from the rail edges */
  .cx-rail__item::before, .cx-selector__trigger::before {
    content: ""; position: absolute; inset: 2px 6px;
    border-radius: var(--r-2);
    background: transparent;
    transition: background-color var(--t-0) var(--ease-settle);
  }
  .cx-rail__item > *, .cx-selector__trigger > * { position: relative; }
  .cx-rail__item svg { inline-size: var(--icon-rail); block-size: var(--icon-rail);
    transition: transform var(--t-0) var(--ease-settle); }
  .cx-rail__label { position: absolute; inline-size: 1px; block-size: 1px; overflow: hidden; clip-path: inset(50%); }

  .cx-rail__item:hover:not([aria-disabled="true"]),
  .cx-selector__trigger:hover { color: var(--text-1); }
  .cx-rail__item:hover:not([aria-disabled="true"])::before,
  .cx-selector__trigger:hover::before,
  .cx-selector__trigger[aria-expanded="true"]::before { background: var(--s-1); }
  .cx-rail__item:active svg { transform: scale(.94); }
  .cx-rail__item:focus-visible, .cx-selector__trigger:focus-visible { outline-offset: -4px; }

  .cx-rail__item[aria-selected="true"] { color: var(--text-1); }
  .cx-rail__item[aria-selected="true"]::before { background: var(--s-1); }
  /* R2-2b — history / files / secrets open a DRAWER over the live view, so
     while one is open its rail item is PRESSED, not selected: the plate lights
     like the hover state and the aurora indicator stays with whatever route is
     really underneath. Two different facts, two different marks: pressed takes
     the ACCENT foreground and a hairline accent ring, selected takes --text-1
     and the aurora indicator. Neither can be mistaken for the other, and
     neither can be mistaken for hover. */
  .cx-rail__item[data-drawer="open"] { color: var(--accent); }
  .cx-rail__item[data-drawer="open"]::before {
    background: var(--s-1);
    box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent) 34%, transparent);
  }

  /* State A of the dependency chain: inert, never hidden, and the title says why. */
  .cx-rail__item[aria-disabled="true"] { color: var(--text-4); cursor: default; }

  /* M-04 — the rail's active indicator FADES; it NEVER MOVES.
     Long vertical travel reads as a distracting flight across the screen.
     Deliberately the OPPOSITE of the workspace section tabs (§4.7), where one
     short-travel underline element translates between tabs. Two different
     rules, on purpose — do not "fix" the inconsistency. The bar is in the DOM
     on all seven items from first paint; nothing is created on click. */
  .cx-rail__ind, .cx-tab__indicator {
    position: absolute;
    inset-inline-end: 0; inset-block-start: 50%;
    translate: 0 -50%;
    inline-size: var(--indicator-w); block-size: var(--indicator-h);
    border-radius: 2px 0 0 2px;
    background: var(--aurora);
    opacity: 0;
    transition: opacity var(--t-1) var(--ease-settle);
  }
  .cx-rail__item[aria-selected="true"] .cx-rail__ind,
  .cx-rail__item[aria-selected="true"] .cx-tab__indicator { opacity: 1; }

  /* The deliberate counterpart to M-04 above. The workspace's section tabs
     (built in stage 2) use ONE 2px underline element that translateX/scaleXes
     between tabs over --t-2 --ease-settle: short horizontal travel reads as a
     single instrument responding, where the rail's long vertical travel would
     read as a distracting flight across the screen. Two different rules, on
     purpose. Do not "fix" the inconsistency in either direction. */
  .cx-secbar__underline {
    position: absolute; inset-block-end: 0; inset-inline-start: 0;
    block-size: 2px; inline-size: 40px;
    background: var(--aurora-rule);
    transform-origin: left center;
    transition: transform var(--t-2) var(--ease-settle);
  }

  /* per-project working bar on the leading edge */
  .cx-rail__work, .cx-workbar {
    position: absolute;
    inset-inline-start: 0; inset-block-start: 50%;
    translate: 0 -50%;
    inline-size: 2px; block-size: var(--indicator-h);
    border-radius: 0 2px 2px 0;
    background: var(--aurora-rule);
    opacity: 0;
    transition: opacity var(--t-1) var(--ease-settle);
  }
  .cx-rail__item[data-working="1"] .cx-rail__work,
  .cx-rail__item[data-working="1"] .cx-workbar,
  .cx-selector__trigger[data-working="1"] .cx-rail__work,
  .cx-selector__trigger[data-working="1"] .cx-workbar { opacity: 1; }

  /* ── 4.5 The selector head. The brand mark is artwork, not an icon. ── */
  .cx-selector__mark { inline-size: 26px; block-size: 26px; }
  .cx-selector__name { position: absolute; inline-size: 1px; block-size: 1px; overflow: hidden; clip-path: inset(50%); }
  /* M-07 — THE BRAND MARK IS THE CHEVRON. The Cosmix mark already carries an
     arrow pointing right; open the selector and it turns 90° so the arrow
     points down at the popover it opened. There is no second disclosure glyph
     on this row, and nothing else on the row moves. Under reduced motion the
     universal rule collapses the duration — the mark still rotates, it just
     arrives instead of travelling. */
  .cx-selector__mark {
    transition: transform var(--t-1) var(--ease-settle);
  }
  .cx-selector__trigger[data-open="true"] .cx-selector__mark { transform: rotate(90deg); }

  .cx-menu-item__check { margin-inline-start: auto; color: var(--accent); display: flex; }
  .cx-menu-item__check svg { inline-size: var(--icon-sm); block-size: var(--icon-sm); }
  /* a project with a live run keeps its aurora edge bar lit */
  .cx-menu-item[data-working="1"]::before { transform: scaleX(1); }

  /* chat rows inside the selector popover */
  .cx-chatrow { display: flex; align-items: center; gap: var(--sp-1); }
  .cx-chatrow__open { flex: 1 1 auto; inline-size: auto; min-inline-size: 0; min-block-size: 52px; }
  .cx-chatrow__lines { display: flex; flex-direction: column; gap: 2px; min-inline-size: 0; }
  .cx-chatrow__meta {
    display: flex; align-items: center; gap: var(--sp-3);
    font: 400 12px/1.40 var(--font-sans); color: var(--text-3);
  }
  .cx-chatrow__state {
    display: inline-flex; align-items: center;
    block-size: 18px; padding-inline: var(--sp-3);
    border-radius: var(--r-full); border: 1px solid var(--line-2);
    font: 500 11px/1 var(--font-mono); letter-spacing: .08em; text-transform: uppercase;
  }
  .cx-chatrow__state--working { border-color: rgba(var(--accent-rgb), .40); background: var(--accent-tint); color: var(--accent-strong); }
  .cx-chatrow__state--capped  { border-color: color-mix(in srgb, var(--warn) 40%, transparent); background: var(--warn-tint); color: var(--warn-strong); }

  /* ── 4.6 The scrim (only while the rail is an overlay) ── */
  .cx-railscrim {
    display: none;
    position: fixed; inset: 0;
    z-index: calc(var(--z-overlay) - 1);
    background: var(--scrim);
    /* M-06 — opacity only. The scrim never travels. */
    --enter-y: 0px;
    animation: cx-enter var(--t-2) var(--ease-settle) backwards;
  }

  /* ── 4.7 The workspace frame. It never scrolls; its children do. ── */
  .cx-workspace {
    position: relative;
    grid-column: 2;
    container-type: inline-size;
    min-inline-size: 0;
    display: flex; flex-direction: column;
    overflow: hidden;
    background: var(--s-1);
    padding-inline-end: var(--safe-r);
    z-index: var(--z-content);
  }
  .cx-view { flex: 1 1 auto; min-block-size: 0; overflow-y: auto; }
  .cx-view--flush { overflow: hidden; }
  .cx-viewpad {
    padding: var(--pad-section);
    padding-inline-start: max(var(--pad-section), calc(var(--toggle) + 24px));
  }
  html[data-rail="open"] .cx-viewpad { padding-inline-start: var(--pad-section); }
  .cx-viewpad--center { display: flex; justify-content: flex-start; }

  /* ── 4.7b THE BOOT CHOREOGRAPHY (M-05 · M-11's siblings · M-01 timing).
     THE SHELL PAINTS WITH ITS FINAL COLOURS IN FRAME ONE. The ground, the
     rail's ground, its outer rule, the top bar and the working rule's idle
     lane are all there before a single glyph moves — the whole app is NEVER
     faded in, because that reads as slow no matter how fast it is.

     What arrives after is furniture, and it arrives in reading order:

        0 →  144ms   rail glyphs, 24ms apart, sliding 6px in from the edge
       60 →  120ms   section tabs, 20ms apart, rising 6px
      120 →  540ms   chat column and preview frame, TOGETHER

     Worst path 540ms, inside the 600ms budget. Nothing scales, nothing
     blurs, nothing on this list is ever replayed by a route change. ── */
  .cx-rail__group > *, .cx-rail__foot > *, .cx-rail__head > * {
    --enter-x: -6px; --enter-y: 0px;
    animation: cx-enter var(--t-2) var(--ease-settle) backwards;
  }
  .cx-rail__head > * { animation-delay: 0ms; }
  .cx-rail__group > *:nth-child(1) { animation-delay: 24ms; }
  .cx-rail__group > *:nth-child(2) { animation-delay: 48ms; }
  .cx-rail__group > *:nth-child(3) { animation-delay: 72ms; }
  .cx-rail__group > *:nth-child(4) { animation-delay: 96ms; }
  .cx-rail__group > *:nth-child(5) { animation-delay: 120ms; }
  .cx-rail__foot > * { animation-delay: 144ms; }

  /* A routed page (Settings, Secrets, history, the creator) rises as one
     block. The workspace box is excluded: it is chrome, and chrome paints. */
  .cx-workspace > *:not(.cx-wsbox) {
    --enter-y: 6px;
    animation: cx-enter var(--t-3) var(--ease-glide) backwards;
  }

  /* The section tabs stagger 20ms apart on the Y axis. The bar itself, its
     ground and the working rule beneath it do not animate at all. */
  .cx-secbar__tab {
    --enter-y: 6px;
    animation: cx-enter var(--t-2) var(--ease-settle) backwards;
  }
  .cx-secbar__tab:nth-child(1) { animation-delay: 60ms; }
  .cx-secbar__tab:nth-child(2) { animation-delay: 80ms; }
  .cx-secbar__tab:nth-child(3) { animation-delay: 100ms; }
  .cx-secbar__tab:nth-child(4) { animation-delay: 120ms; }

  /* The two columns arrive TOGETHER — one gesture, not two. Same delay, same
     duration, same curve; the eye reads them as one surface settling. */
  .cx-ws__chat, .cx-ws__preview {
    --enter-y: 6px;
    animation: cx-enter var(--t-3) var(--ease-glide) 120ms backwards;
  }

  /* ── 4.8 Responsive.
      Collapse, never reflow. At ≤768px the rail becomes a LABELLED overlay:
      a native `title` never fires on a coarse pointer, so touch users must
      never be given an icon-only rail. R5-76 — ONE rail breakpoint, at 768,
      the same line the drawers, the rail toggle default, Esc and the
      item-click auto-close already treat as mobile. The old ≤480 overlay
      band left 481–768 an in-grid icon column with `title` labels on every
      tablet and large-phone landscape — exactly what the law two sentences
      up forbids. Tablets losing the icon column in landscape is the intended
      outcome, not a regression: 769+ keeps the desktop rail (re-verified:
      the ≥769 band still resolves the in-grid track, the `title` labels and
      the open-state inset removals — nothing else moved with the band). ── */
  @media (max-width: 768px) {
    .cx-appshell, html[data-rail="closed"] .cx-appshell { grid-template-columns: 0px minmax(0, 1fr); }
    .cx-rail {
      position: fixed; inset-block: 0; inset-inline-start: 0;
      z-index: var(--z-overlay);
      inline-size: min(84vw, 320px);
      /* R5-08 — THREE tracks for the rail's three children, same as the base
         rule: head · list · foot. The old override declared FOUR
         (var(--rail-top) auto 1fr auto), so the head landed in a phantom
         rail-top-sized track, the foot stretched into the 1fr, and the list
         never got the region it owns. Overrides change track SIZES, never
         track COUNTS. Preserved verbatim by the R5-76 band move 480 → 768. */
      grid-template-rows: auto minmax(0, 1fr) auto;
      transform: translateX(-100%);
      opacity: 1;
      transition: transform var(--t-3) var(--ease-glide);
      box-shadow: var(--shadow-pop);
      background: var(--s-1);
    }
    /* R5-08 — inside the overlay the head no longer clears the floating
       toggle with its own padding; the desktop --rail-top (56px) on top of
       the full-height overlay double-counted the clearance. Preserved
       verbatim by the R5-76 band move 480 → 768. */
    .cx-rail__head { padding-block-start: 0; }
    html[data-rail="open"] .cx-rail { transform: translateX(0); pointer-events: auto; }
    html[data-rail="closed"] .cx-rail { opacity: 1; }
    html[data-rail="open"] .cx-railscrim { display: block; }
    .cx-rail__item { justify-content: flex-start; padding-inline: var(--sp-6); gap: var(--sp-5); }
    .cx-rail__label {
      position: static; inline-size: auto; block-size: auto; clip-path: none;
      overflow: visible; font: 500 14px/1.55 var(--font-sans);
    }
    .cx-selector__name {
      position: static; inline-size: auto; block-size: auto; clip-path: none;
      overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
      font: 500 14px/1.55 var(--font-sans); color: var(--text-1);
    }
    .cx-selector__trigger { justify-content: flex-start; padding-inline: var(--sp-6); gap: var(--sp-5); }
    /* R4-U6: --toggle + 24px is THE leading gap the floating toggle claims,
       at every width and in every view — 12px before it, 12px after it. The
       20px variant that used to live only below 768 is gone. */
    .cx-viewpad { padding-inline-start: max(var(--pad-panel), calc(var(--toggle) + 24px)); }
  }
  @media (max-width: 340px) {
    .cx-rail { inline-size: min(88vw, 300px); }
  }
}

/* ═══════════════════════════ 5. AUTH + FIRST RUN ═══════════════════════════ */

@layer panels {

  /* ── 5.1 Auth. Full-viewport, two-zone — NOT a centred card floating on a
     field. The only aurora on this screen is the lockup's own gradient: no
     gradient background, no glass card, no shadow anywhere. ── */
  .cx-auth {
    position: relative;
    display: grid;
    grid-template-columns: 44fr 56fr;
    block-size: 100dvh;
    block-size: calc(100dvh - var(--kb-inset));
    overflow: hidden;
    background: var(--s-0);
  }
  .cx-auth__brand {
    position: relative;
    display: flex; flex-direction: column;
    padding: var(--pad-section);
    padding-block-start: 12dvh;
    overflow: hidden;
  }
  .cx-auth__lockup { block-size: 40px; inline-size: auto; }
  .cx-auth__lockup img { block-size: 40px; inline-size: auto; }
  .cx-auth__headline {
    margin-block-start: var(--sp-8);
    max-inline-size: 14ch;
    color: var(--text-display);
  }
  .cx-auth__line { margin-block-start: var(--sp-5); max-inline-size: 34ch; color: var(--text-2); }
  /* the drawn furniture that keeps the empty half reading as specified, not unfinished */
  .cx-auth__foot { margin-block-start: auto; }
  .cx-auth__foot .cx-group__rule { background: var(--line-2); }

  .cx-auth__form {
    display: flex; align-items: flex-start;
    padding: var(--pad-section);
    padding-block-start: 42dvh;
    border-inline-start: 1px solid var(--line-2);
    background: var(--s-1);
    overflow-y: auto;
  }
  .cx-auth__col { inline-size: 100%; max-inline-size: 360px; }
  .cx-auth__col > form { display: flex; flex-direction: column; gap: var(--pad-section); }
  .cx-auth__switch { margin-block-start: var(--sp-6); font: 400 13px/1.5 var(--font-sans); color: var(--text-2); }
  .cx-auth__switch + .cx-auth__switch { margin-block-start: var(--sp-4); }
  .cx-auth__result { display: flex; flex-direction: column; gap: var(--sp-5); }

  /* M-01: the brand zone paints instantly with its final colours; the lockup,
     headline and line stagger in; the form column arrives 120ms later. */
  .cx-auth__brand > * { --enter-y: 8px; animation: cx-enter var(--t-3) var(--ease-glide) backwards; }
  .cx-auth__brand > *:nth-child(1) { animation-delay: 0ms; }
  .cx-auth__brand > *:nth-child(2) { animation-delay: 60ms; }
  .cx-auth__brand > *:nth-child(3) { animation-delay: 120ms; }
  .cx-auth__brand > *:nth-child(4) { animation-delay: 180ms; }
  .cx-auth__col { --enter-y: 8px; animation: cx-enter var(--t-3) var(--ease-glide) 120ms backwards; }

  @media (max-width: 900px) {
    .cx-auth { grid-template-columns: 1fr; grid-template-rows: 30dvh 1fr; }
    .cx-auth__brand { padding-block-start: var(--pad-section); }
    .cx-auth__lockup img { block-size: 32px; }
    .cx-auth__headline { font: 600 24px/1.20 var(--font-sans); letter-spacing: -.022em; margin-block-start: var(--sp-6); }
    .cx-auth__form {
      border-inline-start: 0; border-block-start: 1px solid var(--line-2);
      padding-block-start: var(--pad-section);
    }
  }
  @media (max-width: 480px) {
    .cx-auth__brand, .cx-auth__form { padding-inline: var(--pad-panel); }
    .cx-auth__col { max-inline-size: none; }
  }

  /* ── 5.2 First run — the project creator.
     The block sits at 40% height, not true centre. ── */
  .cx-creator {
    max-inline-size: 420px;
    padding-block-start: min(18dvh, 140px);
    border: 0; background: none; box-shadow: none; border-radius: 0;
  }
  .cx-creator__mark img { block-size: 40px; inline-size: 40px; }
  /* Fraunces at 44px — permitted use #1 of three, ONCE per session. That
     restraint is what makes it land. */
  .cx-creator__headline { margin-block-start: var(--sp-7); color: var(--text-display); }
  .cx-creator__line { margin-block-start: var(--sp-5); color: var(--text-2); }
  .cx-creator__form { margin-block-start: var(--sp-8); }
  .cx-creator > * { --enter-y: 8px; animation: cx-enter var(--t-3) var(--ease-glide) backwards; }
  .cx-creator > *:nth-child(1) { animation-delay: 0ms; }
  .cx-creator > *:nth-child(2) { animation-delay: 40ms; }
  .cx-creator > *:nth-child(3) { animation-delay: 80ms; }
  .cx-creator > *:nth-child(4) { animation-delay: 120ms; }
  .cx-creator > *:nth-child(5) { animation-delay: 160ms; }
  @media (max-width: 480px) {
    .cx-creator__headline { font: 500 32px/1.12 var(--font-sans); letter-spacing: -.030em; }
    .cx-creator { max-inline-size: none; }
  }

  /* ── 5.3 Structural lines are permanent page furniture. The rule under the
     top bar and the rule between the columns exist at every viewport and are
     never hidden. They convert emptiness from "unfinished" into "specified". ── */
  .cx-hrule { block-size: 1px; background: var(--line-2); flex: none; }
}

/* ═══════════════════════════ 6. THE WORKSPACE ═══════════════════════════
   Stage 2. ONE top bar inside the workspace: icon section tabs on the left,
   a right slot carrying the publish pill, the publish button, diagnostics
   and select-element, then reload and the overflow menu. The 2px working
   rule is pinned to its bottom edge and is present at all times.
   ═══════════════════════════════════════════════════════════════════════ */

@layer shell {

  /* Ghost reset for the stage-2 primitives the legacy `dashboard` sheet still
     names. Delete with the last legacy stylesheet. */
  .cx-wsbox, .cx-ws__top, .cx-ws__slot, .cx-ws__panes, .cx-ws__chat,
  .cx-ws__preview, .cx-secbar, .cx-secbar__tab,
  .cx-milestone, .cx-pv, .cx-pv__body, .cx-pv__plate, .cx-pv__frame,
  .cx-pv__hint, .cx-pv__controls, .cx-swapcount {
    margin: 0; padding: 0; inset: auto; box-shadow: none; float: none;
    border: 0; min-inline-size: 0; min-block-size: 0; max-inline-size: none;
    text-transform: none; letter-spacing: normal;
  }

  /* ── 6.1 The workspace box ── */
  .cx-wsbox {
    display: flex; flex-direction: column;
    block-size: 100%; min-block-size: 0;
    /* the container every workspace-scoped @container query in this file
       measures against, and the frame the aurora sweep's 100cqw resolves to */
    container-type: inline-size;
    background: var(--s-1);
  }

  /* ── 6.2 The one top bar. 48px, never scrolls, never wraps. R2-2a deleted
     the `more` menu, so the bar's overflow path is now structural: the spacer
     collapses first, then the SECTION BAR yields down to its floor and scrolls
     inside itself. The right slot's controls are fixed-size and never shrink —
     they are the state and the verb for the thing on screen. ── */
  .cx-ws__top {
    position: relative;
    flex: none;
    block-size: var(--topbar-h);
    display: flex; align-items: center; gap: var(--sp-2);
    padding-inline: var(--sp-3);
    /* the floating toggle owns the top-left corner at every viewport, so the
       bar's leading inset clears it whenever the rail is not holding the
       space open. Padding, not margin: the bar's ground is continuous. */
    padding-inline-start: max(var(--sp-3), calc(var(--toggle) + 24px));
    background: var(--s-1);
    /* the bar itself NEVER scrolls; the section strip inside it does, and it
       is the strip that yields every pixel the fixed controls need */
    overflow: hidden;
  }
  html[data-rail="open"] .cx-ws__top { padding-inline-start: var(--sp-3); }
  @media (max-width: 768px) {
    /* the rail is an overlay across this whole band (R5-76) — the toggle
       always floats over the bar, so the inset is unconditional */
    /* R4-U6: ONE leading gap at every width — 12px before the toggle and 12px
       after it, i.e. --toggle + 24px, the same value the wide bar uses. The
       old 20px made the toggle sit 4px closer to the tabs below 480 only. */
    html[data-rail="open"] .cx-ws__top { padding-inline-start: calc(var(--toggle) + 24px); }
    .cx-ws__top { padding-inline-start: calc(var(--toggle) + 24px); }
  }

  /* The working rule lives on the bar's bottom edge. Its idle and working
     paint are in §3.15; this only places it. */
  .cx-ws__top > .cx-rule {
    position: absolute; inset-inline: 0; inset-block-end: 0;
    z-index: 1;
  }

  /* ── 6.3 Section tabs. Icon-only, labelled by title + aria-label. ── */
  .cx-secbar {
    position: relative;
    /* The bar sits in the same 2px band as the working rule (z-index 1). Below
       640px the bar takes a mask, and a mask creates a stacking context that
       would trap the underline's own z-index inside an `auto` parent — so the
       bar itself must be lifted as a unit or the active underline disappears
       on every narrow workspace. Do not remove. */
    z-index: 2;
    align-self: stretch;
    display: flex; align-items: center; gap: var(--sp-1);
    /* The bar is allowed to shrink, but never into a sliver: the right slot's
       controls are fixed-width and would otherwise eat every pixel. Below its
       floor it scrolls — one of the four sanctioned horizontal scrollers. */
    flex: 0 1 auto;
    min-inline-size: min(166px, 34cqw);
    overflow-x: auto; overflow-y: hidden;
    scrollbar-width: none;
    -webkit-mask-image: none; mask-image: none;
  }
  .cx-secbar::-webkit-scrollbar { display: none; }
  /* One of exactly four sanctioned horizontal scrollers. The 16px fade tells
     a thumb there is more; it is only drawn where the bar can actually
     overflow, so the first tab never fades at a comfortable width. */
  @container (max-width: 640px) {
    .cx-secbar {
      -webkit-mask-image: linear-gradient(90deg, transparent, #000 16px, #000 calc(100% - 16px), transparent);
      mask-image: linear-gradient(90deg, transparent, #000 16px, #000 calc(100% - 16px), transparent);
    }
  }
  .cx-secbar__tab {
    position: relative;
    flex: none;
    display: inline-flex; align-items: center; justify-content: center;
    inline-size: 40px; block-size: 40px;
    border-radius: var(--r-2);
    color: var(--text-3);
    transition: color var(--t-0) var(--ease-settle),
                background-color var(--t-0) var(--ease-settle);
  }
  .cx-secbar__tab::after {
    content: ""; position: absolute; inset: 50% auto auto 50%;
    inline-size: var(--tap); block-size: var(--tap); translate: -50% -50%;
  }
  .cx-secbar__tab svg { inline-size: var(--icon-ui); block-size: var(--icon-ui); }
  .cx-secbar__tab:hover { color: var(--text-1); background: var(--s-2); }
  .cx-secbar__tab[aria-selected="true"] { color: var(--text-1); }
  .cx-secbar__label {
    position: absolute; inline-size: 1px; block-size: 1px; margin: -1px;
    overflow: hidden; clip-path: inset(50%); white-space: nowrap;
  }
  /* The travelling underline is declared once, in §4.4, next to the rail's
     FADING indicator, with the comment that names each rule's opposite. This
     only places it inside the bar and lifts it over the working rule. */
  .cx-secbar__underline { z-index: 2; }
  .cx-secbar[data-ready="0"] .cx-secbar__underline { transition: none; }

  /* ── 6.4 The right slot. Emptied and refilled on every section switch:
     this is the ONE top bar, and a panel never carries a second. ── */
  /* The slot never shrinks: its controls are the state and the verb for the
     thing on screen. When space runs out the SECTION BAR yields — it drops to
     its floor and scrolls inside itself. R2-2a: there is no `more` menu below
     480px any more, so that floor is what has to absorb the narrowest case. */
  .cx-ws__slot { display: flex; align-items: center; gap: var(--sp-4); flex: none; }
  .cx-ws__slot:empty { display: none; }
  .cx-ws__spacer { flex: 1 1 auto; min-inline-size: var(--sp-2); }
  .cx-pv__controls { display: flex; align-items: center; gap: var(--sp-4); flex: none; }

  /* R4-U8 (8): the swap button and the reload button are direct children of a
     flex bar whose other child (the section strip) is allowed to yield. They
     are NOT allowed to yield: `flex: none` is what holds their slot at 320,
     where the default flex-shrink:1 would otherwise squeeze a 28px box into a
     sliver the moment the strip hit its floor. */
  .cx-ws__top > .cx-iconbtn { flex: none; }

  /* The swap button's staged-but-unsent count, for a user who declined the
     automatic swap. --warn tone, mono, tabular. */
  .cx-ws__swap { position: relative; }
  .cx-swapcount {
    position: absolute; inset-block-start: 0; inset-inline-end: 0;
    min-inline-size: 16px; block-size: 16px; padding-inline: 4px;
    display: inline-flex; align-items: center; justify-content: center;
    border-radius: var(--r-full);
    border: 1px solid color-mix(in srgb, var(--warn) 40%, transparent);
    background: var(--warn-tint); color: var(--warn-strong);
    font: 500 11px/1 var(--font-mono); font-variant-numeric: tabular-nums;
  }

  /* M-13 — the reload glyph turns 180° ONCE PER PRESS. It is never a
     continuous spin: this product has no spinner, and a glyph that keeps
     turning is one. */
  .cx-ws__reload svg { transition: transform var(--t-2) var(--ease-settle); }

  /* Armed element-selector: the one control in the bar that carries the
     accent, because while it is armed it IS the most important thing on
     screen. */
  .cx-pv__arm[aria-pressed="true"] {
    color: var(--accent);
    background: var(--accent-tint);
    border-color: rgba(var(--accent-rgb), .40);
  }

  /* ── 6.5 The ONE publish control (U2). The status LEADS it: a 6px dot and
     the state word, then a menu on every state. There is no separate status
     pill and no separate visit link — one subject, one box, and the thing you
     read is the thing you press. On hover a 1px AURORA GRADIENT BORDER fades
     in — never a gradient fill — drawn as a masked ::after so the word is
     untouched by the border itself. Type that shifts under the cursor reads
     cheap. ── */
  .cx-publishbtn { position: relative; overflow: visible; gap: var(--sp-3); }
  /* The word is mono uppercase, as the state has always been read here.
     R5-73/F13 deleted the permanent width lock (12ch for "Unpublishing"):
     the resting states are Live and Draft, so the lock bought zero shift
     between states at the price of ~64px of dead space beside a 4-letter
     word. The slot now rests at its NATURAL per-state width; only the busy
     words (Publishing, Unpublishing) may widen the control while a publish
     is actually in flight — a shift while busy is honest motion, a shift
     while idle was padding. Below 340px the word goes dot-only (§6.6c). */
  .cx-publishbtn__word {
    display: inline-block;
    font: 500 11px/1 var(--font-mono); letter-spacing: .08em; text-transform: uppercase;
    text-align: start; white-space: nowrap;
    transition: color var(--t-1) var(--ease-settle);
  }
  .cx-publishbtn[data-state="live"] .cx-publishbtn__word { color: var(--ok-strong); }
  .cx-publishbtn[data-state="publishing"] .cx-publishbtn__word { color: var(--accent-strong); }
  .cx-publishbtn::after {
    content: ""; position: absolute; inset: -1px; border-radius: inherit;
    padding: 1px;
    background: var(--aurora);
    -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
    -webkit-mask-composite: xor;
            mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
            mask-composite: exclude;
    opacity: 0; pointer-events: none;
    transition: opacity var(--t-1) var(--ease-settle);
  }
  .cx-publishbtn:hover::after,
  .cx-publishbtn:focus-visible::after,
  .cx-publishbtn[aria-busy="true"]::after { opacity: 1; }

  /* ── 6.6b The publish control on small screens. Down to 341px it keeps a
     natural-width word: the state is the point of the control, and what
     gives first is the tracking, then a step of type size — the word stays
     legible mono, just tighter — and the section bar, which is the region
     allowed to yield here. At ≤340px (§6.6c below) the control finally
     goes dot-only. R5-73/F13 reverses the prior "keeps its word at every
     width, never collapses to a dot" decision — recorded here so it is not
     corrected back: the width lock and the never-dot rule together priced
     the bar at ~64px of engineered dead space beside a 4-letter resting
     word. ── */
  /* R4-U8 (1) fix — the band nobody measured, re-derived after R5-61/75
      (Fable D9) and again after R5-76 moved the overlay band to ≤768.
      Between 481 and 768 the widest bar is no longer a LIST section — the
      item cluster (back + select + one icon-only save verb, 132px of slot
      with 2 × 8 gaps) totals 366 — but the SURFACES cluster (publish 90 +
      diagnostics 28 + selector 28 + 2 gaps = 150 in the slot), which is
      what the strip's scrollable 76px floor serves now. Box math at 481,
      worst case: 60 inset + 6 trailing + 4×4 gaps + 36 swap + 76 strip +
      4 spacer + 150 slot + 36 reload = 384. R5-76 re-derivation: at 481 the
      rail is now the overlay (0px track), so the bar spans the full 481 —
      the pre-move worst case was 425 (481 minus the open 56px in-grid
      rail), and 384 fit even there. The floor keeps its band with MORE
      slack, and the unconditional toggle inset it budgets (60 = toggle +
      24 + pad) is the same value the moved §6.2 block now applies across
      the whole band. */
  @media (max-width: 768px) {
    .cx-secbar { min-inline-size: min(76px, 34cqw); }
  }
  @media (max-width: 480px) {
    /* R2-2a — nothing hides here. The ⋯ menu that used to be these controls'
       named overflow home is gone, so the diagnostics badge (small by
       construction), the element selector and the one reload button all stay
       on the bar, icon-only, at every width. */
    /* R4-U8 (1) fix — 40px, not 76, re-derived after R5-61/75 (Fable D9):
     the documents cluster that needed every pixel here is gone; the pressed
     bar at 381–480 is the SURFACES cluster — math at 381: 60 + 6 + 16 + 36
     + 40 + 4 + 150 + 36 = 348 — while a list item (back + select + the
     one save verb, 124 in the slot) totals 322 and no longer leans on the
     floor. 40 still shows a scrollable tab. */
    .cx-secbar { min-inline-size: 40px; }
    .cx-publishbtn { padding-inline: var(--sp-3); }
    .cx-publishbtn__word {
      font-size: 10px; letter-spacing: .04em;
    }
    /* 8px of dead space so a thumb cannot straddle two controls */
    .cx-pv__controls { gap: var(--sp-4); }
  }
  /* The floor is what absorbs the overflow now that the `more` menu is gone:
     below 380px the fixed cluster (the one publish control + diagnostics +
     arm + reload) plus the toggle's leading inset claims the bar, so the
     section strip drops to a single tab and scrolls — the one region here
     allowed to. */
  /* R4-U8 (1): the 320px floor, measured rather than guessed. Every fixed
     control keeps its 44px HIT box (the ::after overlays are untouched); only
     the painted boxes and the dead space between them tighten, and the
     section strip stays the one region that scrolls. The arithmetic at 320,
     worst case (narrow workspace, diagnostics badge showing, element selector
     present) is written out in §9.24's twin block. */
  @media (max-width: 380px) {
    .cx-ws__top { gap: var(--sp-1); padding-inline-end: var(--sp-1); }
    /* the spacer is dead space first: it gives every pixel before any control
       is asked to give one */
    .cx-ws__spacer { min-inline-size: 0; }
    .cx-pv__controls { gap: var(--sp-1); }
    .cx-ws__top .cx-iconbtn { inline-size: var(--h-xs); block-size: var(--h-xs); }
    /* R4-U8 (3) fix — the 44px overlay is WIDER than the control pitch here
       (28px box + 2px gap = 30), so two neighbours' hit boxes overlapped by
       14px and the later-in-DOM one silently won. The overlay is clamped to
       the pitch on the inline axis; the block axis keeps its full 44px, so
       the thumb target stays as tall as it ever was and no two targets can
       be straddled at once. */
    .cx-ws__top .cx-iconbtn::after { inline-size: calc(100% + var(--sp-1)); }
    .cx-publishbtn { padding-inline: var(--sp-2); gap: var(--sp-2); }
    /* the word still SAYS the state — it only loses its tracking */
    .cx-publishbtn__word { letter-spacing: 0; }
  }

  /* ── 6.6c R5-73/F13 — the dot-only floor at ≤340px. This DELIBERATELY
     reverses the prior "the publish control no longer collapses to a bare
     dot at any width" rule recorded above the 380 block: with the width
     lock gone, 340 is where the word stops fitting beside the fixed
     cluster, and the bar's 320px arithmetic (§9.24) has no room for it.
     The word span STAYS IN THE DOM, visually hidden with the sheet's own
     clip idiom — setBusy writes the busy word into it and repaints the
     state word back, so removing it would break the busy label. The dot
     and the aria-label (which spells out the full state) carry the
     control's meaning here. ── */
  @media (max-width: 340px) {
    .cx-publishbtn__word {
      position: absolute; inline-size: 1px; block-size: 1px;
      overflow: hidden; clip-path: inset(50%);
    }
  }

  /* ── 6.7 The two panes. Chat left, live preview right, with the permanent
     structural line between them. ── */
  .cx-ws__panes { flex: 1 1 auto; min-block-size: 0; display: flex; min-inline-size: 0; }
  .cx-ws__chat {
    flex: none; inline-size: var(--chat-w); min-inline-size: 0; min-block-size: 0;
    display: flex; flex-direction: column;
    border-inline-end: 1px solid var(--line-2);
    background: var(--s-1);
  }
  .cx-ws__preview {
    position: relative;
    flex: 1 1 auto; min-inline-size: 0; min-block-size: 0;
    display: flex; flex-direction: column;
    background: var(--s-1);
  }
  @container (max-width: 640px) {
    .cx-ws__chat { inline-size: 100%; flex: 1 1 auto; border-inline-end: 0; }
  }
  @media (min-width: 1921px) {
    /* the chat stops growing and the surplus goes to the preview — the user's
       own page is the thing worth showing */
    .cx-ws__chat { inline-size: var(--chat-w-wide); }
  }

  /* M-25 — the pane swap. One pane leaves, one arrives; both are already in
     the DOM, the inactive one hidden AND inert. */
  .cx-ws__pane--enter {
    --enter-x: 8px; --enter-y: 0px;
    animation: cx-enter var(--t-2) var(--ease-settle) backwards;
  }
}

/* ═══════════════════════════ 7. THE CHAT ═══════════════════════════
   The chat column is where most of the premium feeling in this product
   lives, and it comes from one decision: CosmixAgent messages have NO
   BUBBLE. Removing the assistant bubble makes the transcript read as a
   document rather than a chat app.
   ═══════════════════════════════════════════════════════════════════ */

@layer chat {

  .cx-chat {
    /* head · transcript · status · composer. minmax(0,1fr) is load-bearing:
       a bare 1fr will not shrink below its content, and the transcript would
       push the composer off the bottom of the pane. */
    display: grid; grid-template-rows: auto minmax(0, 1fr) auto auto;
    block-size: 100%; min-block-size: 0;
    background: var(--s-1);
  }

  /* ── 7.1 Header. 44px, never scrolls, never wraps. ── */
  .cx-chat__head {
    display: flex; align-items: center; gap: var(--sp-3);
    block-size: 44px; flex: none;
    padding-inline: var(--pad-panel);
    border-block-end: 1px solid var(--line-1);
    min-inline-size: 0;
  }
  .cx-chat__title {
    position: relative;
    display: flex; align-items: center;
    flex: 0 1 auto; min-inline-size: 0; min-block-size: 32px;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    font: 500 14px/1.55 var(--font-sans); color: var(--text-1);
    text-align: start; border-radius: var(--r-2);
    padding-inline: var(--sp-2); margin-inline-start: calc(var(--sp-2) * -1);
    transition: background-color var(--t-0) var(--ease-settle);
  }
  .cx-chat__title:hover { background: var(--s-2); }
  /* the hit box is padded to the full 44px header; the visual box is not
     inflated — that is the rule everywhere in this product */
  .cx-chat__title::after {
    content: ""; position: absolute; inset-block: -6px; inset-inline: -4px;
  }
  input.cx-chat__title::after { content: none; }
  /* Inline rename: the title becomes an input IN PLACE, with the same
     metrics, so nothing shifts. Never a modal. */
  input.cx-chat__title {
    background: var(--s-2); border: 1px solid var(--accent);
    block-size: 28px; inline-size: 100%;
  }
  .cx-chat__grow { flex: 1 1 auto; min-inline-size: var(--sp-2); }
  .cx-chat__meter {
    font: 500 11px/1 var(--font-mono); letter-spacing: .08em; text-transform: uppercase;
    color: var(--text-3); font-variant-numeric: tabular-nums; white-space: nowrap;
  }
  .cx-chat__runstate {
    font: 500 11px/1 var(--font-mono); letter-spacing: .08em; text-transform: uppercase;
    color: var(--text-3); white-space: nowrap;
  }
  .cx-chat__runstate:empty { display: none; }
  .cx-chat__runstate[data-state="working"] { color: var(--accent-strong); }
  .cx-chat__runstate[data-state="capped"] { color: var(--warn-strong); }

  /* ── 7.2 Transcript. The single scroll owner of the pane. ── */
  .cx-chat__log {
    position: relative;
    min-block-size: 0; overflow-y: auto; overscroll-behavior: contain;
    padding: var(--sp-6) var(--pad-panel) var(--sp-5);
    scroll-padding-block-end: var(--sp-8);
  }
  .cx-chat__rows { display: flex; flex-direction: column; gap: var(--sp-7); }
  .cx-chat__spacer { flex: none; }

  /* Day separator — the label-under-rule pattern, so the transcript's own
     furniture matches every panel's. */
  .cx-chat__day { display: block; padding-block-start: var(--sp-4); }
  .cx-chat__day::before {
    content: ""; display: block; block-size: 1px; background: var(--line-1);
  }
  .cx-chat__day span {
    display: block; padding-block-start: var(--sp-3);
    font: 500 11px/1.25 var(--font-mono); letter-spacing: .08em; text-transform: uppercase;
    color: var(--text-3);
  }

  /* D4 — the plan digest's disclosure is a real control. The whole summary
     row toggles (native <details>), so it earns the product's focus ring
     and a hover ground like every interactive row; opened, the summary and
     the plan text are separated by one hairline so the block reads as its
     own space — no transcript rule can cross it (the box owns margin on
     both sides, set from agent.js). */
  .cx-chat__plan summary:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
    border-radius: var(--r-2);
  }
  .cx-chat__plan summary:hover .cx-chat__plan-title { color: var(--text-1); }
  .cx-chat__plan[open] .cx-chat__plan-text {
    border-block-start: 1px solid var(--line-1);
  }

  /* Show earlier messages — a real control, not lazy loading, so a keyboard
     or touch user can always get back. */
  .cx-chat__earlier { display: flex; justify-content: center; padding-block-end: var(--sp-6); }
  .cx-chat__earlier:empty { display: none; }

  /* Jump to latest */
  .cx-chat__jump {
    position: sticky; inset-block-end: var(--sp-4);
    display: flex; align-items: center; gap: var(--sp-3);
    inline-size: max-content; margin-inline: auto; margin-block-start: var(--sp-4);
    block-size: var(--h-sm); padding-inline: var(--sp-5);
    /* A text button, so --r-2: --r-full is dots, status pills, the publish
       pill and avatars, and nothing else (AD §4.2). */
    border: 1px solid var(--line-2); border-radius: var(--r-2);
    background: var(--s-3); color: var(--text-1);
    box-shadow: var(--shadow-pop);
    font: 400 12px/1 var(--font-sans);
    --enter-y: 6px;
    animation: cx-enter var(--t-1) var(--ease-settle) backwards;
  }
  .cx-chat__jump svg { inline-size: var(--icon-chip); block-size: var(--icon-chip); }

  .cx-chat__empty { display: block; }

  /* ── 7.3 Messages ── */
  .cx-msg { display: block; }
  /* M-22 — a message enters ONCE, ever. The transcript is DOM-windowed, so
     the flag lives on the message RECORD, not on the node: a node re-entering
     the window on scroll must not animate again. */
  .cx-msg--enter { animation: cx-enter var(--t-2) var(--ease-settle) backwards; }

  /* The user speaks in a bubble, right-aligned. */
  .cx-msg--user { display: flex; justify-content: flex-end; }
  .cx-msg--user .cx-msg__body {
    max-inline-size: min(560px, 78%);
    padding: var(--sp-5) var(--sp-6);
    border: 1px solid var(--line-2); border-radius: var(--r-3);
    background: var(--s-2); box-shadow: var(--lit);
    color: var(--text-1);
  }

  /* CosmixAgent has NO BUBBLE: prose on the panel ground, a mono label above
     and a 2px rule down the leading edge. This is the decision the whole
     column is built on. */
  .cx-msg--agent .cx-msg__label {
    display: block; margin-block-end: var(--sp-4);
    font: 500 11px/1.25 var(--font-mono); letter-spacing: .08em; text-transform: uppercase;
    color: var(--text-3);
  }
  .cx-msg--agent .cx-msg__body {
    border-inline-start: 2px solid var(--line-2);
    padding-inline-start: var(--sp-5);
    color: var(--text-1);
  }
  .cx-msg__body {
    font: 400 15px/1.65 var(--font-sans); letter-spacing: -.003em;
    /* a pasted URL or a long identifier must break rather than widen the
       column — the transcript never scrolls sideways */
    overflow-wrap: break-word; word-break: break-word;
  }
  /* The user's half is verbatim: their line breaks are their line breaks. */
  .cx-msg--user .cx-msg__body { white-space: pre-wrap; }

  /* M-15 — a prose link draws its underline from the left. */
  .cx-msg__body a {
    color: var(--accent);
    background-image: linear-gradient(currentColor, currentColor);
    background-size: 0 1px; background-position: 0 100%; background-repeat: no-repeat;
    transition: background-size var(--t-2) var(--ease-glide);
  }
  .cx-msg__body a:hover, .cx-msg__body a:focus-visible { background-size: 100% 1px; }

  /* ── 7.4 Chat content. The rendered markdown of an ASSISTANT message and
     NOTHING else: every selector below is a descendant of `.cx-md`, which is
     only ever set on `.cx-msg__body`, so none of this can reach the app's
     own chrome. Colours come from tokens, so both themes are already done.

     Headings are DEMOTED — an h1 in a chat message is a paragraph with
     authority, not a page title, and letting it render at document scale is
     what makes a transcript look like a dumped README. ── */
  .cx-md > :first-child { margin-block-start: 0; }
  .cx-md > :last-child { margin-block-end: 0; }
  .cx-md p { margin-block: 0 var(--sp-5); max-inline-size: 68ch; }
  .cx-md h1, .cx-md h2, .cx-md h3,
  .cx-md h4, .cx-md h5, .cx-md h6 {
    margin-block: var(--sp-7) var(--sp-4);
    font-family: var(--font-sans); font-weight: 650; line-height: 1.3;
    letter-spacing: -.012em; color: var(--text-display);
  }
  .cx-md h1 { font-size: 1.30em; }
  .cx-md h2 { font-size: 1.18em; }
  .cx-md h3 { font-size: 1.06em; }
  .cx-md h4, .cx-md h5, .cx-md h6 { font-size: 1em; }
  .cx-md h6 { color: var(--text-3); }

  .cx-md ul, .cx-md ol { margin-block: 0 var(--sp-5); padding-inline-start: 1.5em; }
  .cx-md li { margin-block: var(--sp-1); }
  .cx-md li > ul, .cx-md li > ol { margin-block: var(--sp-1) 0; }
  .cx-md ul { list-style: disc; }
  .cx-md ul ul { list-style: circle; }
  .cx-md ol { list-style: decimal; }

  .cx-md code {
    padding: .15em .4em;
    border: 1px solid var(--line-1); border-radius: var(--r-1);
    background: var(--s-inset);
    font: 400 .875em/1.4 var(--font-mono); color: var(--text-1);
  }

  /* The code CARD: a hairline box with a label row above a pre that SCROLLS.
     Wrapped code is unreadable code, so the pre never wraps (research §C). */
  .cx-md__code {
    margin-block: var(--sp-5);
    border: 1px solid var(--line-2); border-radius: var(--r-2);
    background: var(--s-inset); overflow: hidden;
  }
  .cx-md__codehead {
    display: flex; align-items: center; justify-content: space-between; gap: var(--sp-4);
    padding: var(--sp-3) var(--sp-5);
    border-block-end: 1px solid var(--line-1);
  }
  .cx-md__lang {
    font: 500 11px/1.25 var(--font-mono); letter-spacing: .08em;
    color: var(--text-3);
  }
  /* M-16 — the copy control confirms on its OWN label, in place, never a
     toast. It is always present here: a hover-only reveal on a control that
     is the whole point of the card is a defect on a touch device. */
  .cx-md__copy {
    padding: 0; border: 0; background: none;
    font: 500 11px/1.25 var(--font-mono); letter-spacing: .08em;
    color: var(--text-3); cursor: pointer;
    transition: color var(--t-0) var(--ease-settle);
  }
  .cx-md__copy:hover { color: var(--text-1); }
  .cx-md__copy[data-copied="1"] { color: var(--ok); }
  .cx-md__code pre {
    margin: 0; padding: var(--sp-5) var(--sp-6);
    overflow-x: auto; overscroll-behavior-x: contain;
    font: 400 13px/1.55 var(--font-mono); color: var(--text-1);
    white-space: pre; tab-size: 2;
  }
  .cx-md__code pre code {
    padding: 0; border: 0; background: none; font-size: inherit;
  }

  /* The TABLE scrolls inside its own box — never the page (matrix law). */
  .cx-md__tablewrap {
    margin-block: var(--sp-5); max-inline-size: 100%;
    overflow-x: auto; overscroll-behavior-x: contain;
  }
  .cx-md table { border-collapse: collapse; font-size: .9em; }
  .cx-md th, .cx-md td {
    padding: var(--sp-3) var(--sp-5);
    border: 1px solid var(--line-2); text-align: start;
  }
  .cx-md th { font-weight: 650; background: var(--s-2); color: var(--text-display); }

  .cx-md blockquote {
    margin-block: var(--sp-5); margin-inline: 0;
    padding-inline-start: var(--sp-6);
    border-inline-start: 3px solid var(--line-3);
    color: var(--text-2); font-style: normal;
  }
  .cx-md hr {
    margin-block: var(--sp-7); border: 0;
    block-size: 1px; background: var(--line-2);
  }

  /* ── 7.5 The status area: tool chip, working line, inline notices. ── */
  .cx-chat__status {
    display: flex; flex-direction: column; gap: var(--sp-4);
    padding: 0 var(--pad-panel);
  }
  .cx-chat__status:empty { display: none; }

  .cx-toolchip {
    align-self: flex-start;
    display: inline-flex; align-items: center;
    block-size: 24px; padding-inline: var(--sp-4);
    border: 1px solid rgba(var(--accent-rgb), .35); border-radius: var(--r-full);
    background: var(--accent-tint); color: var(--accent-strong);
    font: 500 11px/1 var(--font-mono); letter-spacing: .08em; text-transform: uppercase;
    --enter-y: 4px;
    animation: cx-enter var(--t-1) var(--ease-settle) backwards;
  }
  .cx-toolchip[data-tone="failed"] {
    border-color: color-mix(in srgb, var(--danger) 35%, transparent);
    background: var(--danger-tint); color: var(--danger-strong);
  }

  /* The composer status line. There is no spinner: a breathing dot, the word
     WORKING, and an elapsed timer in tabular figures so 0:09 → 0:10 shifts
     nothing. */
  .cx-work {
    display: flex; align-items: center; gap: var(--sp-4);
    block-size: 20px;
    font: 500 11px/1 var(--font-mono); letter-spacing: .08em; text-transform: uppercase;
    color: var(--text-3);
    transition: opacity var(--t-1) var(--ease-settle);
  }
  .cx-work[data-done="1"] { color: var(--text-3); }
  .cx-work__timer { font-variant-numeric: tabular-nums; color: var(--text-2); }
  .cx-work__label { color: var(--text-3); }

  /* ── 7.6 Composer. It rides directly above the on-screen keyboard because
     the SHELL is calc(100dvh - --kb-inset) — no position:fixed anywhere. ── */
  .cx-composer {
    display: flex; flex-direction: column; gap: var(--sp-4);
    flex: none;
    padding: var(--sp-4) var(--pad-panel) var(--comp-pad);
    border-block-start: 1px solid var(--line-2);
    background: var(--s-1);
  }
  /* R4-U4: the FIELD is the visual box — border, radius, background, shadow
     and the focus-within response all live here, not on the textarea. It is a
     column: the scroll region on top, a control strip below it. Because the
     strip is a real flow sibling, the textarea's scrollbar ends ABOVE the
     button by construction rather than by a reserved corner of padding. */
  .cx-composer__field {
    display: flex; flex-direction: column; flex: none;
    min-inline-size: 0;
    padding: var(--sp-3) var(--sp-3) var(--sp-3);
    border: 1px solid var(--line-2); border-radius: var(--r-2);
    background: var(--s-2);
    box-shadow: var(--lit);
    transition: border-color var(--t-0) var(--ease-settle),
                background-color var(--t-0) var(--ease-settle);
  }
  .cx-composer__field:hover { border-color: var(--line-3); }
  .cx-composer__field:focus-within { border-color: var(--accent); background: var(--s-3); }
  .cx-composer__input {
    display: block; inline-size: 100%; min-inline-size: 0;
    flex: 1 1 auto;
    /* two lines at rest, so the placeholder that states the keybinding is
       fully readable before a single character is typed; it grows from there
       to eight rows and then scrolls inside itself */
    min-block-size: 60px; max-block-size: 40dvh;
    padding: var(--sp-2) var(--sp-3);
    border: 0; border-radius: 0;
    background: transparent; color: var(--text-1);
    font: 500 14px/1.55 var(--font-sans);
    resize: none; overflow-y: auto;
  }
  .cx-composer__input::placeholder { color: var(--text-3); }
  .cx-composer__input:focus, .cx-composer__input:focus-visible { outline: none; }
  .cx-composer__input[disabled] { opacity: .45; cursor: not-allowed; }
  /* The control strip: counter leading, the one send/stop control trailing.
     It never collapses, so the button's box is a fixed landmark. */
  .cx-composer__strip {
    display: flex; align-items: center; justify-content: space-between;
    gap: var(--sp-3); flex: none;
    padding-inline: var(--sp-3);
    /* R4-U4 fix — the coarse-pointer tap overlay on the send button is 44px
       around a 34px box, i.e. it reaches 5px ABOVE the strip. Those 5px are
       the last of the old scrollbar overlap: without this inset they cover
       the bottom 5px of the textarea's scrollbar. The strip pays for its own
       overlay, so the scroll region ends where the target begins. */
    padding-block-start: 5px;
    min-block-size: 34px;
  }
  /* ONE button, two states, ONE element: send↔stop is a glyph redraw inside a
     box that never changes size — zero layout shift, by construction. */
  .cx-composer__send {
    position: relative; margin-inline-start: auto;
    display: inline-flex; align-items: center; justify-content: center;
    inline-size: 34px; block-size: 34px; padding: 0;
    border: 1px solid transparent; border-radius: var(--r-full);
    background: var(--accent); color: var(--accent-ink);
    cursor: pointer;
    transition: background-color var(--t-0) var(--ease-settle),
                color var(--t-0) var(--ease-settle),
                transform var(--t-1) var(--ease-settle);
  }
  .cx-composer__send svg { inline-size: var(--icon-ui); block-size: var(--icon-ui); }
  .cx-composer__send:hover { background: var(--accent-hover); }
  .cx-composer__send:active { transform: scale(.94); }
  /* Empty field: still THERE, so the corner never changes shape — just inert,
     with a real disabled attribute behind it. */
  .cx-composer__send[disabled] {
    background: var(--s-inset); border-color: var(--line-2); color: var(--text-4);
    cursor: not-allowed;
  }
  /* Working: the same element, a square glyph, the stop colour. */
  .cx-composer__send[data-mode="stop"] { background: var(--danger); }
  /* --danger-strong is a TEXT-on-tint token and in the dark palette it is
     defined equal to --danger, which would leave this hover with no feedback
     at all. The hover is therefore a darkening of the fill itself, which
     reads identically in both themes. */
  .cx-composer__send[data-mode="stop"]:hover {
    background: color-mix(in srgb, var(--danger) 82%, #000);
  }
  .cx-composer__count {
    font: 500 11px/1 var(--font-mono); letter-spacing: .08em;
    color: var(--text-3); font-variant-numeric: tabular-nums; white-space: nowrap;
  }
  .cx-composer__count:empty { display: none; }
  .cx-composer__count[data-tone="warn"] { color: var(--warn-strong); }
  .cx-composer__count[data-tone="over"] { color: var(--danger); }
  @media (pointer: coarse) {
    .cx-composer__input { min-block-size: var(--tap); }
    /* The button STAYS 34px optically — the 44px target is an invisible
       overlay, so the corner keeps its proportion on a phone. */
    .cx-composer__send::after {
      content: ''; position: absolute;
      inset: calc((34px - var(--tap)) / 2);
    }
  }

  /* ── 7.7 Reference chips. It never scrolls horizontally. ── */
  .cx-chiprow {
    display: flex; flex-wrap: wrap; gap: var(--sp-2);
    /* the chip row is the part that yields: it scrolls inside its own box
       rather than pushing the textarea off the bottom of the pane */
    flex: 0 1 auto; min-block-size: 0;
    max-block-size: 30dvh; overflow-y: auto; overflow-x: hidden;
    overscroll-behavior: contain;
  }
  .cx-chiprow[hidden] { display: none; }
  .cx-refchip {
    display: inline-flex; align-items: center; gap: var(--sp-3);
    block-size: 28px; padding-inline: var(--sp-4) var(--sp-2);
    border: 1px solid var(--line-2); border-radius: var(--r-full);
    background: var(--s-2); color: var(--text-1);
    max-inline-size: 220px;
    /* M-19. The chip DOES have children — a label, a glyph and a remove
       button — so it takes --ease-settle, not --ease-engage: the overshoot
       curve is banned on anything with children because it blurs type
       (AD §5.1, plan §5 law 6). No scale either, for the same reason. */
    --enter-y: 0px; --enter-s: 1;
    animation: cx-enter var(--t-1) var(--ease-settle) backwards;
    transition: background-color var(--t-0) var(--ease-settle),
                border-color var(--t-0) var(--ease-settle);
  }
  .cx-refchip:hover { background: var(--s-3); border-color: var(--line-3); }
  .cx-refchip svg { inline-size: var(--icon-chip); block-size: var(--icon-chip); color: var(--text-3); }
  .cx-refchip__label {
    font: 400 12px/1.40 var(--font-sans); letter-spacing: .005em;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }
  .cx-refchip__x {
    position: relative;
    display: inline-flex; align-items: center; justify-content: center;
    inline-size: 24px; block-size: 24px; flex: none;
    border-radius: var(--r-full); color: var(--text-3);
    transition: color var(--t-0) var(--ease-settle);
  }
  .cx-refchip__x::after {
    content: ""; position: absolute; inset: 50% auto auto 50%;
    inline-size: var(--tap); block-size: var(--tap); translate: -50% -50%;
  }
  .cx-refchip__x svg { inline-size: 12px; block-size: 12px; }
  .cx-refchip__x:hover { color: var(--danger); }

  /* ── 7.8 The empty transcript and the first-run welcome ── */
  .cx-chat__gate {
    display: flex; flex-direction: column; align-items: flex-start; gap: var(--sp-5);
    max-inline-size: 380px;
    /* R5-79 — a viewport-relative drop, never a percentage: a percentage
       block margin resolves against the CONTAINING BLOCK'S WIDTH, so the old
       34% moved this block VERTICALLY whenever the chat column's WIDTH
       changed (the pane swap, --chat-w-wide at ≥1921). Same fix as the
       .cx-welcome twin below. */
    margin-block: clamp(2rem, 12dvh, 8rem) auto;
  }
  .cx-chat__gate h2 {
    font: 600 24px/1.20 var(--font-sans); letter-spacing: -.022em; color: var(--text-display);
  }
  .cx-chat__gate p { font: 500 14px/1.55 var(--font-sans); color: var(--text-2); }
  .cx-chat__suggest { display: flex; flex-wrap: wrap; gap: var(--sp-3); }
  .cx-chat__suggest button {
    display: inline-flex; align-items: center; min-block-size: var(--h-sm);
    padding-inline: var(--sp-5);
    /* text buttons are --r-2 — see .cx-chat__jump */
    border: 1px solid var(--line-2); border-radius: var(--r-2);
    background: var(--s-2); color: var(--text-1);
    font: 400 13px/1.50 var(--font-sans);
    transition: background-color var(--t-0) var(--ease-settle),
                border-color var(--t-0) var(--ease-settle);
  }
  .cx-chat__suggest button:hover { background: var(--s-3); border-color: var(--line-3); }

  /* Permitted Fraunces use #1 — the only 44px type in the product, once per
     session. That restraint is what makes it land. */
  .cx-welcome {
    display: flex; flex-direction: column; align-items: flex-start; gap: var(--sp-5);
    max-inline-size: 420px;
    /* R5-79 — the headline's vertical drop is measured against the VIEWPORT,
       never the chat's inline size: the old margin-block: 30% auto resolved
       its block axis against the column's WIDTH, so the welcome drifted
       vertically as the chat got wider or narrower (worst at the
       --chat-w-wide ≥1921 engagement). clamp keeps a legible 2rem floor on
       short viewports (the ≤480 typography step below still applies at 320+)
       and parks the drop at 8rem on tall ones. */
    margin-block: clamp(2rem, 12dvh, 8rem) auto;
    --enter-y: 10px;
    animation: cx-enter var(--t-3) var(--ease-glide) backwards;
  }
  .cx-welcome h2 {
    font-family: var(--font-display); font-weight: 500;
    font-variation-settings: "opsz" 144, "SOFT" 100, "WONK" 1;
    font-size: 44px; line-height: 1.05; letter-spacing: -.035em;
    color: var(--text-display);
  }
  .cx-welcome p {
    font: 400 15px/1.65 var(--font-sans); letter-spacing: -.003em; color: var(--text-2);
  }
  @media (max-width: 480px) {
    .cx-welcome h2 { font-size: 32px; line-height: 1.12; letter-spacing: -.030em; }
    .cx-chat__log { padding-inline: var(--sp-4); }
    .cx-msg--user .cx-msg__body { max-inline-size: 100%; }
  }
  @media (max-width: 340px) {
    .cx-chat__log { padding-inline: var(--sp-3); }
  }

  /* Landscape phone with the keyboard open: the header becomes a 32px strip
     carrying the title only. It returns the instant the keyboard closes. */
  @media (max-height: 420px) {
    :root[data-kb="open"] .cx-chat__head { block-size: 32px; }
    :root[data-kb="open"] .cx-chat__meter,
    :root[data-kb="open"] .cx-chat__runstate { display: none; }
  }
}

/* ═══════════════════════ 8. THE PREVIEW SURFACES ═══════════════════════
   The user's page sits IN A MOUNT, not floating on the desk. The iframe
   never flashes white and it is never scaled.
   ═════════════════════════════════════════════════════════════════════ */

@layer panels {

  .cx-pv {
    position: relative;
    flex: 1 1 auto; min-block-size: 0; min-inline-size: 0;
    display: flex; flex-direction: column;
    container-type: inline-size;
  }
  .cx-pv[hidden] { display: none; }
  .cx-pv__body {
    position: relative;
    flex: 1 1 auto; min-block-size: 0; min-inline-size: 0;
    display: flex; flex-direction: column;
    overflow-y: auto;
  }
  .cx-pv__body--armed { cursor: crosshair; }

  /* ── 8.1 The frame plate. --s-1, --r-4, one hairline, --lit, and the
     sandboxed iframe inside it on --s-inset. ── */
  .cx-pv__plate {
    position: relative;
    flex: 1 1 auto; min-block-size: 0;
    margin: var(--gutter);
    border: 1px solid var(--line-2); border-radius: var(--r-4);
    background: var(--s-1); box-shadow: var(--lit);
    overflow: hidden;
    display: flex;
  }
  .cx-pv__frame {
    flex: 1 1 auto; inline-size: 100%; min-block-size: 0;
    border: 0; background: var(--s-inset);
    /* above 1920 the surplus is plate, not stretched page: a preview pulled
       to 2000px misrepresents the built site. The iframe is NEVER scaled and
       never gets a device frame — scaling breaks the selector's tap maths. */
    max-inline-size: 1600px; margin-inline: auto;
  }
  /* The plate's top hairline runs the aurora sweep while loading or
     streaming — the same one-layer mechanism as the working rule. */
  .cx-pv__hair {
    position: absolute; inset-inline: 0; inset-block-start: 0; z-index: 2;
    block-size: 2px; overflow: hidden; opacity: 0;
    transition: opacity var(--t-1) var(--ease-settle);
  }
  .cx-pv__hair[data-working="1"] { opacity: 1; }
  .cx-pv__hair[data-working="1"] .cx-rule__sweep {
    opacity: 1; will-change: transform;
    animation: cx-travel 2000ms var(--ease-travel) infinite;
  }

  /* ── 8.2 The never-white load. A --s-inset plate fades in, the iframe
     swaps behind it, the plate fades out. No spinner, no skeleton. ── */
  .cx-plate {
    position: absolute; inset: 0; z-index: 1;
    background: var(--s-inset);
    opacity: 0; pointer-events: none;
    transition: opacity var(--t-2) var(--ease-glide);
  }
  .cx-plate[data-on="1"] { opacity: 1; transition-duration: var(--t-1); }

  /* ── 8.3 Select-element mode: the hint pill. The highlight box itself is
     drawn INSIDE the sandboxed document by the injected inspector. ── */
  .cx-pv__hint {
    position: absolute; inset-block-start: var(--sp-6); inset-inline: 0;
    z-index: 3;
    margin-inline: auto; inline-size: max-content; max-inline-size: calc(100% - var(--sp-8));
    display: inline-flex; align-items: center; gap: var(--sp-3);
    min-block-size: var(--h-sm); padding-inline: var(--sp-5);
    border: 1px solid var(--line-2); border-radius: var(--r-full);
    background: var(--s-3); color: var(--text-1); box-shadow: var(--shadow-pop);
    font: 400 12px/1.40 var(--font-sans);
    /* A capsule stays a capsule: at --r-full a wrapped line turns the pill
       into a blob. It stays one line and truncates instead. */
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
    --enter-y: -6px;
    animation: cx-enter var(--t-1) var(--ease-settle) backwards;
  }
  .cx-pv__hint[hidden] { display: none; }

  /* R4-U7: the in-document twin of the highlight box the injected inspector
     draws inside a preview frame — same 2px accent border, same 8% accent
     fill, same 1px dark inner ring so it stays legible over a light creation.
     A POSITION tween only: width and height snap, exactly as in the frame. */
  .cx-pickbox {
    position: absolute; inset-block-start: 0; inset-inline-start: 0;
    z-index: 3; pointer-events: none;
    inline-size: 0; block-size: 0;
    border: 2px solid var(--accent);
    box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .35);
    background: color-mix(in srgb, var(--accent) 8%, transparent);
    transition: transform var(--t-0) var(--ease-settle);
    will-change: transform;
  }
  .cx-pickbox[hidden] { display: none; }

  /* ── 8.4 The publish milestone. Permitted Fraunces use #3: once, for four
     seconds, and the toast is suppressed when it renders. ── */
  .cx-milestone {
    position: absolute; inset: 0; z-index: 4;
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    gap: var(--sp-4);
    pointer-events: none; text-align: center;
    background: color-mix(in srgb, var(--s-1) 90%, transparent);
    --enter-y: 0px;
    animation: cx-enter var(--t-2) var(--ease-glide) backwards;
  }
  .cx-milestone[data-out="1"] { opacity: 0; transition: opacity var(--t-2) var(--ease-glide); }
  .cx-milestone__line {
    font-family: var(--font-display); font-weight: 500;
    font-variation-settings: "opsz" 144, "SOFT" 100, "WONK" 1;
    font-size: 24px; line-height: 1.20; letter-spacing: -.022em;
    color: var(--text-display);
  }
  .cx-milestone__url { font: 400 12px/1.45 var(--font-mono); color: var(--text-2); }

  /* ── 8.5 Diagnostics list, in the shared right drawer. Each entry is a
     group in the label-under-rule pattern. ── */
  .cx-diaglist { display: flex; flex-direction: column; gap: var(--sp-6); }
  .cx-diaglist__item { display: flex; flex-direction: column; gap: var(--sp-3); }
  .cx-diaglist__item::before {
    content: ""; display: block; block-size: 1px; background: var(--line-1);
  }
  .cx-diaglist__type {
    font: 500 11px/1.25 var(--font-mono); letter-spacing: .08em; text-transform: uppercase;
    color: var(--text-3);
  }
  .cx-diaglist__type[data-type="error"] { color: var(--danger); }
  .cx-diaglist__type[data-type="warning"] { color: var(--warn-strong); }
  .cx-diaglist__msg { font: 400 13px/1.50 var(--font-sans); color: var(--text-1); }
  .cx-diaglist__src { font: 400 12px/1.45 var(--font-mono); color: var(--text-3); word-break: break-all; }

  /* ── 8.6 The text-selection toolbar inside a Documents/Creations preview.
     Two verbs, both staging, neither sending. ── */
  .cx-seltool {
    position: absolute; z-index: var(--z-popover);
    display: inline-flex; align-items: center; gap: var(--sp-3);
    padding: var(--sp-3);
    border: 1px solid var(--line-2); border-radius: var(--r-3);
    background: var(--s-3); box-shadow: var(--shadow-pop);
  }
  .cx-seltool[hidden] { display: none; }

  /* ── 6.6c The publish pill's published-state menu (R2-2a). Republish, open
     the live surface, unpublish — the two orphans of the deleted ⋯ menu plus
     the verb that was already here. It is an ordinary popover; only its
     minimum measure is its own, so the three verbs never wrap. ── */
  .cx-publish__menu { min-inline-size: 210px; }

  /* ── 8.7 The diagnostics badge in the top bar's right slot ── */
  /* M-19. The badge carries a numeral, so — like the reference chip — it
     settles rather than overshooting: --ease-engage blurs type. */
  .cx-pv__diag {
    font-variant-numeric: tabular-nums;
    --enter-y: 0px; --enter-s: 1;
    animation: cx-enter var(--t-1) var(--ease-settle) backwards;
  }
  .cx-pv__diag[hidden] { display: none; }
}

/* ═══════════════════ 9. DRAG, DROP BAR, GHOST, LANDSCAPE ═══════════════════
   Every drag has a menu twin ("Reference in chat") on every draggable row, so
   a touch or keyboard user never needs the gesture. These are the gesture's
   own furniture, and they exist only while it is happening.
   ═════════════════════════════════════════════════════════════════════════ */

@layer chat {

  /* z 110 — deliberately ABOVE the toast at 100, so a pill can never occlude
     a live drop target. */
  .cx-dropbar {
    position: fixed; inset-inline: 0; inset-block-end: var(--safe-b);
    z-index: var(--z-drag);
    display: flex; align-items: center; justify-content: center;
    block-size: 56px;
    border-block-start: 2px dashed rgba(var(--accent-rgb), .45);
    background: var(--accent-tint);
    animation: cx-enter var(--t-1) var(--ease-settle) backwards;
  }
  .cx-dropbar__label { font: 500 14px/1 var(--font-sans); color: var(--accent-strong); }

  /* The ghost carries the LABEL only — a secret's value never leaves its
     row, not in the payload, not here, not in a tooltip. */
  .cx-dragghost {
    position: fixed; z-index: var(--z-drag);
    display: inline-flex; align-items: center;
    block-size: 28px; padding-inline: var(--sp-4);
    border: 1px solid var(--line-2); border-radius: var(--r-full);
    background: var(--s-3); color: var(--text-1);
    box-shadow: var(--shadow-pop);
    font: 400 12px/1 var(--font-sans);
    opacity: .9; pointer-events: none;
  }

  /* The composer as a mouse drop target. */
  .cx-dropping .cx-chiprow,
  .cx-composer.cx-dropping {
    background: var(--accent-tint);
    box-shadow: inset 0 0 0 1px var(--accent);
  }

  /* Landscape phone with the on-screen keyboard open: the section bar hides
     outright to give the composer the whole short viewport. R2-2a dropped the
     `more` menu that used to hold the four sections in this one edge case —
     owner-accepted: the bar returns the instant the keyboard closes. */
  @media (max-height: 420px) {
    :root[data-kb="open"] .cx-secbar { display: none; }
  }
}

/* ═══════════════════════════ 9. THE PAGES ═══════════════════════════
 *
 *  Stage 3. Everything that is not the shell, the workspace bar or the chat:
 *  Documents and Creations, Your files, Secrets, Project history, the Admin
 *  panel renderer, Settings, the project drawer and the remaining routes.
 *
 *  Three laws govern this whole section and are worth stating once:
 *
 *   1. A GROUP IS ANNOUNCED BY A RULE AND A LABEL UNDER IT — never by a box.
 *      There are no boxed group headers here, no card inside a card, and no
 *      second border used to solve a spacing problem.
 *   2. A ROW OR CARD LIFTS BY ONE SURFACE RUNG AND GAINS A DRAWN LINE. It
 *      never gains a shadow: the only two blurred shadows in the product are
 *      --shadow-pop and --shadow-modal, and both live on overlays.
 *   3. HORIZONTAL SCROLL IS RATIONED to four regions and nowhere else — the
 *      workspace section bar, the admin sections/tabs bar, the users tables
 *      and the requests inbox. Everything else wraps, stacks or collapses.
 * ═══════════════════════════════════════════════════════════════════════ */

@layer panels {

  /* ── 9.0 Ghost reset. The panel DOM is shared with the two legacy sheets
     until their import is cut in this same stage; these declarations keep a
     stale rule from leaking geometry into a new component. ── */
  .cx-viewhead, .cx-lp, .cx-lp__rows, .cx-lp__item,
  .cx-lp__row, .cx-lp__preview, .cx-files__head, .cx-files__bar, .cx-fgrid,
  .cx-fcard, .cx-fthumb, .cx-seclist, .cx-secrow, .cx-hist,
  .cx-hist__list, .cx-hist__row, .cx-settings, .cx-rows, .cx-segmented,
  .cx-table, .cx-tablewrap, .cx-projlist, .cx-projrow, .cx-filelist,
  .cx-filerow, .cx-filetree, .cx-fileitem, .cx-quota,
  .cx-ap, .cx-ap-blk, .cx-ap-rows, .cx-ap-row, .cx-ap-stats, .cx-ap-stat,
  .cx-ap-table, .cx-ap-fields, .cx-ap-field, .cx-ap-gallery, .cx-ap-tabbar,
  .cx-ap-rank, .cx-ap-progress, .cx-ap-list, .cx-ap-action,
  .cr-grid, .cr-card, .cu-section, .cu-chips, .cu-chip, .schema-rows,
  .schema-row, .block-list, .block-card, .format-grid, .format-card,
  .editor-layout, .cx-dropzone, .cf-cutout {
    margin: 0; padding: 0; inset: auto; box-shadow: none; float: none;
    border: 0; min-inline-size: 0; max-inline-size: none;
    background: none; text-transform: none; letter-spacing: normal;
  }

  /* The preview pane is its own container. Documents and Creations rearrange
     on the width of the PANE, never on the width of the window — the same
     pane at the same width must always look the same. */
  .cx-ws__preview { container-type: inline-size; container-name: pvpane; }

  /* ── 9.1 The page frame. Every routed page is head → groups. ── */
  .cx-viewhead {
    display: flex; flex-direction: column; gap: var(--sp-4);
    margin-block-end: var(--pad-section);
  }
  .cx-viewhead h2, .cx-viewhead .cx-heading {
    font: 600 24px/1.20 var(--font-sans); letter-spacing: -.022em;
    color: var(--text-display);
  }
  .cx-viewhead p { font: 400 13px/1.50 var(--font-sans); color: var(--text-2); max-inline-size: 68ch; }
  .cx-viewhead__actions { display: flex; flex-wrap: wrap; gap: var(--sp-5); margin-block-start: var(--sp-3); }

  .cx-muted { color: var(--text-3); }
  .cx-faint { color: var(--text-3); }  /* --text-4 may never carry information (AD §2.2) */
  .cx-emptynote { font: 400 13px/1.50 var(--font-sans); color: var(--text-3); }
  .cx-usageline, .cx-files__usage, .cx-blockcount {
    font: 400 12px/1.45 var(--font-mono); color: var(--text-3);
    font-variant-numeric: tabular-nums;
  }

  /* A page-level panel is a REGION, not a box. It is announced by a rule and
     a label; only rows and cards inside it carry surfaces. */
  .cx-panel + .cx-panel { margin-block-start: var(--pad-section); }

  /* ── 9.2 Settings-style rows. label left / value right above 1024px,
     stacked below 768px, one hairline between. No boxes. ── */
  .cx-rows { display: flex; flex-direction: column; }
  .cx-rows > .cx-row + .cx-row { border-block-start: 1px solid var(--line-1); }
  .cx-row {
    display: flex; align-items: center; gap: var(--sp-6);
    min-block-size: var(--tap); padding-block: var(--sp-4);
  }
  .cx-row__label { flex: 0 0 auto; font: 500 14px/1.55 var(--font-sans); color: var(--text-2); }
  .cx-row__value {
    flex: 1 1 auto; min-inline-size: 0;
    display: flex; align-items: center; justify-content: flex-end; gap: var(--sp-5);
    font: 500 14px/1.55 var(--font-sans); color: var(--text-1);
    text-align: end; overflow-wrap: anywhere;
  }
  @media (max-width: 768px) {
    .cx-row { flex-direction: column; align-items: stretch; gap: var(--sp-4); }
    .cx-row__value { justify-content: flex-start; text-align: start; }
  }

  /* ── 9.3 The three-state segmented control. Nothing slides: a sliding
     thumb on a three-item control is motion for its own sake. ── */
  .cx-segmented {
    display: inline-flex; align-items: center; gap: 2px;
    padding: 2px;
    border: 1px solid var(--line-2); border-radius: var(--r-2);
    background: var(--s-2);
  }
  .cx-segmented__item {
    display: inline-flex; align-items: center; justify-content: center;
    min-block-size: 30px; min-inline-size: 64px;
    padding-inline: var(--sp-5);
    border-radius: calc(var(--r-2) - 2px);
    font: 500 13px/1.50 var(--font-sans); color: var(--text-2);
    /* box-shadow is NOT on the animatable list (§5): the selected half's
       drawn accent ring appears with the colour change, in one frame. */
    transition: background-color var(--t-0) var(--ease-settle),
                color var(--t-0) var(--ease-settle);
  }
  .cx-segmented__item:hover { color: var(--text-1); }
  .cx-segmented__item[aria-pressed="true"] {
    background: var(--s-3);
    color: var(--accent-strong);
    box-shadow: inset 0 0 0 1px rgba(var(--accent-rgb), .40), var(--lit);
  }
  @media (pointer: coarse) { .cx-segmented__item { min-block-size: 40px; } }
  @media (max-width: 480px) {
    .cx-segmented { inline-size: 100%; }
    .cx-segmented__item { flex: 1 1 0; min-inline-size: 0; }
  }

  /* ── 9.4 The quota bar. A determinate meter, so it is filled with the flat
     --accent and NEVER the aurora: AD §2.5 and rule §10.2 both name "a progress
     bar fill" in their forbidden lists. Its meaning is always also written out
     in words beneath it; colour never carries it alone. ── */
  .cx-quota {
    inline-size: 100%; block-size: 4px;
    border-radius: var(--r-full);
    background: var(--s-inset);
    box-shadow: var(--well);
    overflow: hidden;
  }
  .cx-quota__fill {
    display: block; block-size: 100%;
    border-radius: var(--r-full);
    background: var(--accent);
    transform-origin: left center;
  }
  .cx-quota + .cx-usageline, .cx-quota + p { margin-block-start: var(--sp-4); }

  /* ── 9.5 Tables. A drawn ledger: a mono header, a hairline beneath it, a
     hairline between rows, tabular figures. No zebra striping, no vertical
     rules, no boxed cells. ── */
  .cx-tablewrap { inline-size: 100%; overflow-x: auto; overscroll-behavior-x: contain; }
  .cx-tablewrap--fit { overflow-x: visible; }
  .cx-table { inline-size: 100%; border-collapse: collapse; background: none; }
  .cx-table th, .cx-table td {
    padding: var(--sp-4) var(--sp-5);
    text-align: start; vertical-align: middle;
    white-space: nowrap;
  }
  .cx-table--wrap th, .cx-table--wrap td { white-space: normal; overflow-wrap: anywhere; }
  .cx-table thead th {
    font: 500 11px/1.25 var(--font-mono); letter-spacing: .08em; text-transform: uppercase;
    color: var(--text-3);
    border-block-end: 1px solid var(--line-2);
  }
  .cx-table tbody td {
    font: 400 13px/1.50 var(--font-sans); color: var(--text-1);
    border-block-start: 1px solid var(--line-1);
  }
  .cx-table tbody tr { transition: background-color var(--t-0) var(--ease-settle); }
  .cx-table tbody tr:hover { background: var(--s-2); }
  .cx-table td.cx-num, .cx-table th.cx-num { text-align: end; font-variant-numeric: tabular-nums; }
  /* The sticky identity column: the row stays nameable while its data
     scrolls. It is painted, never transparent, or the ink collides. */
  .cx-table--sticky th:first-child, .cx-table--sticky td:first-child {
    position: sticky; inset-inline-start: 0; z-index: 1;
    background: var(--s-1);
    box-shadow: 1px 0 0 var(--line-1);
  }
  .cx-table--sticky tbody tr:hover td:first-child { background: var(--s-2); }

  /* ── 9.6 Documents and Creations (§4.8, U3). ONE region: the list renders
     in the preview field and an item view replaces it in place. The panel has
     no bar of its own at any width — its controls ride the workspace's one
     top bar — so the arrangement is identical from 320 to 1440 and there is
     nothing left here that changes its mind on the pane's width. ── */
  .cx-lp {
    /* the containing block for the panel's own overlays: the selected-text
       toolbar and (R4-U7) the pick-mode hint pill and highlight box */
    position: relative;
    /* R4-U7 fix — the pick box is a child of THIS element, not of the
       scroller, so a scrolled-away highlight has to be clipped by the region
       it belongs to rather than painted over the bar above it. */
    overflow: hidden;
    flex: 1 1 auto; min-block-size: 0; min-inline-size: 0;
    display: flex; flex-direction: column;
    background: var(--s-1);
  }
  .cx-lp[hidden] { display: none; }
  .cx-pv__spacer { flex: 1 1 auto; min-inline-size: 0; }
  /* Above 480 the verb reads; the glyph is the small-screen form and nothing
     else, exactly as it was for the publish control. R5-61 left exactly one
     verb in an open item view — the creations' quiet "Save to your files" —
     so this two-form control now serves that one verb alone. */
  .cx-lp__act { position: relative; }
  .cx-lp__act svg { display: none; }

  /* The list is a plain block inside the one region; the region owns the
     scroll, so there is no second scroll container to trap a flick. */
  .cx-lp__rows { display: block; }
  .cx-lp__item { display: block; }
  .cx-lp__row {
    position: relative; inline-size: 100%;
    display: flex; align-items: center; gap: var(--sp-4);
    min-block-size: 44px; padding: var(--sp-3) var(--sp-4);
    border-radius: var(--r-2);
    text-align: start;
    transition: background-color var(--t-0) var(--ease-settle);
  }
  /* M-28 — the hover bar grows from the leading edge; it never fades in. */
  .cx-lp__row::before,
  .cx-secrow::before,
  .cx-hist__row::before,
  .cx-projrow::before {
    content: ""; position: absolute; inset-block: 6px; inset-inline-start: 0;
    inline-size: 2px; border-radius: var(--r-full);
    background: var(--aurora);
    transform: scaleX(0); transform-origin: left center;
    transition: transform var(--t-1) var(--ease-settle);
  }
  .cx-lp__row:hover, .cx-lp__row:focus-visible { background: var(--s-2); }
  .cx-lp__row:hover::before { transform: scaleX(1); }
  .cx-lp__row[aria-selected="true"] { background: var(--s-2); }
  .cx-lp__row[aria-selected="true"]::before { transform: scaleX(1); background: var(--accent); }
  .cx-lp__rowname {
    flex: 1 1 auto; min-inline-size: 0;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    font: 500 13px/1.50 var(--font-sans); color: var(--text-1);
  }
  .cx-lp__row[aria-selected="true"] .cx-lp__rowname { color: var(--text-display); }
  .cx-lp__rowmeta {
    flex: none;
    font: 500 11px/1.25 var(--font-mono); letter-spacing: .08em; text-transform: uppercase;
    color: var(--text-3);
  }
  /* Trailing mono micro, tabular so a column of dates lines up. It is the
     quietest thing in the row: the name is what you are looking for. */
  .cx-lp__rowdate {
    flex: none;
    font: 400 11px/1.25 var(--font-mono); font-variant-numeric: tabular-nums;
    letter-spacing: .02em; color: var(--text-3);
  }
  /* The agent may offer the region; it may not steal it from a reader. */
  .cx-lp__fresh { display: block; margin-block-end: var(--sp-4); }
  .cx-lp__freshbtn {
    inline-size: 100%; justify-content: flex-start;
    border-color: color-mix(in srgb, var(--warn) 40%, transparent);
    background: var(--warn-tint);
    color: var(--warn-strong);
  }
  .cx-lp__freshbtn:hover { border-color: color-mix(in srgb, var(--warn) 70%, transparent); }

  .cx-lp__preview {
    flex: 1 1 auto; min-inline-size: 0; min-block-size: 0;
    overflow: auto; overscroll-behavior: contain;
    padding: var(--gutter);
    background: var(--s-1);
  }
  .cx-lp__frame {
    /* R5-94 — the floor is sized against the PANE the user reads, not the
       window behind it: the pane is the pvpane container (§9.0), so 60cqw
       tracks its inline size. Block-size 100% still fills the region; this
       floor only stops a wide-short pane from collapsing the document to a
       sliver, and it now does so relative to the box the frame actually
       sits in. */
    inline-size: 100%; block-size: 100%; min-block-size: 60cqw;
    border: 1px solid var(--line-2); border-radius: var(--r-3);
    background: var(--s-inset);
    box-shadow: var(--lit);
  }
  /* R5-64 — the draft item view: one honest state line over the frame. A
     draft says it is not a file yet; a saved draft names the real file it
     became, linked once. The frame itself is the same component as a legacy
     PDF preview — one frame, one look. */
  .cx-lp__docwrap {
    display: flex; flex-direction: column; gap: var(--sp-4);
    block-size: 100%; min-block-size: 0;
  }
  .cx-lp__docwrap .cx-lp__frame { flex: 1 1 auto; }
  .cx-lp__docmeta { display: flex; align-items: center; gap: var(--sp-4); flex-wrap: wrap; }
  .cx-lp__dochint { font: 400 12px/1.40 var(--font-sans); color: var(--text-3); }
  .cx-lp__doclink {
    font: 500 12px/1.40 var(--font-sans); color: var(--accent-strong);
    text-decoration: underline; text-underline-offset: 2px;
  }
  /* M-29 — a 2% lift you feel rather than see. */
  .cx-lp__preview img, .cx-lp__preview video, .cx-lp__preview svg {
    max-inline-size: 100%; block-size: auto; margin-inline: auto;
    border-radius: var(--r-2);
    transition: filter var(--t-2) var(--ease-settle);
  }
  .cx-lp__preview img:hover { filter: brightness(1.02) contrast(.98); }

  /* Under 420px of PANE the row gives up its date rather than ellipsising the
     name it exists to show. The badge stays: it is two words at most and it
     is what tells a PDF from a spreadsheet at a glance. */
  @container pvpane (max-width: 420px) {
    .cx-lp__rowdate { display: none; }
  }

  /* ── 9.7 Your files (§4.9). One component, two homes: the page and the
     right drawer. Different clamps, never a second component. ── */
  .cx-files__head { display: flex; flex-direction: column; gap: var(--sp-5); }
  .cx-files__bar {
    display: flex; align-items: center; flex-wrap: wrap; gap: var(--sp-5);
  }
  .cx-files__progress {
    font: 500 11px/1.25 var(--font-mono); letter-spacing: .08em; text-transform: uppercase;
    color: var(--text-3);
  }
  .cx-fgrid {
    display: grid; gap: var(--sp-5);
    grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
    margin-block-start: var(--sp-5);
  }
  .cx-fgrid__empty { grid-column: 1 / -1; }
  .cx-fcard {
    position: relative;
    display: flex; flex-direction: column; gap: var(--sp-4);
    padding: var(--sp-4);
    border: 1px solid var(--line-2); border-radius: var(--r-3);
    background: var(--s-2);
    box-shadow: var(--lit);
    overflow: hidden;
    transition: background-color var(--t-0) var(--ease-settle),
                border-color var(--t-0) var(--ease-settle);
  }
  .cx-fcard:hover { background: var(--s-3); border-color: var(--line-3); }
  .cx-fcard:hover .cx-fthumb { filter: brightness(1.02) contrast(.98); }
  .cx-fcard[data-busy="1"] { opacity: .75; }
  /* T1 BUG A — the explicit preview-open control. It wraps the thumb, the
     name and the meta, so the primary tap target is everything left of the
     row's own actions: a mid-row Download affordance can no longer swallow a
     centred tap on a narrow list row and read as a dead popup. The wrapper is
     a style reset: the card keeps its exact previous geometry (grid stacks,
     list rows), only the hit area changes. */
  .cx-fcard__open {
    display: flex; flex-direction: column;
    gap: var(--sp-4);
    min-inline-size: 0;
    margin: 0; padding: 0;
    border: 0; background: none;
    color: inherit; font: inherit;
    text-align: start;
    cursor: pointer;
  }
  .cx-fcard__text {
    display: flex; flex-direction: column;
    gap: var(--sp-4);
    min-inline-size: 0;
  }
  .cx-fthumb {
    position: relative; aspect-ratio: 4 / 3;
    display: flex; align-items: center; justify-content: center;
    border-radius: var(--r-2);
    background: var(--s-inset);
    box-shadow: var(--well);
    overflow: hidden;
    transition: filter var(--t-2) var(--ease-settle);
  }
  .cx-fthumb img { inline-size: 100%; block-size: 100%; object-fit: cover; }
  /* --text-3 is only certified down to --s-2; in a --s-inset well it measures
     4.33:1, so anything informational inside a well takes --text-2. */
  .cx-fthumb__ext {
    font: 500 11px/1.25 var(--font-mono); letter-spacing: .08em; text-transform: uppercase;
    color: var(--text-2);
  }
  .cx-fthumb--doc { background: var(--s-inset); }
  .cx-fthumb--failed { box-shadow: var(--well), inset 0 0 0 1px color-mix(in srgb, var(--danger) 50%, transparent); }
  .cx-fcard__name {
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
    overflow: hidden;
    font: 500 13px/1.50 var(--font-sans); color: var(--text-1);
    overflow-wrap: anywhere;
  }
  .cx-fcard__meta { font: 400 12px/1.40 var(--font-sans); color: var(--text-3); }
  /* On the danger tint --text-3 falls to 4.33:1, and this line carries the
     actual error sentence — it steps up to --text-2 (5.36:1). */
  .cx-fcard--failed .cx-fcard__meta { color: var(--text-2); }
  .cx-fcard__actions { display: flex; flex-wrap: wrap; gap: var(--sp-3); margin-block-start: auto; }
  /* Uploading: a 2px aurora rule across the card's bottom edge. No spinner. */
  .cx-fcard[data-uploading="1"]::after {
    content: ""; position: absolute; inset-block-end: 0; inset-inline: 0;
    block-size: 2px; background: var(--aurora-rule);
    transform: scaleX(var(--progress, 0)); transform-origin: left center;
    transition: transform var(--t-2) var(--ease-settle);
  }
  .cx-fcard--failed { border-color: color-mix(in srgb, var(--danger) 40%, transparent); background: var(--danger-tint); }

  /* The whole list is a drop zone on a fine pointer — but only WHILE a drag
     is over it. A permanent dashed box is furniture nobody asked for. */
  .cx-dropzone { position: relative; border-radius: var(--r-3); }
  .cx-dropzone.dragover {
    background: var(--accent-tint);
    box-shadow: inset 0 0 0 2px rgba(var(--accent-rgb), .45);
  }
  .dropzone {
    display: flex; flex-direction: column; align-items: flex-start; gap: var(--sp-4);
    padding: var(--pad-panel);
    border: 1px dashed var(--line-3); border-radius: var(--r-3);
    background: var(--s-2);
  }
  .dropzone.dragover { border-color: var(--accent); background: var(--accent-tint); }
  .dropzone-title { font: 500 14px/1.55 var(--font-sans); color: var(--text-1); }
  .dropzone-hint { font: 400 12px/1.40 var(--font-sans); color: var(--text-3); }

  @media (max-width: 480px) {
    .cx-fgrid { grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); }
  }
  /* The density floor: below 340px a grid is a lie. It becomes list rows. */
  @media (max-width: 340px) {
    .cx-fgrid { grid-template-columns: 1fr; gap: var(--sp-3); }
    /* In row mode the card's own actions sit at the trailing edge, exactly
       where the corner "to chat" twin (§9.19b) is placed — so the card
       reserves one tap target there and its actions step inward. */
    .cx-fcard {
      flex-direction: row; align-items: center; gap: var(--sp-5);
      padding-inline-end: calc(var(--sp-4) + var(--tap));
    }
    .cx-fcard__open { flex-direction: row; align-items: center; gap: var(--sp-5); flex: 1 1 auto; }
    .cx-fcard__text { flex-direction: row; align-items: center; gap: var(--sp-5); flex: 1 1 auto; min-inline-size: 0; }
    .cx-fthumb { inline-size: 40px; block-size: 40px; aspect-ratio: 1; flex: none; }
    .cx-fcard__actions { margin-block-start: 0; }
  }

  /* ── The LIST presentation of the same library (B2). The card markup is
     identical in both views — only this block changes what it is. Before it
     existed the class toggled and nothing moved, which is why the switcher
     read as broken. Rows, not cards: no card border, no card shadow, one
     hairline between rows, the same hover language as every other row in the
     product. The thumbnail becomes a small leading square and keeps the very
     same lazy pipeline (the holder element is unchanged, so the
     IntersectionObserver path works identically in list and grid). ── */
  .cx-flist {
    display: flex; flex-direction: column;
    margin-block-start: var(--sp-5);
    border-block-start: 1px solid var(--line-1);
  }
  .cx-flist > .cx-fcard {
    flex-direction: row; align-items: center; gap: var(--sp-5);
    /* The trailing inset reserves the corner "to chat" twin's tap target,
       exactly as the ≤340px row mode does. */
    padding: var(--sp-4) calc(var(--sp-5) + var(--tap)) var(--sp-4) var(--sp-5);
    border: 0; border-block-end: 1px solid var(--line-1);
    border-radius: 0;
    background: transparent;
    box-shadow: none;
  }
  /* T1 BUG A — the open control lays its children out in a ROW inside a list
     row (thumb, then name + meta side by side, exactly the old lineup), and
     stretches across everything before the actions. */
  .cx-flist > .cx-fcard .cx-fcard__open {
    flex-direction: row; align-items: center; gap: var(--sp-5);
    flex: 1 1 auto;
  }
  .cx-flist > .cx-fcard .cx-fcard__text {
    flex-direction: row; align-items: center; gap: var(--sp-5);
    flex: 1 1 auto;
  }
  .cx-flist > .cx-fcard:hover,
  .cx-flist > .cx-fcard:focus-visible { background: var(--s-2); border-color: var(--line-1); }
   .cx-flist .cx-fthumb {
     inline-size: 40px; block-size: 40px; aspect-ratio: 1; flex: none;
     border-radius: var(--r-2);
   }
   /* D3 — the FULL kind label ('VIDEO', not the authored 'VIDE' cut) has
      to sit inside the list view's 40px square: one honest size step and
      letter-spacing trim, no fixed width, no clipping. */
   .cx-flist .cx-fthumb__ext {
     font-size: 10px; letter-spacing: .04em;
     padding-inline: var(--sp-1);
     overflow-wrap: anywhere; text-align: center;
   }
  .cx-flist .cx-fcard__name {
    flex: 1 1 auto; min-inline-size: 0;
    -webkit-line-clamp: 1;
    font: 500 13px/1.50 var(--font-sans);
  }
  /* kind / size / date, in the product's mono microlabel voice. */
  .cx-flist .cx-fcard__meta {
    flex: 0 1 auto; min-inline-size: 0;
    font: 500 11px/1.25 var(--font-mono); letter-spacing: .08em; text-transform: uppercase;
    color: var(--text-3);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }
  .cx-flist .cx-fcard__actions { flex: none; margin-block-start: 0; flex-wrap: nowrap; }
  .cx-flist .cx-fgrid__empty { border-block-end: 0; }
  /* 320px-safe: the meta line drops under the name rather than crushing it,
     staying aligned to the text column. Nothing is ever hidden. */
  @media (max-width: 480px) {
    .cx-flist > .cx-fcard { flex-wrap: wrap; row-gap: var(--sp-3); }
    .cx-flist > .cx-fcard .cx-fcard__text {
      flex: 1 1 auto; flex-wrap: wrap;
      row-gap: var(--sp-3);
    }
    .cx-flist .cx-fcard__meta {
      flex: 1 1 100%; white-space: normal;
      /* The old rule indented past the leading thumb from the ROW's edge;
         inside the open control the text column already starts there, so no
         extra offset is needed for the same alignment. */
      margin-inline-start: 0;
    }
    .cx-flist .cx-fcard__actions { margin-inline-start: auto; flex-wrap: wrap; }
  }

  /* ── File preview, on the shared modal surface (B3). Never a new window:
     the stage is the same inset well the cutout modal uses, so a photo, a
     clip and a text file all arrive in one place. ── */
  .cx-fprev { display: flex; flex-direction: column; gap: var(--sp-5); }
  .cx-fprev__stage {
    display: flex; align-items: center; justify-content: center;
    min-block-size: 180px; padding: var(--sp-5);
    border-radius: var(--r-3);
    background: var(--s-inset);
    box-shadow: var(--well);
    overflow: hidden;
  }
  .cx-fprev__img {
    max-inline-size: 100%; max-block-size: 52dvh;
    border-radius: var(--r-2);
  }
  /* F-5c — a saved video plays FITTED: like an image, it scales inside
     both bounds (width and 52dvh), with the clip's true aspect set on
     loadedmetadata. No 300x150 default, no letterbox frame. */
  .cx-fprev__media {
    inline-size: auto; block-size: auto;
    max-inline-size: 100%; max-block-size: 52dvh;
    border-radius: var(--r-2);
    background: var(--s-inset);
  }
  .cx-fprev__audio { inline-size: 100%; }
  .cx-fprev__frame {
    inline-size: 100%; block-size: 52dvh;
    border: 0; border-radius: var(--r-2);
    background: var(--s-1);
  }
  .cx-fprev__textwrap { inline-size: 100%; display: flex; flex-direction: column; gap: var(--sp-4); }
  .cx-fprev__text {
    margin: 0; max-block-size: 46dvh; overflow: auto;
    white-space: pre-wrap; overflow-wrap: anywhere;
    /* In a --s-inset well informational text takes --text-2, never --text-3. */
    font: 400 13px/1.60 var(--font-mono); color: var(--text-2);
  }
  .cx-fprev__none { display: flex; flex-direction: column; align-items: center; gap: var(--sp-4); text-align: center; }
  /* T1 BUG B — the spec-rendered design of an unsaved creation rides in the
     stage inside the Creations tab's own card markup. Neutralise the outer
     list chrome so only the well, the design and its meta show; the stage
     cap bounds it like every other preview kind. */
  .cx-fprev__spec {
    inline-size: 100%;
    max-block-size: 52dvh;
    overflow-y: auto;
  }
  .cx-fprev__spec .cr-grid { margin-block-start: 0; }
  .cx-fprev__spec .cr-card {
    border: 0; background: none; box-shadow: none;
    padding: 0;
  }
  .cx-fprev__spec:hover,
  .cx-fprev__spec .cr-card:hover { background: none; border-color: transparent; }
  .cx-fprev__badge {
    padding: var(--sp-2) var(--sp-4);
    border: 1px solid var(--line-2); border-radius: var(--r-2);
    font: 500 11px/1.25 var(--font-mono); letter-spacing: .08em; text-transform: uppercase;
    color: var(--text-2);
  }
  .cx-fprev__nonetitle { font: 500 14px/1.55 var(--font-sans); color: var(--text-1); }
  .cx-fprev__status, .cx-fprev__trunc, .cx-fprev__meta {
    font: 500 11px/1.25 var(--font-mono); letter-spacing: .08em; text-transform: uppercase;
    color: var(--text-3);
  }
  .cx-fprev__status, .cx-fprev__trunc { color: var(--text-2); }

  /* Background removal, in the shared drawer. */
  .cf-cutout { display: flex; flex-direction: column; gap: var(--sp-6); }
  .cf-cutout-stage {
    display: flex; align-items: center; justify-content: center;
    min-block-size: 200px; padding: var(--sp-5);
    border-radius: var(--r-3);
    background: var(--s-inset);
    box-shadow: var(--well);
  }
  .cf-cutout-img { max-inline-size: 100%; max-block-size: 46dvh; border-radius: var(--r-2); }
  .cf-cutout-row { display: flex; align-items: center; gap: var(--sp-5); flex-wrap: wrap; }
  .cf-cutout-cell { flex: 1 1 220px; min-inline-size: 0; }
  .cf-cutout-tolerance { inline-size: 100%; accent-color: var(--accent); }
  .cf-cutout-tag {
    font: 500 11px/1.25 var(--font-mono); letter-spacing: .08em; text-transform: uppercase;
    color: var(--text-3);
  }
  .cf-cutout-status {
    position: relative;
    padding-block-start: var(--sp-4);
    font: 500 11px/1.25 var(--font-mono); letter-spacing: .08em; text-transform: uppercase;
    color: var(--text-2);
  }
  .cf-cutout-status[data-working="1"]::before {
    content: ""; position: absolute; inset-block-start: 0; inset-inline: 0;
    block-size: 2px; border-radius: var(--r-full);
    background: var(--aurora-rule);
    -webkit-mask-image: linear-gradient(90deg, transparent, #000 22%, #000 78%, transparent);
    mask-image: linear-gradient(90deg, transparent, #000 22%, #000 78%, transparent);
  }

  /* ── 9.8 Secrets (§4.10). A value never appears in any node, payload,
     tooltip or staged string — only booleans, masks and labels do. ── */
  .cx-seclist {
    display: flex; flex-direction: column; gap: var(--sp-4);
    inline-size: 100%; max-inline-size: 1000px;
  }
  .cx-seclist__empty { display: block; }
  .cx-secrow {
    position: relative;
    display: grid; align-items: center;
    grid-template-columns: minmax(0, 1fr) auto auto auto;
    gap: var(--sp-5);
    min-block-size: 64px; padding: var(--sp-4) var(--sp-5);
    border: 1px solid var(--line-2); border-radius: var(--r-3);
    background: var(--s-2);
    box-shadow: var(--lit);
    transition: background-color var(--t-0) var(--ease-settle),
                border-color var(--t-0) var(--ease-settle);
  }
  .cx-secrow:hover { background: var(--s-3); border-color: var(--line-3); }
  .cx-secrow:hover::before { transform: scaleX(1); }
  .cx-secrow::before { inset-block: 10px; }
  .cx-secrow[data-busy="1"] { opacity: .5; }
  .cx-secrow__main { display: flex; flex-direction: column; gap: var(--sp-2); min-inline-size: 0; }
  .cx-secrow__label { font: 500 14px/1.55 var(--font-sans); color: var(--text-1); overflow-wrap: anywhere; }
  .cx-secrow__meta { font: 400 12px/1.40 var(--font-sans); color: var(--text-3); }
  .cx-secrow__state { display: inline-flex; align-items: center; gap: var(--sp-4); }
  .cx-secrow__mask { font: 400 12px/1.45 var(--font-mono); color: var(--text-3); letter-spacing: .06em; }
  /* Filled / unfilled booleans. Never a value, never a length. */
  .cx-secrow__dots { display: inline-flex; align-items: center; gap: var(--sp-3); }
  .cx-secrow__dots .cx-dot { inline-size: 6px; block-size: 6px; }

  .cx-secform__fields { display: flex; flex-direction: column; gap: var(--sp-5); }
  .cx-secform__row { display: flex; align-items: flex-end; gap: var(--sp-4); }
  .cx-secform__row > .cx-field { flex: 1 1 auto; min-inline-size: 0; }
  /* The reveal toggle is a 44px twin sitting inside the field's own box. */
  .cx-reveal { position: relative; display: flex; align-items: center; }
  .cx-reveal > .cx-input { padding-inline-end: 44px; }
  .cx-reveal > button {
    position: absolute; inset-inline-end: 0;
    inline-size: 44px; block-size: 100%; min-block-size: var(--tap);
    display: inline-flex; align-items: center; justify-content: center;
    color: var(--text-3); border-radius: var(--r-2);
    transition: color var(--t-0) var(--ease-settle);
  }
  .cx-reveal > button:hover { color: var(--text-1); }
  .cx-reveal > button svg { inline-size: var(--icon-ui); block-size: var(--icon-ui); }

  @media (max-width: 768px) {
    .cx-secrow {
      grid-template-columns: minmax(0, 1fr) auto;
      grid-template-areas: "main more" "badge more" "state more";
      row-gap: var(--sp-4);
    }
    .cx-secrow__main { grid-area: main; }
    .cx-secrow > .cx-pill { grid-area: badge; justify-self: start; }
    .cx-secrow__state { grid-area: state; justify-self: start; }
    .cx-secrow > .cx-iconbtn { grid-area: more; align-self: start; }
    /* The corner "to chat" twin (§9.19b) turns on at this same breakpoint and
       is absolutely placed against the row's inner trailing edge. The row
       therefore RESERVES one tap target there, which steps its own `more`
       button inward by exactly that much — the twin never lands on top of the
       menu that carries Edit and Delete. */
    .cx-secrow { padding-inline-end: calc(var(--sp-5) + var(--tap)); }
  }

  /* ── 9.9 Project history (§4.11). Friendly prose only — never a hash,
     never an id. ── */
  .cx-hist { inline-size: 100%; max-inline-size: 880px; margin-inline: auto; }
  .cx-hist__list { display: flex; flex-direction: column; gap: var(--sp-4); }
  .cx-hist__row {
    position: relative;
    display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center;
    gap: var(--sp-5);
    min-block-size: 64px; padding: var(--sp-4) var(--sp-5);
    border: 1px solid var(--line-2); border-radius: var(--r-3);
    background: var(--s-2);
    box-shadow: var(--lit);
    overflow: hidden;
    transition: background-color var(--t-0) var(--ease-settle),
                border-color var(--t-0) var(--ease-settle);
  }
  .cx-hist__row:hover { background: var(--s-3); border-color: var(--line-3); }
  .cx-hist__row:hover::before { transform: scaleX(1); }
  .cx-hist__row::before { inset-block: 10px; }
  .cx-hist__main { display: flex; flex-direction: column; gap: var(--sp-2); min-inline-size: 0; }
  .cx-hist__label { font: 500 14px/1.55 var(--font-sans); color: var(--text-1); }
  .cx-hist__meta { font: 400 12px/1.40 var(--font-sans); color: var(--text-3); }
  .cx-hist__action { flex: none; }
  /* Restoring: the aurora crosses the row's own bottom edge. */
  .cx-hist__row[data-busy="1"]::after {
    content: ""; position: absolute; inset-block-end: 0; inset-inline-start: 0;
    inline-size: 240px; block-size: 2px;
    background: var(--aurora-rule);
    -webkit-mask-image: linear-gradient(90deg, transparent, #000 22%, #000 78%, transparent);
    mask-image: linear-gradient(90deg, transparent, #000 22%, #000 78%, transparent);
    animation: cx-travel 2000ms var(--ease-travel) infinite;
    will-change: transform;
  }
  .cx-hist__consequences { display: flex; flex-direction: column; gap: var(--sp-4); }
  @media (max-width: 480px) {
    .cx-hist__row { grid-template-columns: 1fr; }
    .cx-hist__action { inline-size: 100%; min-block-size: var(--tap); }
  }

  /* ── 9.10 The admin panel renderer (§4.12). The agent chooses blocks and
     grouping; it never chooses layout mechanics — so a panel authored for a
     desktop-shaped project is automatically right on a phone. ── */
  .cx-ap, .cx-ap__body { display: flex; flex-direction: column; min-inline-size: 0; }
  .cx-ap { container-type: inline-size; container-name: appanel; }
  .cx-ap__head { margin-block-end: var(--pad-section); }
  .cx-ap-body { display: flex; flex-direction: column; }

  /* Every block is announced by a rule and the label beneath it. Depth is
     expressed by rule WEIGHT, never by a box. There is no third tier. */
  .cx-ap-blk { display: block; }
  .cx-ap-blk + .cx-ap-blk { margin-block-start: var(--pad-section); }
  .cx-ap-blk__head {
    display: flex; align-items: center; flex-wrap: wrap; gap: var(--sp-4);
    padding-block-start: var(--sp-4);
    border-block-start: 1px solid var(--line-2);
  }
  .cx-ap-blk__title {
    font: 500 11px/1.25 var(--font-mono); letter-spacing: .08em; text-transform: uppercase;
    color: var(--text-3);
  }
  .cx-ap-blk__spacer { flex: 1 1 auto; min-inline-size: 0; }
  .cx-ap-blk__tools { display: flex; align-items: center; gap: var(--sp-4); }
  .cx-ap-blk__more { display: none; }
  .cx-ap-blk__body { margin-block-start: var(--sp-5); }
  .cx-ap-section { display: block; }
  .cx-ap-section + .cx-ap-section { margin-block-start: var(--pad-section); }
  .cx-ap-section__title {
    display: block; padding-block-start: var(--sp-4);
    border-block-start: 1px solid var(--line-1);
    font: 500 11px/1.25 var(--font-mono); letter-spacing: .08em; text-transform: uppercase;
    color: var(--text-3);
  }
  .cx-ap-stack { display: flex; flex-direction: column; gap: var(--sp-5); }
  .cx-ap-note, .cx-ap-prose {
    font: 400 15px/1.65 var(--font-sans); letter-spacing: -.003em;
    color: var(--text-1); max-inline-size: 72ch;
  }
  .cx-ap-note { color: var(--text-2); font-size: 13px; line-height: 1.5; }
  /* R5-01 — the read-only card a public-data field shows when its stored
     value is not a scalar string: an inset well in the sheet's own code
     voice, never an editable textarea seeded with JSON. */
  .cx-ap-structured {
    padding: var(--sp-5);
    border-radius: var(--r-2);
    background: var(--s-inset);
    box-shadow: var(--well);
    color: var(--text-2);
    font: 400 13px/1.50 var(--font-mono);
    overflow-wrap: anywhere;
  }
  @media (max-width: 480px) { .cx-ap-prose { max-inline-size: 66ch; } }
  .cx-ap-loading {
    display: inline-flex; align-items: center; gap: var(--sp-4);
    font: 500 11px/1.25 var(--font-mono); letter-spacing: .08em; text-transform: uppercase;
    color: var(--text-3);
  }
  .cx-ap-fail { color: var(--danger); font: 400 12px/1.40 var(--font-sans); }
  .cx-ap-empty { display: block; }
  .cx-ap-saved {
    font: 500 11px/1.25 var(--font-mono); letter-spacing: .08em; text-transform: uppercase;
    color: var(--ok-strong);
  }

  /* stat tiles — a value and a label. No icon, no sparkline, no colour fill. */
  .cx-ap-stats {
    display: grid; gap: var(--sp-5);
    grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  }
  .cx-ap-stat {
    display: flex; flex-direction: column; gap: var(--sp-3);
    padding: var(--sp-5);
    border: 1px solid var(--line-2); border-radius: var(--r-3);
    background: var(--s-2);
    box-shadow: var(--lit);
  }
  .cx-ap-stat__value {
    font: 600 24px/1.20 var(--font-sans); letter-spacing: -.022em;
    color: var(--text-display); font-variant-numeric: tabular-nums;
  }
  .cx-ap-stat__label {
    font: 500 11px/1.25 var(--font-mono); letter-spacing: .08em; text-transform: uppercase;
    color: var(--text-3);
  }

  /* rows / requests */
  .cx-ap-rows { display: flex; flex-direction: column; gap: var(--sp-4); }
  .cx-ap-row {
    position: relative;
    display: flex; align-items: center; gap: var(--sp-5); flex-wrap: wrap;
    min-block-size: var(--tap); padding: var(--sp-4) var(--sp-5);
    border: 1px solid var(--line-2); border-radius: var(--r-3);
    background: var(--s-2);
    box-shadow: var(--lit);
    transition: background-color var(--t-0) var(--ease-settle),
                border-color var(--t-0) var(--ease-settle);
  }
  .cx-ap-row:hover { background: var(--s-3); border-color: var(--line-3); }
  .cx-ap-row__more { margin-inline-start: auto; }
  .cx-ap-reply { display: flex; flex-direction: column; gap: var(--sp-4); }
  .cx-ap-textarea { min-block-size: 96px; resize: none; }
  .cx-ap-atts { display: flex; flex-wrap: wrap; gap: var(--sp-4); }
  .cx-ap-atts__size { color: var(--text-3); font: 400 12px/1.45 var(--font-mono); }
  .cx-ap-detail { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: var(--sp-4) var(--sp-6); }
  .cx-ap-detail__k {
    font: 500 11px/1.25 var(--font-mono); letter-spacing: .08em; text-transform: uppercase;
    color: var(--text-3);
  }
  .cx-ap-detail__v { font: 400 13px/1.50 var(--font-sans); color: var(--text-1); overflow-wrap: anywhere; }
  @media (max-width: 480px) { .cx-ap-detail { grid-template-columns: 1fr; gap: var(--sp-2) 0; } }

  .cx-ap-filter { max-inline-size: 220px; }
  /* Export CSV is renderer-owned and keeps two homes: inline in the block
     header at ≥768px of panel, and in the header's sheet below it. Eight
     pixels of dead space always separate it from the filter beside it. */
  .cx-ap-export { margin-inline-start: var(--sp-4); }
  @container appanel (max-width: 520px) {
    .cx-ap-blk__tools { inline-size: 100%; }
    .cx-ap-filter { max-inline-size: none; flex: 1 1 auto; }
    .cx-ap-export { inline-size: 100%; margin-inline-start: 0; margin-block-start: var(--sp-4); }
  }

  .cx-ap-tablewrap { inline-size: 100%; overflow-x: auto; overscroll-behavior-x: contain; }
  .cx-ap-table { inline-size: 100%; border-collapse: collapse; }
  .cx-ap-table th, .cx-ap-table td { padding: var(--sp-4) var(--sp-5); text-align: start; white-space: nowrap; }
  .cx-ap-table thead th {
    font: 500 11px/1.25 var(--font-mono); letter-spacing: .08em; text-transform: uppercase;
    color: var(--text-3); border-block-end: 1px solid var(--line-2);
  }
  .cx-ap-table tbody td { font: 400 13px/1.50 var(--font-sans); color: var(--text-1); border-block-start: 1px solid var(--line-1); }
  .cx-ap-table tbody tr:hover { background: var(--s-2); }
  .cx-ap-table th:first-child, .cx-ap-table td:first-child {
    position: sticky; inset-inline-start: 0; z-index: 1;
    background: var(--s-1); box-shadow: 1px 0 0 var(--line-1);
  }
  .cx-ap-table tbody tr:hover td:first-child { background: var(--s-2); }

  .cx-ap-2pane { display: grid; grid-template-columns: 160px minmax(0, 1fr); gap: var(--sp-6); }
  .cx-ap-2pane--empty { grid-template-columns: 1fr; }
  .cx-ap-2pane__side { display: flex; flex-direction: column; gap: var(--sp-2); min-inline-size: 0; }
  .cx-ap-2pane__main { min-inline-size: 0; }
  @container appanel (max-width: 640px) {
    .cx-ap-2pane { grid-template-columns: 1fr; }
  }

  .cx-ap-fields { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--sp-6); }
  .cx-ap-field { min-inline-size: 0; }
  .cx-ap-field--toggle { display: flex; align-items: center; gap: var(--sp-5); }
  .cx-ap-field__actions { display: flex; gap: var(--sp-4); grid-column: 1 / -1; }
  @container appanel (max-width: 1024px) {
    .cx-ap-fields { grid-template-columns: 1fr; max-inline-size: 640px; }
  }

  .cx-ap-gallery {
    display: grid; gap: var(--sp-5);
    grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  }
  .cx-ap-gallery__item {
    display: flex; flex-direction: column; gap: var(--sp-4);
    padding: var(--sp-4);
    border: 1px solid var(--line-2); border-radius: var(--r-3);
    background: var(--s-2); box-shadow: var(--lit);
  }
  .cx-ap-gallery__link { display: block; border-radius: var(--r-2); overflow: hidden; }
  .cx-ap-gallery__img {
    inline-size: 100%; aspect-ratio: 4 / 3; object-fit: cover;
    border-radius: var(--r-2); background: var(--s-inset);
    transition: filter var(--t-2) var(--ease-settle);
  }
  .cx-ap-gallery__link:hover .cx-ap-gallery__img { filter: brightness(1.02) contrast(.98); }
  .cx-ap-gallery__ext {
    display: flex; align-items: center; justify-content: center;
    aspect-ratio: 4 / 3; border-radius: var(--r-2);
    background: var(--s-inset); box-shadow: var(--well);
    font: 500 11px/1.25 var(--font-mono); letter-spacing: .08em; text-transform: uppercase;
    color: var(--text-3);
  }
  .cx-ap-gallery__name { font: 500 13px/1.50 var(--font-sans); color: var(--text-1); overflow-wrap: anywhere; }
  .cx-ap-gallery__meta { font: 400 12px/1.40 var(--font-sans); color: var(--text-3); }
  @media (max-width: 480px) { .cx-ap-gallery { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

  .cx-ap-people { display: flex; flex-direction: column; gap: var(--sp-4); }
  .cx-ap-people__item { display: flex; align-items: center; gap: var(--sp-5); min-block-size: var(--tap); }

  /* the panel's own section bar — the second of exactly four horizontal
     scrollers, keyed to the panel's container, not the window */
  .cx-ap-tabbar {
    display: flex; align-items: center; gap: var(--sp-2);
    overflow-x: auto; overscroll-behavior-x: contain;
    scrollbar-width: none;
    border-block-end: 1px solid var(--line-2);
  }
  .cx-ap-tabbar::-webkit-scrollbar { display: none; }
  .cx-ap-tabs__panels { margin-block-start: var(--sp-6); }
  .cx-ap-tabs__panel[hidden] { display: none; }

  .cx-ap-switch { display: inline-flex; align-items: center; gap: var(--sp-5); cursor: pointer; }
  .cx-ap-switch__input { position: absolute; opacity: 0; inline-size: 1px; block-size: 1px; }
  .cx-ap-switch__track {
    position: relative; flex: none;
    inline-size: 40px; block-size: 22px;
    border: 1px solid var(--line-2); border-radius: var(--r-full);
    background: var(--s-inset); box-shadow: var(--well);
    transition: background-color var(--t-1) var(--ease-settle),
                border-color var(--t-1) var(--ease-settle);
  }
  /* Identical geometry to §3.17 — the product has exactly ONE switch. A 16px
     knob at a 2px inset leaves 1px of clearance and reads crowded. */
  .cx-ap-switch__track::after {
    content: ""; position: absolute; inset-block-start: 3px; inset-inline-start: 3px;
    inline-size: 14px; block-size: 14px; border-radius: var(--r-full);
    background: var(--s-3); border: 1px solid var(--line-3);
    box-shadow: var(--lit);
    transition: transform var(--t-1) var(--ease-settle),
                background-color var(--t-1) var(--ease-settle),
                border-color var(--t-1) var(--ease-settle);
  }
  .cx-ap-switch__input:checked + .cx-ap-switch__track { background: var(--accent-tint); border-color: rgba(var(--accent-rgb), .70); }
  /* On, the knob itself carries the accent — a 14px dot, not a surface. It
     is the cheapest way to make the state legible in BOTH themes without
     tinting anything larger. */
  .cx-ap-switch__input:checked + .cx-ap-switch__track::after {
    transform: translateX(18px);
    background: var(--accent); border-color: transparent;
  }
  .cx-ap-switch__input:focus-visible + .cx-ap-switch__track { outline: 2px solid var(--accent); outline-offset: 2px; }
  .cx-ap-switch__text { font: 500 14px/1.55 var(--font-sans); color: var(--text-1); }

  /* ── 9.10b The R5-40 data primitives: drill-ins, progress, standings,
     actions, ordered lists. Tokens only; every width rides the appanel
     container, never the window. ── */

  /* A structured value in a cell: quiet accent text that opens the drill-in
     drawer — visibly interactive without competing with row actions. */
  .cx-ap-drill {
    appearance: none; border: 0; padding: 0; background: none; cursor: pointer;
    font: 500 13px/1.50 var(--font-sans); color: var(--accent);
    text-decoration: underline; text-decoration-underline-offset: 3px;
    text-decoration-color: rgba(var(--accent-rgb), .45);
  }
  .cx-ap-drill:hover { text-decoration-color: var(--accent); }
  .cx-ap-drill:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: var(--r-1); }

  /* A progress cell: a 120px track (96px under 480px of panel, so an 8-col
     table keeps room for its text columns) with the accent fill carrying
     the value; the percent itself lives in the aria-label, not as chrome. */
  .cx-ap-progress {
    inline-size: 120px; max-inline-size: 100%; block-size: 8px;
    border: 1px solid var(--line-1); border-radius: var(--r-full);
    background: var(--s-inset); box-shadow: var(--well); overflow: hidden;
  }
  .cx-ap-progress__fill { display: block; block-size: 100%; background: var(--accent); }
  @container appanel (max-width: 480px) { .cx-ap-progress { inline-size: 96px; } }

  /* Standings: rank / name / value on one grid so the values align down
     the block. The 40px rank column (32px under 480px of panel) holds a
     two-digit rank in 12px mono with tabular figures; the value column is
     auto so a wide number never squeezes the name. The head row repeats
     the value_label and hides the obvious columns from sighted readers. */
  .cx-ap-rank { display: flex; flex-direction: column; gap: var(--sp-3); }
  .cx-ap-rank__head, .cx-ap-rank__row {
    display: grid; grid-template-columns: 40px minmax(0, 1fr) auto; gap: var(--sp-4);
    align-items: center;
  }
  .cx-ap-rank__head {
    padding-inline: var(--sp-4);
    font: 500 11px/1.25 var(--font-mono); letter-spacing: .08em; text-transform: uppercase;
    color: var(--text-3);
  }
  .cx-ap-rank__head > :last-child, .cx-ap-rank__value { justify-self: end; }
  .cx-ap-rank ol { display: flex; flex-direction: column; gap: var(--sp-2); margin: 0; padding: 0; list-style: none; }
  .cx-ap-rank__row {
    padding: var(--sp-4);
    border: 1px solid var(--line-1); border-radius: var(--r-2); background: var(--s-2);
    font: 400 13px/1.50 var(--font-sans);
  }
  .cx-ap-rank__rank { font: 500 12px/1.40 var(--font-mono); color: var(--text-3); font-variant-numeric: tabular-nums; }
  .cx-ap-rank__label { color: var(--text-1); min-inline-size: 0; overflow-wrap: anywhere; }
  .cx-ap-rank__value { font: 600 13px/1.50 var(--font-sans); color: var(--text-display); font-variant-numeric: tabular-nums; }
  @container appanel (max-width: 480px) {
    .cx-ap-rank__head { display: none; }
    .cx-ap-rank__row { grid-template-columns: 32px minmax(0, 1fr) auto; }
  }

  /* One-tap owner action: the accent button at touch height, with its
     result states in the panel's existing saved/error vocabulary. */
  .cx-ap-action { display: flex; flex-direction: column; align-items: flex-start; gap: var(--sp-4); }
  .cx-ap-action__btn { min-block-size: var(--tap); padding-inline: var(--sp-6); }
  @container appanel (max-width: 480px) { .cx-ap-action__btn { inline-size: 100%; } }

  /* The ordered-strings list editor: index / input / row tools on one
     line. Under 480px of panel the tool cluster wraps under the input
     (grid-column 2) so a 320px panel keeps a usable text field. */
  .cx-ap-list { display: flex; flex-direction: column; gap: var(--sp-3); margin: 0; padding: 0; list-style: none; }
  .cx-ap-list__row {
    display: grid; grid-template-columns: auto minmax(0, 1fr) auto;
    gap: var(--sp-3); align-items: center;
  }
  .cx-ap-list__no {
    min-inline-size: 20px; text-align: end;
    font: 500 12px/1.40 var(--font-mono); color: var(--text-3); font-variant-numeric: tabular-nums;
  }
  .cx-ap-list__tools { display: flex; gap: var(--sp-2); }
  .cx-ap-list__actions { display: flex; }
  @container appanel (max-width: 480px) {
    .cx-ap-list__row { grid-template-columns: auto minmax(0, 1fr); }
    .cx-ap-list__tools { grid-column: 2; }
  }

  /* ── 9.11 Settings (§4.13) and the action index. ── */
  .cx-settings { inline-size: 100%; max-inline-size: 800px; margin-inline: auto; }
  .cx-settings__panel { display: block; }
  .cx-settings__panel + .cx-settings__panel { margin-block-start: var(--pad-section); }
  /* M-30 — groups arrive in order, 40ms apart, and never again. */
  .cx-settings__panel { --enter-y: 6px; animation: cx-enter var(--t-2) var(--ease-settle) backwards; }
  .cx-settings__panel:nth-child(1) { animation-delay: 0ms; }
  .cx-settings__panel:nth-child(2) { animation-delay: 40ms; }
  .cx-settings__panel:nth-child(3) { animation-delay: 80ms; }
  .cx-settings__panel:nth-child(4) { animation-delay: 120ms; }
  .cx-settings__panel:nth-child(5) { animation-delay: 160ms; }

  /* ── 9.12 Your users (#/users) and the schema editor. ── */
  .cx-users { display: block; }
  .cu-section { display: block; }
  .cu-section + .cu-section { margin-block-start: var(--pad-section); }
  .cu-chips { display: flex; flex-wrap: wrap; gap: var(--sp-4); }
  .cu-chip {
    display: inline-flex; align-items: center; gap: var(--sp-3);
    min-block-size: 24px; padding-inline: var(--sp-4);
    border: 1px solid var(--line-2); border-radius: var(--r-full);
    background: var(--s-2);
    font: 400 12px/1.40 var(--font-sans); color: var(--text-2);
  }
  .cu-chip-dot { inline-size: 6px; block-size: 6px; border-radius: var(--r-full); background: var(--line-3); }
  .cu-chip-dot[data-on="1"] { background: var(--ok); }
  .cu-val { font: 400 13px/1.50 var(--font-sans); color: var(--text-1); overflow-wrap: anywhere; }
  .cu-collection { max-inline-size: 320px; }
  .cu-data-box { display: flex; flex-direction: column; gap: var(--sp-5); }
  .cu-pager { display: flex; align-items: center; gap: var(--sp-5); margin-block-start: var(--sp-5); }
  .cu-picker { display: flex; flex-wrap: wrap; gap: var(--sp-4); align-items: flex-end; }

  .schema-editor { display: block; }
  .schema-rows { display: flex; flex-direction: column; gap: var(--sp-4); }
  .schema-row {
    display: grid; align-items: end; gap: var(--sp-4);
    grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr) auto auto auto;
    padding: var(--sp-4);
    border: 1px solid var(--line-2); border-radius: var(--r-3);
    background: var(--s-2); box-shadow: var(--lit);
  }
  .schema-key, .schema-type { min-inline-size: 0; }
  .schema-required, .schema-max { display: flex; align-items: center; gap: var(--sp-3); }
  .schema-max { max-inline-size: 96px; }
  @media (max-width: 768px) {
    .schema-row { grid-template-columns: 1fr; }
  }
  .subusers-box { display: block; margin-block-start: var(--sp-6); }
  .subuser-table { inline-size: 100%; border-collapse: collapse; }
  .subuser-table th, .subuser-table td { padding: var(--sp-4) var(--sp-5); text-align: start; white-space: nowrap; }
  .subuser-table thead th {
    font: 500 11px/1.25 var(--font-mono); letter-spacing: .08em; text-transform: uppercase;
    color: var(--text-3); border-block-end: 1px solid var(--line-2);
  }
  .subuser-table tbody td { font: 400 13px/1.50 var(--font-sans); color: var(--text-1); border-block-start: 1px solid var(--line-1); }
  .subuser-table tbody tr:hover { background: var(--s-2); }
  .subuser-table th:first-child, .subuser-table td:first-child {
    position: sticky; inset-inline-start: 0; z-index: 1;
    background: var(--s-1); box-shadow: 1px 0 0 var(--line-1);
  }

  /* ── 9.13 Creations studio (#/creations). ── */
  .cr-grid {
    display: grid; gap: var(--sp-5);
    grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  }
  .cr-grid--single { grid-template-columns: 1fr; }
  .cr-grid-empty { grid-column: 1 / -1; }
  .cr-card {
    display: flex; flex-direction: column; gap: var(--sp-4);
    padding: var(--sp-4);
    border: 1px solid var(--line-2); border-radius: var(--r-3);
    background: var(--s-2); box-shadow: var(--lit);
    transition: background-color var(--t-0) var(--ease-settle),
                border-color var(--t-0) var(--ease-settle);
  }
  .cr-card:hover { background: var(--s-3); border-color: var(--line-3); }
  .cr-title-row { display: flex; align-items: center; gap: var(--sp-4); }
  .cr-title { flex: 1 1 auto; min-inline-size: 0; font: 500 13px/1.50 var(--font-sans); color: var(--text-1); overflow-wrap: anywhere; }
  .cr-meta { font: 400 12px/1.40 var(--font-sans); color: var(--text-3); }
  .cr-body { display: flex; flex-direction: column; gap: var(--sp-5); }
  .cr-preview {
    display: flex; align-items: center; justify-content: center;
    padding: var(--sp-5);
    border-radius: var(--r-3);
    background: var(--s-inset); box-shadow: var(--well);
    max-inline-size: 1200px; margin-inline: auto; inline-size: 100%;
  }
  .cr-preview img, .cr-preview canvas, .cr-preview svg, .cr-canvas {
    max-inline-size: 100%; block-size: auto; border-radius: var(--r-2);
  }
  .cr-preview-note, .cr-note, .cr-project-note { font: 400 12px/1.40 var(--font-sans); color: var(--text-3); }
  .cr-actions { display: flex; flex-wrap: wrap; gap: var(--sp-4); }
  .cr-project-row { display: flex; align-items: center; gap: var(--sp-5); flex-wrap: wrap; }

  /* R5-66 — the sandboxed frame IS the creation's real document (compiled
     SVG / the post's page): full bleed in the well, the design's own aspect
     via the inline ratio, pane-sized. The 240px floor keeps a text post
     page-like; the cx-lp__frame 60cqw floor stays the DOCUMENT pane's own —
     a creation sizes to its design, not to a reading budget.
     D1 (owner viewport defect) — this higher-specificity rule makes that
     contract cascade-proof: a creation frame is bounded by the viewport
     (max-block-size), never stretched by the document pane's 60cqw floor,
     and the JS in creations-panel.js caps its width so the whole design
     fits scaled down — never upscaled past 1:1, never overflowing. */
  .cr-preview--frame { padding: 0; }
  .cr-preview--frame .cr-frame {
    display: block; block-size: auto; min-block-size: 240px;
    max-block-size: min(72dvh, 760px);
    margin-inline: auto;
  }
  .cr-frame { display: block; block-size: auto; min-block-size: 240px; }

  /* R5-90/94 + F-4 — the canvas path (motion/video): the stage keeps the
     scene's true aspect against the pane, and when that aspect would
     push the height past the viewport-bound cap the stage's WIDTH
     self-adjusts (inline min() set from JS) so the design always FITS —
     centred, never overflowing, never letterboxed. The canvas fills it
     at devicePixelRatio, and the transport (play/pause + scrub + time)
     rides beneath on the system's 44px floor. */
  .cr-preview--canvas {
    flex-direction: column; align-items: stretch; justify-content: flex-start;
    gap: var(--sp-4);
  }
  .cr-stage {
    position: relative; inline-size: 100%;
    margin-inline: auto;
    max-block-size: min(66dvh, 720px);
  }
  .cr-stage .cr-canvas { display: block; inline-size: 100%; block-size: 100%; }
  /* F-4 — the resolution control (motion/video item view): one quiet row
     on the transport's own language. */
  .cr-resolution {
    display: flex; align-items: center; gap: var(--sp-4); flex-wrap: wrap;
    padding-block-start: var(--sp-3);
  }
  .cr-resolution__label {
    font: 500 11px/1.25 var(--font-mono); letter-spacing: .08em; text-transform: uppercase;
    color: var(--text-3);
  }
  .cr-resolution__select { inline-size: auto; min-inline-size: 9.5em; }
  .cr-resolution__hint { font: 400 12px/1.40 var(--font-sans); color: var(--text-3); }
  /* F-5a — the failed-save line: the reason in --text-2 on the error
     tone, and the retry on the quiet button. Appears only after a
     failure; never a second permanent actions row. */
  .cr-retry {
    display: flex; align-items: center; gap: var(--sp-4); flex-wrap: wrap;
    padding: var(--sp-3) var(--sp-4);
    border: 1px solid color-mix(in srgb, var(--danger) 45%, transparent); border-radius: var(--r-2);
    background: var(--danger-tint);
  }
  .cr-retry__why {
    flex: 1 1 auto; min-inline-size: 0;
    font: 400 12px/1.45 var(--font-sans); color: var(--text-2);
    overflow-wrap: anywhere;
  }
  .cr-transport {
    display: flex; align-items: center; gap: var(--sp-4);
    padding-block-start: var(--sp-3);
  }
  .cr-time {
    font: 500 11px/1.25 var(--font-mono); letter-spacing: .08em; text-transform: uppercase;
    color: var(--text-3); font-variant-numeric: tabular-nums; white-space: nowrap;
  }
  /* The scrub slider: the progress bar's own language (4px inset track, flat
     --accent thumb) on the mandatory 44px target. */
  .cx-scrub {
    -webkit-appearance: none; appearance: none;
    flex: 1 1 auto; min-inline-size: 0; block-size: var(--tap);
    margin: 0; background: transparent; cursor: pointer;
  }
  .cx-scrub::-webkit-slider-runnable-track {
    block-size: 4px; border-radius: var(--r-full);
    background: var(--s-inset); box-shadow: var(--well);
  }
  .cx-scrub::-webkit-slider-thumb {
    -webkit-appearance: none; appearance: none;
    inline-size: 20px; block-size: 20px; margin-block-start: -8px;
    border: none; border-radius: var(--r-full);
    background: var(--accent); box-shadow: var(--lit);
  }
  .cx-scrub::-moz-range-track {
    block-size: 4px; border-radius: var(--r-full);
    background: var(--s-inset); box-shadow: var(--well);
  }
  .cx-scrub::-moz-range-thumb {
    inline-size: 20px; block-size: 20px; border: none; border-radius: var(--r-full);
    background: var(--accent); box-shadow: var(--lit);
  }
  /* rendering: the aurora on a 2px rule beneath the well, plus a mono
     percentage. Never a spinner. */
  .cr-preview[data-working="1"] { position: relative; }
  .cr-preview[data-working="1"]::after {
    content: ""; position: absolute; inset-block-end: 0; inset-inline-start: 0;
    inline-size: 240px; block-size: 2px; background: var(--aurora-rule);
    -webkit-mask-image: linear-gradient(90deg, transparent, #000 22%, #000 78%, transparent);
    mask-image: linear-gradient(90deg, transparent, #000 22%, #000 78%, transparent);
    animation: cx-travel 2000ms var(--ease-travel) infinite;
    will-change: transform;
  }

  /* ── 9.14 Documents composer (#/documents). ── */
  .doc-list { display: flex; flex-direction: column; gap: var(--sp-4); }
  .block-list { display: flex; flex-direction: column; gap: var(--sp-5); }
  .block-card {
    display: flex; flex-direction: column; gap: var(--sp-5);
    padding: var(--sp-5);
    border: 1px solid var(--line-2); border-radius: var(--r-3);
    background: var(--s-2); box-shadow: var(--lit);
  }
  .block-head { display: flex; align-items: center; gap: var(--sp-4); }
  .block-type {
    font: 500 11px/1.25 var(--font-mono); letter-spacing: .08em; text-transform: uppercase;
    color: var(--text-3);
  }
  /* the block number is information (it is how the move-up/move-down menu is
     read), so it is --text-3; --text-4 is disabled-only. */
  .block-index { font: 400 12px/1.45 var(--font-mono); color: var(--text-3); font-variant-numeric: tabular-nums; }
  .block-tools { display: flex; align-items: center; gap: var(--sp-3); margin-inline-start: auto; }
  .block-input { inline-size: 100%; }
  textarea.block-input { min-block-size: 96px; resize: none; }
  .cx-blockcount { display: block; }
  .format-grid {
    display: grid; gap: var(--sp-4);
    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  }
  .format-card {
    display: flex; align-items: center; gap: var(--sp-4);
    min-block-size: var(--tap); padding: var(--sp-4) var(--sp-5);
    border: 1px solid var(--line-2); border-radius: var(--r-2);
    background: var(--s-2);
    transition: background-color var(--t-0) var(--ease-settle),
                border-color var(--t-0) var(--ease-settle);
  }
  .format-card:hover { background: var(--s-3); border-color: var(--line-3); }
  .format-input { accent-color: var(--accent); }
  .format-name { font: 500 13px/1.50 var(--font-sans); color: var(--text-1); }
  .format-hint { font: 400 12px/1.40 var(--font-sans); color: var(--text-3); }
  .cx-docdone { display: flex; flex-direction: column; gap: var(--sp-5); align-items: flex-start; }

  /* ── 9.15 Projects, files and the file editor (#/websites, #/apps). ── */
  .cx-projlist, .cx-filelist { display: flex; flex-direction: column; gap: var(--sp-4); }
  .cx-projrow, .cx-filerow {
    position: relative;
    display: flex; align-items: center; gap: var(--sp-5); flex-wrap: wrap;
    min-block-size: 64px; padding: var(--sp-4) var(--sp-5);
    border: 1px solid var(--line-2); border-radius: var(--r-3);
    background: var(--s-2); box-shadow: var(--lit);
    transition: background-color var(--t-0) var(--ease-settle),
                border-color var(--t-0) var(--ease-settle);
  }
  .cx-projrow:hover, .cx-filerow:hover { background: var(--s-3); border-color: var(--line-3); }
  .cx-projrow:hover::before { transform: scaleX(1); }
  .cx-projrow::before { inset-block: 10px; }
  .cx-projrow__main, .cx-filerow__main {
    flex: 1 1 220px; min-inline-size: 0;
    display: flex; flex-direction: column; gap: var(--sp-2);
  }
  .cx-projrow__title { font: 500 14px/1.55 var(--font-sans); color: var(--text-1); }
  .cx-projrow__meta, .cx-filerow__meta { font: 400 12px/1.40 var(--font-sans); color: var(--text-3); }
  .cx-filerow__name { font: 500 13px/1.50 var(--font-sans); color: var(--text-1); overflow-wrap: anywhere; }
  .cx-projrow__link { font: 400 12px/1.45 var(--font-mono); color: var(--accent); overflow-wrap: anywhere; }
  .cx-liveurl {
    display: flex; align-items: center; flex-wrap: wrap; gap: var(--sp-4);
    padding: var(--sp-4) var(--sp-5);
    border: 1px solid var(--line-2); border-radius: var(--r-2);
    background: var(--s-2);
  }
  .cx-liveurl__label {
    font: 500 11px/1.25 var(--font-mono); letter-spacing: .08em; text-transform: uppercase;
    color: var(--text-3);
  }
  .cx-liveurl a { font: 400 12px/1.45 var(--font-mono); overflow-wrap: anywhere; }
  .cx-fieldgrid {
    display: grid; gap: var(--sp-5);
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  }
  .cx-field--color input[type="color"] {
    inline-size: 100%; block-size: var(--h-md); padding: 2px;
    border: 1px solid var(--line-2); border-radius: var(--r-2);
    background: var(--s-2);
  }
  .cx-backbtn { align-self: flex-start; margin-block-end: var(--sp-5); }

  .editor-layout { display: grid; grid-template-columns: 220px minmax(0, 1fr); gap: var(--sp-6); }
  .editor-side { min-inline-size: 0; display: flex; flex-direction: column; gap: var(--sp-4); }
  .editor-pane { min-inline-size: 0; display: flex; flex-direction: column; gap: var(--sp-4); }
  .editor-path { font: 400 12px/1.45 var(--font-mono); color: var(--text-3); overflow-wrap: anywhere; }
  .cx-editor__note { font: 400 12px/1.40 var(--font-sans); }
  .editor-area {
    inline-size: 100%; min-block-size: 40dvh; resize: vertical;
    padding: var(--sp-5);
    border: 1px solid var(--line-2); border-radius: var(--r-2);
    background: var(--s-inset); box-shadow: var(--well);
    color: var(--text-1);
    font: 400 13px/1.60 var(--font-mono);
    tab-size: 2;
  }
  .editor-area:focus { border-color: var(--accent); outline: 2px solid var(--accent); outline-offset: 2px; }
  .cx-filetree { display: flex; flex-direction: column; gap: 2px; }
  .cx-fileitem {
    display: block; inline-size: 100%;
    min-block-size: var(--tap); padding: var(--sp-3) var(--sp-4);
    border-radius: var(--r-2);
    text-align: start;
    font: 400 12px/1.45 var(--font-mono); color: var(--text-2);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    transition: background-color var(--t-0) var(--ease-settle), color var(--t-0) var(--ease-settle);
  }
  .cx-fileitem:hover { background: var(--s-2); color: var(--text-1); }
  .cx-fileitem.active { background: var(--s-2); color: var(--accent-strong); }
  @media (max-width: 768px) {
    .editor-layout { grid-template-columns: 1fr; }
  }

  /* ── 9.16 Determinate progress. This is NOT an indeterminate bar: it only
     ever renders against a real percentage. ── */
  .progress {
    display: flex; flex-direction: column; gap: var(--sp-3);
    margin-block-start: var(--sp-5);
  }
  .progress-bar {
    inline-size: 100%; block-size: 4px; border-radius: var(--r-full);
    background: var(--s-inset); box-shadow: var(--well); overflow: hidden;
  }
  /* Flat --accent, not the aurora: a progress fill is a surface, and the
     aurora is edges only (AD §2.5 / §10.2). */
  .progress-fill {
    block-size: 100%; inline-size: 0;
    border-radius: var(--r-full); background: var(--accent);
  }
  .progress-label {
    font: 500 11px/1.25 var(--font-mono); letter-spacing: .08em; text-transform: uppercase;
    color: var(--text-3); font-variant-numeric: tabular-nums;
  }

  /* ── 9.17 The legacy switch markup, brought into the system (M-32). ── */
  .switch-grid { display: flex; flex-direction: column; gap: var(--sp-6); }
  .switch-row { display: flex; flex-direction: column; gap: var(--sp-3); }
  .switch { display: inline-flex; align-items: center; gap: var(--sp-5); cursor: pointer; min-block-size: var(--tap); }
  .switch-input { position: absolute; opacity: 0; inline-size: 1px; block-size: 1px; }
  .switch-track {
    position: relative; flex: none;
    inline-size: 40px; block-size: 22px;
    border: 1px solid var(--line-2); border-radius: var(--r-full);
    background: var(--s-inset); box-shadow: var(--well);
    transition: background-color var(--t-1) var(--ease-settle),
                border-color var(--t-1) var(--ease-settle);
  }
  /* Identical geometry to §3.17 — the product has exactly ONE switch. A 16px
     knob at a 2px inset leaves 1px of clearance and reads crowded. */
  .switch-track::after {
    content: ""; position: absolute; inset-block-start: 3px; inset-inline-start: 3px;
    inline-size: 14px; block-size: 14px; border-radius: var(--r-full);
    background: var(--s-3); border: 1px solid var(--line-3);
    box-shadow: var(--lit);
    transition: transform var(--t-1) var(--ease-settle),
                background-color var(--t-1) var(--ease-settle),
                border-color var(--t-1) var(--ease-settle);
  }
  .switch-input:checked + .switch-track { background: var(--accent-tint); border-color: rgba(var(--accent-rgb), .70); }
  /* On, the knob itself carries the accent — a 14px dot, not a surface. It
     is the cheapest way to make the state legible in BOTH themes without
     tinting anything larger. */
  .switch-input:checked + .switch-track::after {
    transform: translateX(18px);
    background: var(--accent); border-color: transparent;
  }
  .switch-input:focus-visible + .switch-track { outline: 2px solid var(--accent); outline-offset: 2px; }
  .switch-text { font: 500 14px/1.55 var(--font-sans); color: var(--text-1); }

  /* ── 9.18 The agent's own side column (the conversation list). ── */
  .agent-side { display: flex; flex-direction: column; gap: var(--sp-5); min-inline-size: 0; }
  .agent-side-section { display: flex; flex-direction: column; gap: var(--sp-4); }
  .agent-project-select { inline-size: 100%; }
  .agent-project-row { display: flex; align-items: center; gap: var(--sp-4); }
  .agent-conv-list { display: flex; flex-direction: column; gap: var(--sp-2); }
  .agent-conv {
    position: relative; display: flex; align-items: center; gap: var(--sp-4);
    min-block-size: var(--tap); padding: var(--sp-3) var(--sp-4);
    border-radius: var(--r-2);
    transition: background-color var(--t-0) var(--ease-settle);
  }
  .agent-conv:hover { background: var(--s-2); }
  .agent-conv-open { flex: 1 1 auto; min-inline-size: 0; text-align: start; }
  .agent-conv-title {
    display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    font: 500 13px/1.50 var(--font-sans); color: var(--text-1);
  }
  .agent-conv-meta { display: block; font: 400 12px/1.40 var(--font-sans); color: var(--text-3); }
  .agent-conv-act { flex: none; }
  .agent-conv-empty { font: 400 13px/1.50 var(--font-sans); color: var(--text-3); }
  .conv-running .agent-conv-title { color: var(--accent-strong); }
  .conv-capped .agent-conv-title { color: var(--warn-strong); }

  /* ── 9.19 The rail's vertical tab lists and the selector's chats group. ── */
  .cx-tabs { display: flex; align-items: center; gap: var(--sp-2); }
  .cx-tabs--side { flex-direction: column; align-items: stretch; }
  .cx-selector__menu { display: flex; flex-direction: column; }
  .cx-selector__chats { display: flex; flex-direction: column; min-block-size: 0; }
  .cx-selector__chatlist {
    display: flex; flex-direction: column;
    max-block-size: 50dvh; min-block-size: 0;
    overflow-y: auto; overscroll-behavior: contain;
  }
  .cx-menu-item--project { justify-content: flex-start; }
  /* ── 9.19b The corner "to chat" affordance (R2-2c). A drag needs somewhere
     to drop, and at ≤768px the full-screen drawer has hidden the chat — so the
     row carries a one-tap twin instead. It does not exist on a pointer device:
     desktop keeps the drag and the row's menu. ── */
  .cx-refable { position: relative; }
  .cx-toref {
    position: absolute; inset-block-start: 0; inset-inline-end: 0;
    display: none; align-items: center; justify-content: center;
    inline-size: var(--tap); block-size: var(--tap);
    padding: 0; border: 0; background: transparent;
    color: var(--text-3);
    transition: color var(--t-0) var(--ease-settle);
  }
  .cx-toref svg { inline-size: var(--icon-sm); block-size: var(--icon-sm); }
  .cx-toref:hover, .cx-toref:focus-visible { color: var(--accent); }
  @media (max-width: 768px) {
    .cx-toref { display: inline-flex; }
  }

  /* R4-U5: `.cx-drawer--right` deleted — §3.11 already anchors every drawer to
     the inline-end edge, and a second rule restating it was the only reason
     the direction could ever disagree with itself. The class name is gone
     from the markup too — there is nothing left to mark, because there is
     nothing left to disagree with. */
  @media (min-width: 1921px) { .cx-drawer { inline-size: var(--drawer-w-wide); } }
  /* R2-2c — at ≤768px the drawer IS the screen. The chat is behind it, so
     every draggable row inside carries the corner "to chat" button below. */
  @media (max-width: 768px) { .cx-drawer { inline-size: 100%; } }

  /* ── 9.20 Panel-level responsive floor. Below 480px every panel gives its
     gutters back to the content and every action becomes full width. ── */
  @media (max-width: 480px) {
    .cx-files__bar { flex-direction: column; align-items: stretch; }
    .cx-files__bar > .cx-btn { inline-size: 100%; }
    .cx-viewhead h2, .cx-viewhead .cx-heading { font-size: 20px; }
  }
}

@layer panels {
  /* ── 9.21 The last few hooks the panel DOM still names. ── */
  .cx-modal__text { font: 400 14px/1.60 var(--font-sans); color: var(--text-2); }
  .cx-modal__text + .cx-losslist { margin-block-start: var(--sp-5); }
  .cx-editor { display: block; }
  .cx-ap-tabs { display: block; }
  .cx-workhead__title { position: absolute; }
  .agent-new-conv { justify-content: center; }
  /* One shared tab primitive. The rail's vertical list and the panel's
     horizontal bar are the same idea seen twice; only the axis differs. */
  .cx-tab {
    position: relative;
    display: inline-flex; align-items: center; justify-content: center; gap: var(--sp-4);
    min-block-size: var(--tap); padding-inline: var(--sp-5);
    border-radius: var(--r-2);
    font: 500 13px/1.50 var(--font-sans); color: var(--text-3);
    white-space: nowrap;
    transition: background-color var(--t-0) var(--ease-settle), color var(--t-0) var(--ease-settle);
  }
  .cx-tab:hover { background: var(--s-2); color: var(--text-1); }
  .cx-tab[aria-selected="true"] { color: var(--text-display); }
  .cx-tabs--underline { position: relative; }
  /* M-11 — the underline TRAVELS here (unlike the rail indicator, which only
     ever fades). The two rules are deliberately different and neither is a
     bug: a horizontal bar reads as travel, a vertical one reads as flight. */
  .cx-tab__indicator {
    position: absolute; inset-block-end: 0; inset-inline-start: 0;
    block-size: 2px; inline-size: 1px;
    background: var(--aurora-rule);
    transform-origin: left center;
    transition: transform var(--t-2) var(--ease-settle);
  }
}

@layer panels {
  /* ── 9.22 The destructive dialog's one tint. A 1px --danger edge at the
     top and nothing else: a fully red dialog reads as alarm, and alarm is
     not the same thing as consequence. ── */
  .cx-modal__panel--danger { position: relative; overflow: hidden; }
  .cx-modal__panel--danger::before {
    content: ""; position: absolute; inset-block-start: 0; inset-inline: 0;
    block-size: 1px; background: color-mix(in srgb, var(--danger) 25%, transparent);
  }
  /* Below 768px a dialog is a full-bleed sheet rising from the bottom edge.
     Same component, different clamps. */
  @media (max-width: 768px) {
    .cx-modal { align-items: flex-end; padding: 0; }
    .cx-modal__panel {
      inline-size: 100%;
      max-block-size: calc(100dvh - var(--safe-t) - var(--kb-inset));
      border-radius: var(--r-4) var(--r-4) 0 0;
      border-inline: 0; border-block-end: 0;
      /* the same shared entrance at sheet amplitude — one motion, one
         keyframe, two very different distances */
      --enter-y: 100%;
    }
    .cx-modal__footer { padding-block-end: calc(var(--pad-panel) + var(--safe-b)); }
  }
}

@layer panels {
  /* ── 9.23 Alignment corrections found in the specimen. ── */

  /* A settings row is a LABEL AND A VALUE, not a target. It carries no hover
     plate and no aurora bar, so its text lines up with the group label above
     it instead of sitting 12px inboard of it. */
  .cx-rows > .cx-row { padding-inline: 0; border-radius: 0; }
  .cx-rows > .cx-row:hover { background: none; }
  .cx-rows > .cx-row::before { display: none; }

  /* A group that follows content — rather than another group — still needs
     its section of air above the rule. */
  .cx-group:not(:first-child) { margin-block-start: var(--pad-section); }

  /* One height for everything in a block header, so the filter and its
     Export control read as a pair rather than a mistake. */
  .cx-ap-blk__tools .cx-btn,
  .cx-ap-blk__tools .cx-input { block-size: var(--h-md); min-block-size: var(--h-md); }
  @media (pointer: coarse) {
    .cx-ap-blk__tools .cx-btn,
    .cx-ap-blk__tools .cx-input { min-block-size: var(--tap); }
  }
}

@layer panels {
  /* ── 9.24 The narrow floors, found by measuring the panels at 320. ── */

  /* The list panel has no head to reflow any more (U3): its controls are in
      the workspace's one top bar, which already has its own narrow floors, and
      the region below it is a single scroller at every width.

      What the top bar still needs at 480 is the narrow form of the ONE item
      verb it now carries — the same form the publish control uses: the label
      clips, the box squares up to the tap floor, and the verb rides aria-label
      and title. It lives in THIS layer because `panels` wins over `shell`, and
      the glyph must beat the .cx-lp__act rule that hides it above 768.

      R5-61/75 re-derivation (Fable D9) — the threshold is 768 (the product's
      existing panel breakpoint, not a new ad-hoc value), NOT 480. The item
      cluster it used to be forced by — three READING verbs at 13px/sp-5
      ("Download" 78 + "Flag this document" 146 + "Reference in chat" 140,
      424px of slot, 748px of bar) — is deleted. What remains is back + the
      select icon + ONE save verb, whose labelled worst case ("Save to your
      files", 18 characters, the same measure as the old 146px "Flag this
      document") is 234px of slot and a 558px bar at 769 — of the 713
      available there, so the words now FIT above the breakpoint. 768 is kept
      anyway, for the reason U3 states: one arrangement for the whole compact
      band, so the bar never changes its mind mid-band. Everything below 768
      takes the icon-only form: 132px of slot (36 back + 36 select + 44 verb
      + 2 × 8 gaps) instead. */
  @media (max-width: 768px) {
    .cx-lp__act {
      inline-size: var(--tap); block-size: var(--tap); padding-inline: 0;
      justify-content: center;
    }
    .cx-lp__act .cx-btn__label {
      position: absolute; inline-size: 1px; block-size: 1px; margin: -1px;
      overflow: hidden; clip-path: inset(50%); white-space: nowrap;
    }
    .cx-lp__act svg { display: block; }
  }

  /* R4-U8 (1) — the 320 floor for a LIST section, re-derived for the
      R5-61/75 cluster (Fable D9). A CREATIONS item is the list sections'
      widest case now: back + select + the one save verb (a documents item
      shows back alone). Box math at 320, worst case:

        leading inset   --toggle 36 + 24            =  60
        trailing pad    --sp-1                      =   2
        bar gaps        4 × --sp-1                  =   8
        swap button     --h-xs                      =  28
        section strip   floor (scrolls inside)      =  40
        spacer          0 at ≤380                   =   0
        slot            back 28 + select 28 + save 36
                        + 2 × --sp-1                =  96
        reload button   --h-xs                      =  28
                                                      ----
                                                      262  ≤ 320  ✓

      58px of slack — the ~114px the three deleted verbs used to claim is
      what turned the old 308/320 into this. The hit boxes keep the full 44px
      on the BLOCK axis; on the inline axis they are clamped to the control
      pitch (painted box + gap), because a 44px overlay around a 28–36px box
      at a 30–38px pitch overlapped its neighbour and the later-in-DOM
      control silently won the straddle. A surfaces section is the widest
      bar at this floor now: publish 90 + diagnostics 28 + selector 28 +
      2 gaps = 150 in the slot, 316 in total — the one cluster that still
      brushes the 320 line. */
  @media (max-width: 480px) {
    /* R4-U8 (1) fix — 36px painted, 8px of gap: a pitch of exactly 44, so the
       hit boxes below TOUCH and never overlap. */
    .cx-lp__act { inline-size: var(--h-md); }
    /* the painted box shrinks; the TARGET does not */
    .cx-lp__act::after {
      content: ""; position: absolute; inset: 50% auto auto 50%;
      inline-size: var(--tap); block-size: var(--tap);
      translate: -50% -50%;
    }
  }
  @media (max-width: 380px) {
    /* R4-U8 (3) fix — the gap drops to 2px here, so the pitch is 38 and a
       44px overlay would straddle its neighbour. Clamp the inline axis to the
       pitch; the block axis keeps the full tap height. */
    .cx-lp__act::after { inline-size: calc(100% + var(--sp-1)); }
  }

  /* A confirm at 320 stacks its verbs, and the one that keeps your work sits
     under the one that destroys it — the reach order stays the same, so the
     safe verb is never where the destructive one used to be. */
  @media (max-width: 480px) {
    .cx-modal__footer { flex-direction: column-reverse; align-items: stretch; }
    .cx-modal__footer .cx-btn { inline-size: 100%; }
  }
}

@layer panels {
  /* A field that follows prose in a dialog needs its own air. */
  .cx-field--spaced { margin-block-start: var(--sp-6); }
}

/* ══════════════ 10. STANDALONE PAGES (offline, install) ══════════════
   Two routes live outside the dashboard shell: `/offline.html`, served by the
   service worker when there is no network, and `/install/`, the PWA install
   page. They have no JS shell to build their DOM, so they compose the same
   system from static markup: the shell ground, the ambient wash, the real
   brand lockup, the button formula, and the label-under-rule grouping.
   They link cosmix.css and nothing else — `style-src 'self'` means no inline
   <style> can ever rescue a page that links a stylesheet that is not there. */

@layer panels {
  .cx-standalone {
    position: relative;
    min-block-size: 100dvh;
    display: flex; align-items: center; justify-content: center;
    padding: var(--pad-section);
    background: var(--s-0);
  }
  .cx-standalone__col {
    position: relative; z-index: var(--z-content);
    inline-size: 100%; max-inline-size: 420px;
    display: flex; flex-direction: column; align-items: flex-start;
    gap: var(--sp-6);
    /* M-01 — the same entrance the auth screen uses. */
    --enter-y: 8px;
    animation: cx-enter var(--t-3) var(--ease-glide) backwards;
  }
  .cx-standalone__lockup { block-size: 32px; inline-size: auto; }
  .cx-standalone__lockup img { block-size: 32px; inline-size: auto; }
  .cx-standalone__title {
    font: 600 24px/1.20 var(--font-sans); letter-spacing: -.022em;
    color: var(--text-display);
  }
  .cx-standalone__line {
    font: 400 15px/1.65 var(--font-sans); letter-spacing: -.003em;
    color: var(--text-2); max-inline-size: 46ch;
  }
  .cx-standalone__actions { display: flex; flex-direction: column; gap: var(--sp-4); inline-size: 100%; }
  .cx-standalone__foot { font: 400 13px/1.50 var(--font-sans); color: var(--text-3); }
  .cx-standalone__section[hidden] { display: none; }
  .cx-standalone__section {
    display: flex; flex-direction: column; align-items: flex-start;
    gap: var(--sp-6); inline-size: 100%;
  }

  /* The install steps. A numbered ledger, not a card stack: a mono numeral,
     the instruction, and a hairline between rows. */
  .cx-steps { display: flex; flex-direction: column; inline-size: 100%; }
  .cx-steps > li {
    display: grid; grid-template-columns: 24px 1fr; gap: var(--sp-5);
    align-items: start;
    padding-block: var(--sp-5);
    border-block-start: 1px solid var(--line-1);
  }
  .cx-steps > li:last-child { border-block-end: 1px solid var(--line-1); }
  .cx-steps__num {
    font: 500 11px/1.25 var(--font-mono); letter-spacing: .08em;
    color: var(--text-3); font-variant-numeric: tabular-nums;
    padding-block-start: 3px;
  }
  .cx-steps__body { font: 400 14px/1.55 var(--font-sans); color: var(--text-2); }
  .cx-steps__body strong {
    display: block; font-weight: 500; color: var(--text-1);
    margin-block-end: var(--sp-2);
  }
  .cx-bullets { display: flex; flex-direction: column; gap: var(--sp-4); inline-size: 100%; }
  .cx-bullets > li {
    position: relative; padding-inline-start: var(--sp-5);
    font: 400 14px/1.55 var(--font-sans); color: var(--text-2);
  }
  .cx-bullets > li::before {
    content: ""; position: absolute; inset-block-start: .62em; inset-inline-start: 0;
    inline-size: 4px; block-size: 4px; border-radius: var(--r-full);
    background: var(--line-3);
  }
  .cx-bullets strong { font-weight: 500; color: var(--text-1); }

  @media (max-width: 480px) {
    .cx-standalone { align-items: flex-start; padding-block-start: 12dvh; }
  }
}
