/* Creator console — shared design system.
 * One source of truth for tokens, base elements, and common components across
 * every console page (review · analytics · timeline · live · impact). Page-
 * specific component styles stay in each page's inline <style>; anything shared
 * lives here so the surfaces stay consistent and don't drift.
 *
 * Palette: oklch tokens (docs/DECISIONS.md "Creator Console redesign").
 * Fonts are self-hosted (assets/fonts/*.woff2), not a Google Fonts <link> —
 * matches this repo's "all assets bundled, no third-party runtime fetches"
 * convention (CLAUDE.md) and avoids a render-blocking cross-origin request. */

@font-face {
  font-family: "Public Sans";
  font-style: normal;
  font-weight: 400 800; /* variable font: one file covers the whole range */
  font-display: swap;
  src: url("assets/fonts/public-sans-variable.woff2") format("woff2");
}
@font-face {
  font-family: "IBM Plex Mono";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("assets/fonts/ibm-plex-mono-400.woff2") format("woff2");
}
@font-face {
  font-family: "IBM Plex Mono";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("assets/fonts/ibm-plex-mono-500.woff2") format("woff2");
}
@font-face {
  font-family: "IBM Plex Mono";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("assets/fonts/ibm-plex-mono-600.woff2") format("woff2");
}

:root {
  color-scheme: dark;

  /* Surfaces — deep ink with a green undertone, layered as translucent glass
     so the ambient light behind the page reads through without competing. */
  --bg: oklch(0.145 0.014 168);
  --bg-solid: oklch(0.17 0.016 168);
  --surface: oklch(0.2 0.017 166 / 0.72);
  --surface-2: oklch(0.225 0.018 166 / 0.78);
  --surface-3: oklch(0.27 0.02 166 / 0.82);
  --field: oklch(0.12 0.012 168 / 0.72);
  /* Solid: components paint rings and holes against it (readiness donut). */
  --card: oklch(0.19 0.016 166);
  --border: oklch(1 0 0 / 0.11);
  --border-soft: oklch(1 0 0 / 0.07);
  --glass: linear-gradient(180deg, oklch(1 0 0 / 0.06), oklch(1 0 0 / 0.018));
  --glass-hi: inset 0 1px 0 oklch(1 0 0 / 0.07);

  /* Text — warm cream, not blue-grey. */
  --text: oklch(0.965 0.012 95);
  --muted: oklch(0.74 0.02 150);
  --faint: oklch(0.56 0.02 155);

  /* Brand + status. Creai's own identity (configs/creai.json): green + gold.
     A licensee rebrands the console by swapping this block. */
  --primary: oklch(0.76 0.14 160);
  --primary-bright: oklch(0.84 0.13 162);
  --on-primary: oklch(0.2 0.04 165);
  --brand-green: oklch(0.56 0.11 160);
  --brand-green-bright: oklch(0.76 0.14 160);
  --brand-gold: oklch(0.82 0.12 86);
  --brand-gold-bright: oklch(0.89 0.12 90);
  --teal: oklch(0.8 0.1 200);
  --violet: oklch(0.8 0.11 300);
  --violet-bg: oklch(0.8 0.11 300 / 0.15);
  --ok: oklch(0.8 0.15 152);
  --danger: oklch(0.75 0.15 28);
  --warn: oklch(0.85 0.13 82);
  --ok-bg: oklch(0.8 0.15 152 / 0.14);
  --danger-bg: oklch(0.72 0.17 28 / 0.16);
  --warn-bg: oklch(0.85 0.13 82 / 0.14);

  /* Fonts */
  --font-sans: "Public Sans", system-ui, -apple-system, "Segoe UI", sans-serif;
  --font-mono: "IBM Plex Mono", ui-monospace, "SF Mono", Menlo, monospace;

  /* Type scale — one ladder; new component CSS picks a rung, never a value
     between rungs. */
  --fs-xs: 11px;   /* meta, timestamps, badges */
  --fs-sm: 12px;   /* secondary labels, dense table cells */
  --fs-base: 13px; /* body default */
  --fs-md: 15px;   /* emphasized body, card titles */
  --fs-lg: 18px;   /* section headings */
  --fs-xl: 24px;   /* page headings */
  --fs-2xl: 32px;  /* hero stat numbers (Impact report) */

  /* Spacing scale — 4px based. */
  --sp-1: 4px;
  --sp-2: 8px;
  --sp-3: 12px;
  --sp-4: 16px;
  --sp-5: 24px;
  --sp-6: 32px;
  --sp-7: 48px;

  /* z-index scale */
  --z-sticky: 5;
  /* Above the timeline's own hover-raised cards (z-index 4) and the sticky
     card head, below the drawer — a menu opens over the page it acts on. */
  --z-menu: 20;
  --z-drawer-backdrop: 40;
  --z-drawer-panel: 41;
  --z-toast: 100;

  /* Shape, depth, motion */
  --r-sm: 8px;
  --r-md: 12px;
  --r-lg: 18px;
  --shadow: 0 1px 2px oklch(0 0 0 / 0.35), var(--glass-hi);
  --shadow-lg: 0 18px 48px oklch(0.05 0.02 168 / 0.55), 0 2px 8px oklch(0 0 0 / 0.3);
  --ring: 0 0 0 3px oklch(0.76 0.14 160 / 0.3), 0 0 22px oklch(0.76 0.14 160 / 0.22);
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
  --ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
}

* { box-sizing: border-box; }

/* The page never moves sideways: the scrollbar's gutter is reserved on the root
   scroller (on body it does nothing), and nothing may overflow horizontally —
   a decoration hanging 60px past the edge made every page scroll 50px (B167). */
html { background: var(--bg); scrollbar-gutter: stable; overflow-x: clip; }

/* The body is its own stacking context so the ambient layers below can sit at
   z-index -1: under every in-flow element, over the body background. Without
   isolation they would paint OVER unpositioned content and tint it. */
body {
  margin: 0;
  font-family: var(--font-sans);
  background: var(--bg);
  color: var(--text);
  font-size: 14px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  overflow-x: clip;
  min-height: 100vh;
  isolation: isolate;
}

/* Ground — a still, faint tint in the section's colour. Working pages hold
   dense data, so nothing behind them moves (B167). */
body::before {
  content: ""; position: fixed; inset: 0; z-index: -1; pointer-events: none;
  background:
    radial-gradient(60vmax 40vmax at 0% 0%, color-mix(in oklch, var(--amb-a) 28%, transparent), transparent 70%),
    radial-gradient(50vmax 36vmax at 100% 100%, color-mix(in oklch, var(--amb-c) 16%, transparent), transparent 70%);
}
/* Film grain — keeps the gradients from banding and gives the dark a texture. */
body::after {
  content: ""; position: fixed; inset: 0; z-index: -1; pointer-events: none;
  opacity: 0.07; mix-blend-mode: soft-light;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}
::selection { background: oklch(0.76 0.14 160 / 0.35); color: var(--text); }
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-thumb { background: oklch(1 0 0 / 0.1); border-radius: 999px; border: 2px solid transparent; background-clip: padding-box; }
::-webkit-scrollbar-thumb:hover { background: oklch(1 0 0 / 0.18); background-clip: padding-box; }
::-webkit-scrollbar-track { background: transparent; }

.page { max-width: 1040px; margin: 0 auto; padding: 22px 24px 96px; }
/* Beside the rail the page starts at the rail, so a width change never re-centres it. */
@media (min-width: 900px) { .page { margin-left: 0; } }

/* Brand lockup — static markup at the top of each console page. */
.ce-brand {
  display: inline-flex; align-items: center; gap: 10px; margin: 0 0 18px;
  color: var(--text); text-decoration: none; font-weight: 700; font-size: 17px;
  letter-spacing: -0.02em;
}
.ce-mark { width: 30px; height: 30px; flex: none; overflow: visible; }
.ce-mark-track { stroke: oklch(1 0 0 / 0.12); }
.ce-mark-sweep { transform-origin: 16px 16px; }
.ce-mark-core { transform-origin: 16px 16px; }
.ce-mark-spark { transform-origin: 25px 7px; }
.ce-mark-ripple { display: none; }
.ce-wordmark { background: linear-gradient(95deg, var(--text), var(--brand-gold-bright)); -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent; }

h1 {
  font-size: clamp(26px, 3.2vw, 36px); font-weight: 750; letter-spacing: -0.035em; line-height: 1.1;
  margin: 0 0 18px; color: var(--text);
}
h2 {
  font-size: 11.5px; text-transform: uppercase; letter-spacing: 0.09em;
  color: var(--muted); font-weight: 650; margin: 40px 0 12px;
}
h2::before {
  content: ""; display: inline-block; vertical-align: middle; margin: -2px 9px 0 0;
  width: 6px; height: 6px; border-radius: 2px;
  background: linear-gradient(135deg, var(--brand-gold-bright), var(--primary));
  box-shadow: 0 0 10px oklch(0.82 0.12 86 / 0.6);
}

a { color: var(--primary-bright); text-decoration-color: oklch(0.84 0.13 162 / 0.4); text-underline-offset: 3px; }
a:hover { text-decoration-color: currentColor; }

/* Nav — a glass segmented rail. Pages swap without a transition: the rail and
   the frame stay exactly where they were (B167). */
nav {
  display: flex; flex-wrap: wrap; align-items: center; gap: 2px; width: fit-content; max-width: 100%;
  padding: 4px; margin-bottom: 22px; border-radius: 22px;
  background: var(--glass), oklch(0.18 0.016 166 / 0.6);
  border: 1px solid var(--border-soft); box-shadow: var(--shadow);
  backdrop-filter: blur(14px) saturate(1.3);
}
nav a {
  position: relative; color: var(--muted); font-size: 13px; font-weight: 550; text-decoration: none;
  padding: 7px 14px; border-radius: 999px; border: 1px solid transparent;
  transition: color 0.2s, background 0.2s;
}
nav a:hover { color: var(--text); background: oklch(1 0 0 / 0.05); }
nav a[aria-current="page"] {
  color: var(--text);
  background: linear-gradient(180deg, oklch(0.76 0.14 160 / 0.28), oklch(0.56 0.11 160 / 0.2));
  border-color: oklch(0.76 0.14 160 / 0.35);
  box-shadow: 0 0 20px oklch(0.76 0.14 160 / 0.22), inset 0 1px 0 oklch(1 0 0 / 0.1);
}
nav button { min-height: 30px; border-radius: 999px; }

@keyframes ce-rise { from { opacity: 0; } }

/* Reduced motion: the few feedback motions left (entrance fades, the drawer's
   slide, toasts, the press) become plain fades or stop. */
@media (prefers-reduced-motion: reduce) {
  h1, nav, .controls, .cards > .card, .page > * { animation: none !important; }
  button { transform: none !important; }
  .ce-drawer-panel { transform: none !important; transition: opacity 0.2s ease; }
  .ce-toast { animation: ce-fade-in 0.2s ease, ce-fade-out 0.3s ease 3.5s forwards; }
  button.loading::before { animation-duration: 1.6s; }
}
@keyframes ce-fade-in { from { opacity: 0; } }
@keyframes ce-fade-out { to { opacity: 0; } }


/* Toolbar — the client/owner-key row. Only a local operator sees it (a signed-in
   creator's is hidden by arrangePageChrome), as a plain strip that scrolls away
   with the page instead of floating over it (B167). */
.controls {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
  padding: 8px 12px; margin: 0 0 14px; border-radius: var(--r-md);
  background: oklch(1 0 0 / 0.03);
  border: 1px dashed var(--border-soft);
  font-size: var(--fs-sm);
}
.controls.connection::before { content: "Connection"; font-size: 11px; text-transform: uppercase; letter-spacing: 0.08em; color: var(--faint); margin-right: 4px; }
.controls .logout { margin-left: auto; }

/* A one-line note about a change in how the numbers are computed (B168). */
.recount-note { margin: -8px 0 14px; font-size: var(--fs-sm); color: var(--muted); max-width: 70ch; }

/* The page's own actions, beside its title. min-height reserves the row so a
   status message arriving later never pushes the page down. */
.page-actions {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
  min-height: 34px; margin: -6px 0 18px;
}
.page-actions #status { order: 99; margin-left: auto; color: var(--muted); font-size: var(--fs-sm); min-width: 1ch; }

/* Account and Log out: pinned to the foot of the rail on desktop, top-right of
   the page on a phone (the bottom tab bar has no room). */
.rail-foot { display: flex; align-items: center; gap: 6px; }
.rail-foot .client-name { font-size: 10.5px; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rail-foot select { font-size: 10.5px; padding: 4px; min-height: 28px; max-width: 100%; }
.rail-foot button { min-height: 30px; font-size: 11px; padding: 4px 8px; }
@media (min-width: 900px) {
  .rail-foot { position: fixed; z-index: 31; left: 14px; bottom: 26px; width: 78px; flex-direction: column; align-items: stretch; padding: 0 7px; }
  .rail-foot .client-name { text-align: center; }
  .ce-signed-in :is(.page, .layout) > nav { padding-bottom: 120px; }
}
@media (max-width: 899px) {
  .rail-foot { position: absolute; top: 18px; right: 16px; }
  .rail-foot .client-name { max-width: 120px; }
}


/* Hosted mode: the creator's client display name, shown where the raw
   client-id input was (initClientId hides the input and fills this). */
.client-name { font-weight: 600; color: var(--text); }
.client-name:empty { display: none; }

/* Video-level interest classification (one per video; items inherit it) */
.video-context {
  margin: 10px 0 14px; padding: 12px 14px;
  background: var(--field); border: 1px solid var(--border);
  border-radius: var(--r-sm);
}
.ctx-label {
  font-size: 11px; font-weight: 700; letter-spacing: 0.02em;
  text-transform: uppercase; color: var(--muted); margin-bottom: 4px;
}
.ctx-hint { font-size: 11px; color: var(--muted); margin: 2px 0 8px; }
.ig-hint { font-size: 12px; color: var(--muted); margin: -4px 0 10px; }
/* Read-only inherited classification shown on each item editor */
.ctx-inline {
  margin-top: 10px; font-size: 11px; color: var(--muted);
}

/* Commercial-card (action) authoring group — shown when Type = action. */
.cta-section {
  margin-top: 10px; padding: 10px 12px;
  border: 1px solid var(--border); border-left: 3px solid var(--brand-gold);
  border-radius: var(--r-sm); background: var(--surface-2);
}
.cta-section::before {
  content: "Promo card"; display: block;
  font-size: 11px; font-weight: 700; letter-spacing: 0.02em;
  text-transform: uppercase; color: var(--muted); margin-bottom: 6px;
}

/* Compact prompt rows (review list) — collapsed by default; expand one to edit. */
.item-list { display: flex; flex-direction: column; gap: 6px; margin-top: 8px; }
.item-summary {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 10px; border: 1px solid var(--border);
  border-radius: var(--r-sm); background: var(--surface-2);
  cursor: pointer;
}
.item-summary:hover { border-color: var(--primary-bright); }
.is-time {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums; color: var(--muted);
  font-size: 12px; min-width: 44px;
}
.is-type {
  font-size: 10px; text-transform: uppercase; letter-spacing: 0.03em;
  padding: 2px 7px; border-radius: 999px;
  background: var(--field); color: var(--muted); white-space: nowrap;
}
.is-type.is-action { background: var(--violet-bg); color: var(--violet); }
/* Two fixed lines, not one ellipsised line. 72% of real rows overflowed the old
   90-char truncation, and expanding one in place displaced every row below it —
   so the list could not be scanned while reading any part of it. */
.is-body {
  flex: 1; min-width: 0; overflow: hidden;
  font-size: 13px; color: var(--text); line-height: 1.4;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
}
.item-summary .is-edit { margin-left: auto; }

/* ── Prompt editor groups (B173) ────────────────────────────────────────────
 * Sam: "drawer is too complex." The editor was one flat stack of ~14 controls
 * with nothing saying which belonged together, so setting a jump target and
 * changing the card style looked like the same kind of decision. Three
 * <details> groups — Question / When it appears / Look — plus Follow-ups,
 * which the review page appends itself. */
.item-group { border-top: 1px solid var(--border-soft); margin-top: var(--sp-3); }
.item-group:first-of-type { border-top: 0; margin-top: var(--sp-2); }
.item-group-head {
  display: flex; align-items: center; gap: 6px;
  padding: 8px 0; cursor: pointer; user-select: none;
  font-size: 11px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--muted); list-style: none;
}
.item-group-head::-webkit-details-marker { display: none; }
.item-group-head::marker { content: ""; }
/* A caret the group owns, so the open/closed state reads without the default
   platform marker (which differs per browser and sits awkwardly in a header). */
.item-group-head::before {
  content: "\25B8"; display: inline-block; font-size: 10px;
  transition: transform 0.15s ease; color: var(--faint);
}
.item-group[open] > .item-group-head::before { transform: rotate(90deg); }
.item-group-head:hover { color: var(--text); }
.item-group-body { padding-bottom: var(--sp-2); }
/* Replaces the old "Save changes" button: edits have autosaved since B167, so
   the panel says so rather than offering a button that saved nothing. */
.editor-autosave-note {
  margin-top: var(--sp-2);
  font-size: 11.5px; color: var(--faint);
}
.item-editor-footer {
  margin-top: var(--sp-3); padding-top: var(--sp-3);
  border-top: 1px solid var(--border-soft);
}
.fu-add { display: flex; gap: 6px; margin-top: 8px; flex-wrap: wrap; }
.fu-type { margin-left: 6px; }
/* Follow-up tree — shared by the review drawer AND the timeline inspector
   (moved out of review.html so both pages style renderFollowupTree). */
.fu-node { border-left: 2px solid var(--border); padding: 6px 0 6px 10px; margin: 6px 0; }
.fu-head { display: flex; align-items: center; gap: 8px; margin-bottom: 4px; }
.fu-on { font-weight: 700; white-space: nowrap; font-size: 11px; }
.fu-on.fu-correct { color: var(--ok); }
.fu-on.fu-wrong { color: var(--danger); }
.fu-head .reject { margin-left: auto; }

/* Live card preview — renders the REAL L1 card; styled here for the console
   (light) surface so the creator sees what ships without loading overlay.css. */
.card-preview { margin-top: 12px; padding: 12px; border: 1px dashed var(--border); border-radius: var(--r-md); }
.card-preview-label { font-size: 10px; text-transform: uppercase; letter-spacing: 0.03em; color: var(--muted); margin-bottom: 8px; }
.card-preview .ce-card {
  background: #fff; color: #111; border-radius: 14px; padding: 14px 16px;
  max-width: 340px; box-shadow: 0 6px 20px rgba(0, 0, 0, 0.28); font-size: 14px; line-height: 1.5;
}
.card-preview .ce-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px; }
.card-preview .ce-title { font-size: 12px; font-weight: 600; color: #555; }
.card-preview .ce-dismiss { background: none; border: none; color: #aaa; cursor: default; font-size: 14px; }
.card-preview .ce-body { margin-bottom: 10px; }
/* Mirror the overlay's long-text fit so the preview shows the shipped size. */
.card-preview .ce-body[data-len="long"] { font-size: 13px; line-height: 1.45; }
.card-preview .ce-body[data-len="xlong"] { font-size: 12px; line-height: 1.4; }
.card-preview .ce-content { display: flex; flex-direction: column; gap: 6px; }
.card-preview .ce-btn {
  display: inline-flex; align-items: center; justify-content: center;
  padding: 8px 12px; border-radius: 8px; border: 1px solid #ccc;
  background: #f3f3f3; color: #111; cursor: default; font-size: 13px;
}
.card-preview .ce-btn-choice, .card-preview .ce-btn-scale { width: 100%; }
.card-preview .ce-cta-image { width: 100%; max-height: 140px; object-fit: cover; border-radius: 8px; }
.card-preview .ce-cta-price { font-weight: 700; font-size: 15px; }
.card-preview .ce-cta-disclosure { font-size: 11px; opacity: 0.6; }
/* Dark ink on the gold, not white: white-on-gold measures 2.01:1, well under
   the 4.5:1 AA floor (the blue this replaced was 4.90:1). Dark is 10.5:1. */
.card-preview .ce-btn-cta { width: 100%; background: var(--brand-gold); color: #0b0d10; border-color: transparent; font-weight: 600; }
.card-preview .ce-feedback, .card-preview .ce-snooze { display: none; } /* not relevant in preview */

/* Promo-card analytics — a funnel card per commerciable prompt, not a raw table. */
.cta-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 12px; margin-top: 8px; }
.cta-card {
  border: 1px solid var(--border); border-radius: var(--r-md);
  padding: 12px 14px; background: var(--surface-2);
}
.cta-card-head { display: flex; align-items: center; gap: 8px; margin-bottom: 4px; }
.cta-kind-badge {
  font-size: 10px; text-transform: uppercase; letter-spacing: 0.03em;
  padding: 2px 7px; border-radius: 999px; white-space: nowrap;
  background: var(--field); color: var(--muted);
}
.cta-kind-badge.k-shop { background: var(--violet-bg); color: var(--violet); }
.cta-kind-badge.k-tip, .cta-kind-badge.k-fund { background: oklch(0.8 0.13 55 / 0.15); color: oklch(0.83 0.13 58); }
.cta-kind-badge.k-sponsor { background: var(--ok-bg); color: var(--ok); }
.cta-card-title { font-size: 13px; color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cta-card-video { font-size: 11px; color: var(--muted); margin-bottom: 8px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cta-funnel { display: flex; align-items: center; gap: 10px; margin: 6px 0; }
.cta-stat { display: flex; flex-direction: column; line-height: 1.1; }
.cta-stat b { font-size: 18px; }
.cta-stat span { font-size: 10px; color: var(--muted); text-transform: uppercase; }
.cta-arrow { color: var(--muted); }
.cta-ctr { margin-left: auto; font-size: 12px; color: var(--muted); }
.cta-ctr b { font-size: 14px; color: var(--text); }
.cta-bar { height: 6px; border-radius: 999px; background: var(--field); overflow: hidden; }
.cta-bar-fill { height: 100%; background: linear-gradient(90deg, var(--violet), var(--brand-gold)); border-radius: 999px; box-shadow: 0 0 12px oklch(0.8 0.11 300 / 0.5); animation: ce-grow 1s var(--ease-out) both; transform-origin: left; }
@keyframes ce-grow { from { transform: scaleX(0); } }

/* Forms */
label { font-size: 12px; color: var(--muted); }
input, select, textarea {
  background: var(--field); color: var(--text);
  border: 1px solid var(--border); border-radius: var(--r-sm);
  padding: 8px 11px; font-size: 13px; font-family: inherit;
  box-shadow: inset 0 1px 2px oklch(0 0 0 / 0.25);
  transition: border-color 0.2s, box-shadow 0.25s var(--ease-out), background 0.2s;
}
input:hover, select:hover, textarea:hover { border-color: oklch(1 0 0 / 0.18); }
input:focus, select:focus, textarea:focus { outline: none; border-color: var(--primary); box-shadow: var(--ring); background: oklch(0.13 0.014 166 / 0.9); }
input::placeholder, textarea::placeholder { color: var(--faint); }
select option { background: var(--bg-solid); color: var(--text); }
/* A checkbox is not a text field: scoped by type so it keeps its real shape. */
input[type="checkbox"], input[type="radio"] {
  width: auto; padding: 0; margin: 0; flex: none; accent-color: var(--brand-gold); box-shadow: none;
}
textarea { min-height: 54px; resize: vertical; width: 100%; line-height: 1.5; }

/* Buttons — three real sizes; even .xs keeps a 28px tap target (WCAG-AA 24). */
button {
  cursor: pointer; border: 1px solid var(--border-soft); border-radius: var(--r-sm);
  padding: var(--sp-2) var(--sp-3); font-weight: 600; font-size: var(--fs-base);
  font-family: inherit; min-height: 34px;
  background: var(--glass), var(--surface-3); color: var(--text);
  transition: background 0.2s, border-color 0.2s, box-shadow 0.25s var(--ease-out), transform 0.08s ease, color 0.2s;
}
button:hover { background: var(--glass), oklch(0.32 0.024 166 / 0.9); border-color: var(--border); }
/* Depth and a press on plain and variant buttons: component buttons (timeline
   marks, drawer close, row openers) own their transform and shadow. Hover never
   moves a button — only its colour changes (B167). */
:is(button:not([class]), button.primary, button.approve, button.reject, button.xs) { box-shadow: var(--glass-hi), 0 1px 2px oklch(0 0 0 / 0.3); }
:is(button:not([class]), button.primary, button.approve, button.reject, button.xs):active { transform: scale(0.97); transition-duration: 0.06s; }
button.primary { position: relative; }
button:focus-visible { outline: none; box-shadow: var(--ring); }
button:disabled { opacity: 0.5; cursor: default; transform: none; }
button.loading { position: relative; padding-left: 32px; pointer-events: none; }
button.loading::before {
  content: ""; position: absolute; left: 10px; top: 50%; margin-top: -6px;
  width: 12px; height: 12px; border: 2px solid currentColor;
  border-right-color: transparent; border-radius: 50%;
  animation: ce-spin 0.6s linear infinite;
}
@keyframes ce-spin { to { transform: rotate(360deg); } }
button.primary {
  background: linear-gradient(180deg, var(--primary-bright), var(--primary)); color: var(--on-primary);
  border-color: oklch(0.9 0.1 160 / 0.4); overflow: hidden;
  box-shadow: 0 0 0 1px oklch(0.4 0.08 160 / 0.4), 0 6px 20px oklch(0.76 0.14 160 / 0.28), inset 0 1px 0 oklch(1 0 0 / 0.35);
}
button.primary:hover { background: linear-gradient(180deg, oklch(0.88 0.12 162), var(--primary-bright)); box-shadow: 0 0 0 1px oklch(0.4 0.08 160 / 0.4), 0 10px 30px oklch(0.76 0.14 160 / 0.4), inset 0 1px 0 oklch(1 0 0 / 0.4); }
button.approve { background: var(--ok-bg); color: var(--ok); border-color: oklch(0.8 0.15 152 / 0.3); }
button.approve:hover { background: oklch(0.8 0.15 152 / 0.24); box-shadow: 0 0 18px oklch(0.8 0.15 152 / 0.25); }
button.reject { background: var(--danger-bg); color: var(--danger); border-color: oklch(0.75 0.15 28 / 0.3); }
button.reject:hover { background: oklch(0.75 0.15 28 / 0.24); }
/* Dense variant for inline row actions — still ≥ 28px tall (WCAG-AA is 24px). */
button.xs {
  padding: 5px var(--sp-2); font-size: var(--fs-xs); font-weight: 500; min-height: 28px;
}

/* Panel (generic surface; pages keep their own .card variants) */
.panel {
  background: var(--glass), var(--surface); border: 1px solid var(--border-soft);
  border-radius: var(--r-lg); box-shadow: var(--shadow);
  backdrop-filter: blur(16px) saturate(1.2);
}

/* Pills / badges */
.pill {
  font-size: 11px; font-weight: 700; padding: 2px 9px; border-radius: 999px;
  white-space: nowrap; border: 1px solid transparent;
}
.pill.ok { background: var(--ok-bg); color: var(--ok); border-color: oklch(0.8 0.15 152 / 0.22); }
.pill.bad { background: var(--danger-bg); color: var(--danger); border-color: oklch(0.75 0.15 28 / 0.22); }
.pill.warn { background: var(--warn-bg); color: var(--warn); border-color: oklch(0.85 0.13 82 / 0.22); }

/* States */
.empty { color: var(--muted); margin-top: 14px; font-size: 13px; }
#status { font-size: 12px; margin-left: 6px; }

/* Toast notifications — floating, fixed-position, auto-dismissing. */
.ce-toast-container {
  position: fixed; bottom: 20px; right: 20px; z-index: var(--z-toast);
  display: flex; flex-direction: column; gap: 8px;
  pointer-events: none;
}
.ce-toast {
  pointer-events: auto; position: relative; overflow: hidden;
  background: var(--glass), oklch(0.22 0.02 166 / 0.85); color: var(--text);
  border: 1px solid var(--border);
  border-radius: var(--r-md); padding: 11px 16px 11px 34px;
  font-size: 13px; font-weight: 500;
  box-shadow: var(--shadow-lg), var(--glass-hi);
  backdrop-filter: blur(18px) saturate(1.4);
  animation: ce-toast-in 0.5s var(--ease-spring), ce-toast-out 0.35s ease 3.5s forwards;
  max-width: 360px;
}
.ce-toast::before {
  content: ""; position: absolute; left: 14px; top: 50%; width: 8px; height: 8px; margin-top: -4px;
  border-radius: 50%; background: var(--primary); box-shadow: 0 0 0 4px oklch(0.76 0.14 160 / 0.2), 0 0 14px var(--primary);
}
.ce-toast.ok::before { background: var(--ok); box-shadow: 0 0 0 4px var(--ok-bg), 0 0 14px var(--ok); }
.ce-toast.err::before { background: var(--danger); box-shadow: 0 0 0 4px var(--danger-bg), 0 0 14px var(--danger); }
/* The drain line shows how long the toast will stay. */
.ce-toast::after {
  content: ""; position: absolute; left: 0; bottom: 0; height: 2px; width: 100%;
  background: linear-gradient(90deg, var(--primary), var(--brand-gold)); transform-origin: left;
  animation: ce-toast-drain 3.5s linear forwards; opacity: 0.7;
}
.ce-toast.err::after { background: var(--danger); }
@keyframes ce-toast-drain { to { transform: scaleX(0); } }
@keyframes ce-toast-in {
  from { opacity: 0; transform: translateY(16px) scale(0.94); }
  to { opacity: 1; transform: none; }
}
@keyframes ce-toast-out {
  from { opacity: 1; }
  to { opacity: 0; transform: translateY(-8px) scale(0.98); }
}

/* Shared pack timeline component (review + analytics).
 * .pack-tl-axis's height is set inline per-pack (JS computes it from how many
 * stacked "rings" of preview cards are needed above/below the track — see
 * placeMarkers() in pack-timeline.ts); everything inside is positioned via
 * inline `top`/`left`/`bottom` computed from that same layout pass. */
.pack-tally { font-size: 12px; color: var(--muted); margin: 4px 0 8px; }
.pack-tl-empty { font-size: 12px; color: var(--muted); }
.pack-tl-axis { position: relative; background: radial-gradient(120% 80% at 50% 50%, oklch(0.56 0.12 160 / 0.08), transparent 70%), var(--field); border: 1px solid var(--border-soft); border-radius: var(--r-md); margin-bottom: 24px; }
.pack-tl-track { position: absolute; left: 0; right: 0; height: 2px; border-radius: 1px; background: linear-gradient(90deg, transparent, oklch(0.76 0.14 160 / 0.55) 12%, oklch(0.82 0.12 86 / 0.5) 60%, oklch(0.8 0.1 200 / 0.45) 88%, transparent); }
.pack-tl-track::after { content: ""; position: absolute; top: -1px; width: 80px; height: 4px; border-radius: 2px; background: radial-gradient(closest-side, oklch(1 0 0 / 0.9), transparent); display: none; }
.pack-tl-ticks { position: absolute; left: 0; right: 0; }
.pack-tl-tick { position: absolute; top: 10px; transform: translateX(-50%); font-size: 10px; font-family: var(--font-mono); color: var(--muted); }
.pack-tl-connector {
  position: absolute; width: 1.5px; opacity: 0.45; background: var(--muted);
  transform: translateX(-50%); pointer-events: none;
}
/* Fixed 22px circle. min-height:0 is load-bearing: the global button rule sets
   min-height:34px, which clamps this to 22x34 — an oval, with the glyph stuck
   in the top 22px by the line-height and the marker overhanging the axis by
   17px instead of 11px because of the -50% translate. */
.pack-tl-mark {
  position: absolute; transform: translate(-50%, -50%);
  width: 22px; height: 22px; min-height: 0; border-radius: 50%; border: none;
  color: var(--on-primary); font-weight: 700; font-size: 12px; line-height: 22px; padding: 0;
  box-shadow: 0 0 0 3px var(--bg-solid), 0 0 16px var(--mark-glow, var(--primary));
  transition: transform 0.35s var(--ease-spring), box-shadow 0.25s; z-index: 2;
}
.pack-tl-mark[data-primitive="single_choice"] { background: var(--teal); --mark-glow: var(--teal); }
.pack-tl-mark[data-primitive="scale"] { background: var(--warn); --mark-glow: var(--warn); }
.pack-tl-mark[data-primitive="notice"] { background: var(--ok); --mark-glow: var(--ok); }
.pack-tl-mark[data-primitive="action"] { background: var(--violet); --mark-glow: var(--violet); }
.pack-tl-mark[data-primitive="up_next"] { background: var(--primary-bright); --mark-glow: var(--primary-bright); }
.pack-tl-mark:hover { transform: translate(-50%, -50%) scale(1.25); background-image: none; }
.pack-tl-mark.sel { outline: none; box-shadow: 0 0 0 3px var(--bg-solid), 0 0 0 5px var(--text), 0 0 26px var(--mark-glow, var(--primary)); }
/* Draggable when the caller wired onMove (Review; never analytics' read-only
 * timeline). touch-action:none stops the browser scrolling the page instead
 * of tracking the drag on a touchscreen. */
.pack-tl.draggable .pack-tl-mark { cursor: grab; touch-action: none; }
.pack-tl-mark.dragging { cursor: grabbing; box-shadow: 0 0 0 3px var(--bg-solid), 0 0 24px var(--mark-glow, var(--primary)); z-index: 3; }
/* Rests as a compact chip — width matches CARD_WIDTH_PX in pack-timeline.ts,
 * which the collision math (placeMarkers) is based on; keep the two in sync.
 * `:hover` and `.sel` (the selected item, so it reads fully even when nothing
 * is under the pointer) widen it back out and reveal the options/note below —
 * a momentary overlap of a neighbour on expansion is fine, same as the
 * existing z-index bump already assumed (B170: "small chip by default, full
 * card on hover/select"). The action row (Edit/Delete/+Follow-up) is NEVER
 * hidden by this — `.pack-tl-card-act.edit` etc. stay clickable at rest,
 * a test contract (tests/e2e/pack-write-race.spec.ts). */
.pack-tl-card {
  position: absolute; transform: translateX(-50%); width: 96px;
  background: var(--surface-2); border: 1px solid var(--border-soft);
  border-top: 2.5px solid var(--muted); border-radius: var(--r-sm);
  padding: 5px 7px; cursor: pointer; z-index: 1;
  box-shadow: var(--shadow-lg);
  transition: width 0.15s var(--ease-out);
}
.pack-tl-card:hover, .pack-tl-card.sel { width: 158px; border-color: var(--muted); }
.pack-tl-card-kicker {
  display: inline-block; font-size: 9px; font-weight: 700; letter-spacing: 0.03em;
  text-transform: uppercase; color: var(--muted);
}
.pack-tl-card-time {
  float: right; font-family: var(--font-mono); font-size: 9.5px; color: var(--faint);
}
.pack-tl-card-body {
  clear: both; margin-top: 3px; font-size: 10.5px; font-weight: 600; color: var(--text);
  line-height: 1.3; display: -webkit-box; -webkit-line-clamp: 1; -webkit-box-orient: vertical;
  overflow: hidden;
}
.pack-tl-card:hover .pack-tl-card-body, .pack-tl-card.sel .pack-tl-card-body {
  font-size: 11px; -webkit-line-clamp: 3;
}
.pack-tl-card-note { display: none; margin-top: 4px; font-size: 10px; color: var(--warn); }
.pack-tl-card:hover .pack-tl-card-note, .pack-tl-card.sel .pack-tl-card-note { display: block; }
.pack-tl-connector.follow-up { opacity: 0.3; }
.pack-tl-card.follow-up {
  width: 78px; padding: 4px 6px; border-top-color: var(--muted);
  background: var(--surface-3);
}
.pack-tl-card.follow-up:hover, .pack-tl-card.follow-up.sel { width: 140px; }
.pack-tl-card.follow-up .pack-tl-card-kicker { color: var(--primary-bright); }
.pack-tl-card.follow-up .pack-tl-card-body { font-size: 9.5px; font-weight: 500; -webkit-line-clamp: 1; }
.pack-tl-card.follow-up:hover .pack-tl-card-body, .pack-tl-card.follow-up.sel .pack-tl-card-body {
  font-size: 10px; -webkit-line-clamp: 2;
}

/* Inline single_choice options on a timeline card — the keyed answer glows. */
.pack-tl-card-options { clear: both; margin-top: 5px; display: none; flex-direction: column; gap: 2px; }
.pack-tl-card:hover .pack-tl-card-options, .pack-tl-card.sel .pack-tl-card-options { display: flex; }
.pack-tl-card-option {
  display: flex; align-items: center; gap: 4px; justify-content: space-between;
  font-size: 9.5px; line-height: 1.25; color: var(--muted);
  background: var(--surface-3); border-radius: 4px; padding: 2px 5px;
}
.pack-tl-card-option-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pack-tl-card-option.correct {
  background: var(--ok-bg); color: var(--ok); font-weight: 700;
}
.pack-tl-card-option-jump { flex: none; font-family: var(--font-mono); font-size: 9px; color: var(--primary-bright); }
/* The landing beat for a branch option. Since 1.20.0 nothing is anchored at
   the destination, so this is the only place the creator can see what a jump
   actually gives the viewer. Indented under its option row. */
.pack-tl-card-option-arrival {
  margin: 2px 0 6px 10px;
  padding-left: 8px;
  border-left: 2px solid var(--primary-bright);
  font-size: 10px;
  line-height: 1.4;
  color: var(--muted);
  opacity: 0.9;
}
.pack-tl-card.follow-up .pack-tl-card-option { font-size: 9px; }

/* Card action buttons (Follow-up / Edit / Delete). Same 28px-minimum dense
   button as button.xs — this used to be a separate 9.5px/19px component that
   button.xs's own fix never touched. */
.pack-tl-card-actions { clear: both; margin-top: var(--sp-2); display: flex; flex-wrap: wrap; gap: 4px; }
.pack-tl-card-act {
  flex: 1 1 auto; padding: 5px var(--sp-2); font-size: var(--fs-xs); font-weight: 600; min-height: 28px;
  border-radius: var(--r-sm); border: 1px solid var(--border-soft); background: var(--surface-3);
  color: var(--text); cursor: pointer; white-space: nowrap;
}
.pack-tl-card-act:hover { border-color: var(--muted); }
.pack-tl-card-act.fu { color: var(--primary-bright); }
.pack-tl-card-act.del { color: var(--danger); }
.pack-tl-untimed { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin-top: 6px; }
.pack-tl-untimed-lbl { font-size: 11px; color: var(--muted); }
.pack-tl-chip {
  background: var(--surface-3); color: var(--text); border: none; border-radius: 999px;
  padding: 3px 10px; font-size: 11px; max-width: 320px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.pack-tl-chip.sel { outline: 2px solid var(--text); }
.pack-tl-ghost {
  position: absolute; transform: translate(-50%, -50%);
  width: 16px; height: 16px; border-radius: 50%; cursor: pointer;
  border: 2px dashed var(--primary-bright); background: transparent; z-index: 3;
}

/* Click-to-add moment action bar — shared by Review's mini-timeline and the
   Timeline page's full axis (was page-local inline CSS on timeline.html). */
.moment-bar { display: none; align-items: center; flex-wrap: wrap; gap: 8px; margin-top: 8px; padding: 8px 10px; background: var(--surface-3); border: 1px solid var(--border-soft); border-radius: var(--r-sm); }
.moment-bar.active { display: flex; }
.moment-bar .moment-label { font-size: 12px; color: var(--text); font-weight: 600; }
.moment-bar .moment-hint { font-size: 11px; color: var(--muted); }
/* Right where the creator clicked — a raw provider error used to only reach
 * the page-header status line, far from the timeline (B171). */
.moment-error { flex-basis: 100%; font-size: 11.5px; color: var(--danger); }

/* YouTube Analytics Hotspots suggestion rows — shared by Review (inline panel)
   and Timeline (was page-local inline CSS on timeline.html). */
.reco { background: var(--surface-2); border: 1px solid var(--border-soft); border-radius: var(--r-sm); padding: 9px 12px; margin: 6px 0; display: flex; gap: 10px; align-items: baseline; font-size: 13px; }
.reco .at { color: var(--primary-bright); font-weight: 700; font-size: 12px; white-space: nowrap; }
.reco .why { flex: 1; }
.reco .why .d { color: var(--muted); font-size: 12px; }
.reco button { font-size: 11px; white-space: nowrap; }
.hotspots-panel { margin: 8px 0; }

/* Readiness donut (renderReadinessMeter) — conic-gradient ring, no SVG. */
.pack-readiness { display: flex; align-items: center; gap: 16px; }
.pack-readiness-donut {
  --pct: 0;
  width: 56px; height: 56px; border-radius: 50%; flex-shrink: 0;
  background: conic-gradient(var(--ring-color, var(--primary-bright)) calc(var(--pct) * 3.6deg), var(--surface-3) 0deg);
}
.pack-readiness-donut.band-high { --ring-color: var(--ok); }
.pack-readiness-donut.band-medium { --ring-color: var(--warn); }
.pack-readiness-donut.band-low { --ring-color: var(--danger); }
.pack-readiness-donut-hole {
  width: 42px; height: 42px; margin: 7px; border-radius: 50%;
  background: var(--card); display: flex; align-items: center; justify-content: center;
}
.pack-readiness-donut-pct {
  font-family: var(--font-mono); font-size: 13px; font-weight: 600; color: var(--text);
}
.pack-readiness-detail { flex: 1; min-width: 0; }

/* Branch (choose-your-own-path) health panel (renderBranchHealth). */
.pack-branch-health { margin: 8px 0; }
.pack-branch-health-brief {
  margin-bottom: 8px; padding: 8px 10px; border-radius: var(--r-md, 9px);
  background: var(--surface-2, var(--card)); border: 1px solid var(--border-soft);
}
.pack-branch-health-brief-label {
  font-size: 10px; text-transform: uppercase; letter-spacing: 0.04em;
  color: var(--muted); margin-bottom: 3px;
}
.pack-branch-health-brief-text {
  font-size: 12px; line-height: 1.5; color: var(--text); white-space: pre-wrap;
}
.pack-branch-health-brief-warning {
  font-size: 11px; color: var(--warn); margin-top: 4px;
}
.pack-branch-health-summary { font-size: 12px; color: var(--muted); }
.pack-branch-health-ok { font-size: 12px; color: var(--ok); margin-top: 4px; }
.pack-branch-health-issues { margin-top: 6px; display: flex; flex-direction: column; gap: 4px; }
.pack-branch-health-issue {
  font-size: 12px; padding: 4px 8px; border-radius: var(--r-sm, 6px);
  background: var(--surface-3); border-left: 3px solid var(--warn);
}
.pack-branch-health-issue.seek_too_close,
.pack-branch-health-issue.seek_past_duration { border-left-color: var(--danger); }
.pack-branch-health-issue-text { color: var(--text); }

/* Slide-in item-editor drawer (lib/drawer.ts) — one implementation, used
 * everywhere a panel opens (Settings, Generate/Regenerate, item editor,
 * Timeline's inspector). Fixing it here fixes every one of those at once.
 *
 * Redesigned off the generic "flat slab pinned to the screen edge, dims
 * everything, plain × button" template — every scaffolded SaaS dashboard
 * ships exactly that. This one floats as a rounded card inset from the
 * viewport edges (not edge-to-edge), and its header carries the same
 * brand-tinted treatment as the generation panel, so it reads as Creai's own
 * chrome rather than a borrowed UI-kit component.
 *
 * Backdrop + panel are opened as DOM siblings under <body>, both start
 * off-transitioned and get `.open` added a frame later so the animation
 * actually plays. */
.ce-drawer-backdrop {
  position: fixed; inset: 0; z-index: var(--z-drawer-backdrop);
  background: oklch(0.1 0.01 158 / 0.6); backdrop-filter: blur(3px);
  opacity: 0; transition: opacity 0.18s ease;
}
.ce-drawer-backdrop.open { opacity: 1; }
.ce-drawer-panel {
  position: fixed; top: var(--sp-3); right: var(--sp-3); bottom: var(--sp-3);
  z-index: var(--z-drawer-panel);
  width: 520px; max-width: calc(92vw - var(--sp-3) * 2);
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-lg);
  overflow-y: auto; box-shadow: 0 24px 70px oklch(0 0 0 / 0.5), var(--shadow);
  transform: translateX(calc(100% + var(--sp-4))); opacity: 0;
  transition: transform 0.22s cubic-bezier(0.2, 0.7, 0.2, 1), opacity 0.18s ease;
}
.ce-drawer-panel.open { transform: translateX(0); opacity: 1; }
@media (prefers-reduced-motion: reduce) {
  .ce-drawer-backdrop, .ce-drawer-panel { transition: none; }
}
.ce-drawer-head {
  position: sticky; top: 0; z-index: 1;
  border-radius: var(--r-lg) var(--r-lg) 0 0;
  background:
    radial-gradient(140% 100% at 0% 0%, color-mix(in oklch, var(--brand-green) 15%, var(--surface)) 0%, var(--surface) 60%),
    color-mix(in srgb, var(--surface) 95%, transparent);
  backdrop-filter: blur(8px);
  padding: var(--sp-4) var(--sp-5); border-bottom: 1px solid var(--border-soft);
  display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3);
}
.ce-drawer-title { font-size: var(--fs-md); font-weight: 700; }
/* min-height:0 keeps this an actual circle — the global button min-height:34px
   otherwise stretches the declared 32x32 into a 32x34 oval. */
.ce-drawer-close {
  background: var(--surface-3); border: 1px solid var(--border-soft); color: var(--muted);
  width: 32px; height: 32px; min-height: 0; border-radius: 999px; font-size: var(--fs-md); padding: 0;
  display: flex; align-items: center; justify-content: center; flex: none;
  transition: background 0.12s, color 0.12s, border-color 0.12s;
}
.ce-drawer-close:hover {
  color: var(--brand-gold-bright); border-color: color-mix(in oklch, var(--brand-gold) 45%, transparent);
  background: color-mix(in oklch, var(--brand-gold) 12%, var(--surface-3));
}
.ce-drawer-body { padding: var(--sp-5); }

/* Generate stepper — Fetching → Downloaded → Processing → Ready. Empty (no
 * children) when nothing is running, so it takes no visual space at rest. */
.gen-stepper { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; margin-top: 8px; }
.gen-step {
  font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.02em;
  padding: 3px 9px; border-radius: 999px; color: var(--muted); background: var(--surface-3);
  border: 1px solid var(--border-soft);
}
.gen-step.done { color: var(--ok); background: var(--ok-bg); border-color: transparent; }
.gen-step.active { color: var(--primary-bright); background: var(--surface-3); border-color: var(--primary-bright); }
.gen-step.error { color: var(--danger); background: var(--danger-bg); border-color: transparent; }
.gen-step.pending { opacity: 0.55; }
.gen-step-sep { color: var(--faint); font-size: 11px; }

/* Per-pack "Saving…/Saved ✓" chip (persist() already tracked this state;
 * previously nothing rendered it). */
/* A fixed slot: "Saving…" → "Saved ✓" → "" must not shift what sits beside it. */
.save-indicator { font-size: 11px; margin-left: 8px; display: inline-block; min-width: 72px; }
.save-indicator.saving { color: var(--muted); }
.save-indicator.saved { color: var(--ok); }

/* ── Review & Edit: three-column shell ──────────────────────────────────────
   Videos | selected pack | prompt editor. Only this page opts in (.page-review);
   every other console page keeps the 980px single column. */
/* The middle column carries the timeline, which is the default view and the
   widest thing on the page — so the rail and the editor are kept to the minimum
   that works (the editor matches the 520px the drawer always used) and the rest
   goes to the timeline. */
.page-review { max-width: 1440px; }
.review-grid { display: grid; grid-template-columns: 260px minmax(420px, 1fr); gap: 16px; margin-top: 18px; align-items: start; }
/* 1540px, not 1400: beside the 104px rail and the page's padding, the three
   columns (260 + 560 + 520 + gaps) only fit from here. At 1400-1539 they
   overflowed and the page slid sideways (B167). review.ts reads the same number. */
@media (min-width: 1540px) {
  .review-grid { grid-template-columns: 260px minmax(560px, 1fr) 520px; }
}
/* Each column scrolls independently, so reading a prompt never moves the video
   list and approving a pack never returns you to the top of the page. */
.rail, .editor-col { position: sticky; top: 12px; max-height: calc(100vh - 24px); display: flex; flex-direction: column; }
.rail {
  background: var(--card); border: 1px solid var(--border-soft);
  border-radius: var(--r-lg); overflow: hidden;
}
.rail-head { padding: 10px; border-bottom: 1px solid var(--border-soft); display: flex; flex-direction: column; gap: 8px; }
.rail-generate { width: 100%; }
/* Typed, not bare: the rail head also holds the state checkboxes now, and a
   bare `.rail-head input` beat the checkbox reset above on later-wins, which
   stretched each box to the full 224px and pushed its label out of the row. */
.rail-head input[type="search"] { width: 100%; }
/* State filter. Three checkboxes, not chips: the states they name partition the
   list (a video is live, or has an update waiting, or has never been published),
   so ticking two has to mean "show me both" — which a single-select chip row
   could not express, and which is why the old chips double-counted every video
   that had a live pack AND a draft. */
.rail-filters { display: flex; flex-direction: column; gap: 5px; }
.rail-filters-summary { font-size: var(--fs-xs); color: var(--muted); }
/* Stacked, not a wrapped row of pills: the rail is 260px and "Update waiting"
   does not share a line with anything, so pills wrapped mid-label into 50px
   ovals. A column also makes the counts line up as a legend you can read down. */
.rail-checks { display: flex; flex-direction: column; gap: 1px; }
.rail-check {
  display: flex; align-items: center; gap: 6px; cursor: pointer; white-space: nowrap;
  font-size: var(--fs-xs); padding: 3px 6px; min-height: 22px; border-radius: var(--r-sm);
  border: 1px solid transparent; color: var(--muted); user-select: none;
}
.rail-check:hover { color: var(--text); background: var(--surface-2); }
/* The swatch ties each box to the flag colour on the rows it filters to. */
.rail-check::before {
  content: ""; width: 6px; height: 6px; border-radius: 2px; flex: none; background: var(--warn);
}
.rail-check.live::before { background: var(--ok); }
.rail-check.draft::before { background: var(--muted); }
.rail-check-n { margin-left: auto; font-variant-numeric: tabular-nums; }
/* Same gold-ring selection language as the rail rows — selection covers the
   control, it never lines one edge. */
.rail-check.on {
  background: color-mix(in oklch, var(--brand-gold) 18%, transparent);
  color: var(--brand-gold-bright);
}

/* Bulk publish / take-down. Hidden until something is ticked, so the rail head
   keeps its height for the (common) single-video case. */
.rail-bulk { display: flex; align-items: center; gap: 5px; flex-wrap: wrap; }
.rail-bulk[hidden] { display: none; }
/* Its own line, so the three buttons share the next one instead of orphaning
   "Clear" below them at this rail width. */
.rail-bulk-count { flex-basis: 100%; font-size: var(--fs-xs); color: var(--muted); }
.rail-list { flex: 1; overflow-y: auto; }
/* The row is a wrapper, not a button: its select checkbox must be a sibling of
   the open-this-video button, never nested inside it. */
.rail-row {
  display: flex; gap: 7px; align-items: flex-start; width: 100%;
  padding: 9px 10px; border-bottom: 1px solid var(--border-soft);
}
.rail-row:hover { background: var(--surface-2); }
.rail-pick { margin-top: 9px; cursor: pointer; }
.rail-open {
  display: flex; gap: 9px; align-items: flex-start; flex: 1; min-width: 0;
  text-align: left; padding: 0; min-height: 0; border: 0; border-radius: 0;
  background: none; color: inherit; cursor: pointer; font: inherit;
}
.rail-open:hover { background: none; }
/* Ticked for a bulk action — distinct from .on, which is "open in the column". */
.rail-row.picked { background: color-mix(in oklch, var(--brand-gold) 7%, transparent); }
/* Selected video: the whole row is covered in a brand-gold tint and ringed,
   rather than marked with a left-edge stripe (the AI-tell highlight Sam flagged). */
.rail-row.on {
  background: color-mix(in oklch, var(--brand-gold) 13%, var(--surface-2));
  box-shadow: inset 0 0 0 1px color-mix(in oklch, var(--brand-gold) 34%, transparent);
}
/* Creator titles are frequently useless for telling videos apart
   ("videoplayback 2", raw ids), so the thumbnail is load-bearing, not decoration. */
.rail-thumb { width: 56px; height: 32px; flex: none; border-radius: 4px; object-fit: cover; background: var(--surface-3); }
.rail-body { flex: 1; min-width: 0; }
.rail-title { font-size: 12px; line-height: 1.35; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.rail-meta { font-size: 11px; color: var(--muted); margin-top: 3px; display: flex; align-items: center; gap: 6px; }
.rail-bar { display: inline-block; width: 34px; height: 4px; border-radius: 999px; background: var(--surface-3); overflow: hidden; }
.rail-bar > span { display: block; height: 100%; background: var(--primary-bright); }
/* Pack composition badges — their own block under .rail-meta, never inside it
   (.rail-meta is a nowrap flex row and wrapping badges inside it broke the
   whole line). Sized off --fs-xs: 11px is the smallest rung of the type scale
   and the one it documents for badges. */
.rail-types { display: flex; flex-wrap: wrap; align-items: center; gap: 3px; margin-top: 4px; }
.rail-types-of {
  font-size: var(--fs-xs); font-weight: 700; letter-spacing: 0.04em;
  text-transform: uppercase; color: var(--faint);
}
.rail-type-badge {
  display: inline-block; font-size: var(--fs-xs); line-height: 1.3;
  padding: 1px 5px; border-radius: 3px; font-weight: 600; white-space: nowrap;
  background: var(--surface-3); color: var(--text);
}
.rail-type-badge.branch { background: color-mix(in oklch, var(--brand-gold) 18%, var(--surface-3)); }
.rail-type-badge.follow { background: color-mix(in oklch, var(--brand-green) 18%, var(--surface-3)); }
.rail-type-badge.quiz { background: color-mix(in oklch, var(--ok) 18%, var(--surface-3)); }
.rail-type-badge.poll { background: color-mix(in oklch, var(--primary-bright) 18%, var(--surface-3)); }
.rail-type-badge.scale { background: color-mix(in oklch, var(--warn) 18%, var(--surface-3)); }
.rail-type-badge.promo { background: color-mix(in oklch, oklch(0.62 0.17 300) 18%, var(--surface-3)); }
/* Distinct from .poll — both resolved to --primary-bright, so two of the seven
   badge types were indistinguishable by colour. */
.rail-type-badge.next { background: color-mix(in oklch, oklch(0.7 0.12 200) 22%, var(--surface-3)); }
/* The row's state, in the same three words the filter checkboxes use. Colour
   carries the urgency: an update waiting on you is the only one that is a
   to-do, so it is the only warm one. */
.rail-flag { display: inline-block; margin-top: 4px; font-size: 10px; padding: 1px 5px; border-radius: 4px; background: var(--warn-bg); color: var(--warn); }
.rail-flag.live { background: var(--ok-bg); color: var(--ok); }
.rail-flag.draft { background: var(--surface-3); color: var(--muted); }

/* ── Pack column ────────────────────────────────────────────────────────── */
.pack-col .pack { margin-top: 0; }
/* The pack's own actions stay reachable while you scroll a long timeline. */
.pack-card-head { position: sticky; top: 0; z-index: 2; background: var(--card); }
/* Is this video live? The state line, above the view switch and visually
   heavier than it, because "what are viewers getting" is the question the
   column has to answer before "which pack am I editing". */
.pack-state-row {
  display: flex; align-items: center; flex-wrap: wrap; gap: var(--sp-2);
  margin-bottom: var(--sp-2);
}
/* One state pill, the same three words and colours the rail's .rail-flag uses
 * (B169) — this used to be a PUBLISHED/PENDING badge on the card below PLUS a
 * "Live to viewers" checkbox up here, in different words from each other and
 * from the rail. */
.video-state-pill {
  display: inline-flex; align-items: center; font-size: var(--fs-sm); font-weight: 600;
  padding: 4px var(--sp-3); min-height: 30px; border-radius: 999px;
  background: var(--warn-bg); color: var(--warn);
}
.video-state-pill.live { background: var(--ok-bg); color: var(--ok); }
.video-state-pill.draft { background: var(--surface-3); color: var(--muted); }

/* Draft | Live — which of a video's two packs the column is showing. Always
   rendered (missing slots render disabled) so the column keeps its geometry
   between one-pack and two-pack videos and across an approve. The trailing
   note is what actually answers "is this what viewers see?". */
/* Same gold-ring selection language as the rail rows and the filter chips —
   selection is shown by covering the control, never by lining one edge. */

/* Publish / reject, separated from the utility actions below them. These two
   are the only buttons on the card that change what viewers are served, so
   they get their own row, and the destructive one drops to a ghost weight and
   sits a gap away instead of flush against the primary. */
.pack-decide { align-items: center; }
.pack-decide button.reject {
  margin-left: var(--sp-3); background: none; font-weight: 500;
  color: var(--muted); border-color: var(--border-soft);
}
.pack-decide button.reject:hover {
  color: var(--danger); background: var(--danger-bg);
  border-color: color-mix(in oklch, var(--danger) 45%, transparent);
}
/* ── The card's ⋯ menu (B172) ───────────────────────────────────────────────
 * Replaces a flat row of six buttons under the video header. A <details>, so
 * the open state, the keyboard and focus are the platform's rather than a
 * popover implementation's. */
.pack-title-row { display: flex; align-items: center; flex-wrap: wrap; gap: var(--sp-2); }
/* Visible header buttons (B174) sit left of the ⋯, pushed to the right edge
 * together; wrapping lets them drop under the title on a narrow card. */
.pack-head-actions { display: flex; flex-wrap: wrap; gap: 6px; margin-left: auto; }
.pack-title-row .pack-menu { flex: none; }
.pack-menu { position: relative; }
.pack-menu-trigger {
  display: inline-flex; align-items: center; justify-content: center;
  width: 30px; height: 26px; border-radius: var(--r-sm);
  color: var(--muted); font-size: 15px; line-height: 1; cursor: pointer;
  border: 1px solid var(--border-soft); background: var(--surface-2);
  list-style: none; user-select: none;
}
.pack-menu-trigger::-webkit-details-marker { display: none; }
.pack-menu-trigger::marker { content: ""; }
.pack-menu-trigger:hover,
.pack-menu[open] .pack-menu-trigger { color: var(--text); border-color: var(--border); }
.pack-menu-pop {
  position: absolute; top: calc(100% + 4px); right: 0; z-index: var(--z-menu);
  min-width: 210px; padding: 4px;
  display: flex; flex-direction: column; gap: 1px;
  background: var(--card); border: 1px solid var(--border);
  border-radius: var(--r-md); box-shadow: 0 10px 28px rgb(0 0 0 / 0.38);
}
.pack-menu-item {
  text-align: left; padding: 7px 10px; border-radius: var(--r-sm);
  font-size: 12.5px; color: var(--text); white-space: nowrap;
  background: none; border: 0; cursor: pointer;
}
.pack-menu-item:hover { background: var(--surface-2); }
.pack-menu-item.danger { color: var(--danger); }
.pack-menu-item.danger:hover { background: var(--danger-bg); }

/* ── Editor column ──────────────────────────────────────────────────────── */
.editor-col {
  background: var(--card); border: 1px solid var(--border-soft);
  border-radius: var(--r-lg); overflow-y: auto;
}
.editor-col:empty::after {
  content: "Pick a prompt to edit it here.";
  display: block; padding: 24px 20px; color: var(--muted); font-size: 13px;
}
@media (max-width: 1539px) { .editor-col { display: none; } }

/* ── Fullscreen timeline (B170) ──────────────────────────────────────────── */
.pack-overview { position: relative; }
/* The timeline's own action row, top-right of the axis it acts on: full
 * screen, and (published videos) the hotspots whose panel opens right below
 * it (B172). Absolutely positioned as one row rather than per button, so
 * adding a second one didn't need a second set of coordinates. */
.pack-tl-actions { position: absolute; top: 6px; right: 6px; z-index: 2; display: flex; gap: 4px; }
.fullscreen-timeline { display: flex; flex-direction: column; height: 100%; min-height: 0; }
.fullscreen-timeline-body {
  display: flex; gap: var(--sp-4); align-items: stretch;
  padding: var(--sp-4); min-height: 0;
}
.fullscreen-timeline-pane { flex: 1 1 auto; min-width: 0; overflow: auto; }
.fullscreen-timeline-editor {
  flex: 0 0 420px; min-width: 0; overflow-y: auto;
  background: var(--card); border: 1px solid var(--border-soft); border-radius: var(--r-lg);
}
.fullscreen-timeline-editor:empty::after {
  content: "Select a prompt on the timeline to edit it here.";
  display: block; padding: 24px 20px; color: var(--muted); font-size: 13px;
}
@media (max-width: 1000px) {
  .fullscreen-timeline-body { flex-direction: column; }
  .fullscreen-timeline-editor { flex-basis: auto; }
}

.ce-stale-bundle {
  position: sticky;
  top: 0;
  z-index: 60;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 16px;
  font-size: 13px;
  color: var(--warn);
  background: var(--warn-bg);
  border-bottom: 1px solid var(--border);
}
.ce-stale-reload {
  padding: 3px 12px;
  font: inherit;
  font-weight: 600;
  color: inherit;
  background: none;
  border: 1px solid currentColor;
  border-radius: 6px;
  cursor: pointer;
}
.ce-toast-action {
  margin-left: 10px;
  padding: 2px 8px;
  font: inherit;
  font-weight: 600;
  color: inherit;
  background: rgba(255, 255, 255, 0.14);
  border: 1px solid currentColor;
  border-radius: 6px;
  cursor: pointer;
}
.ce-toast-action:hover { background: rgba(255, 255, 255, 0.24); }
/* ── Generation tabs, genuinely browser-tab-sized (B171, B172) ──────────────
 * A tab carries a live-dot, a date and the model that made it — B170's pill
 * (model, prompt count, a readiness bar, live text, all on one line) was
 * "too much space" per Sam even compact. Everything else lives in
 * .version-preview-bar, ONE toolbar for whichever tab is focused, the way a
 * browser's own toolbar acts on the front tab rather than repeating itself on
 * every one. The model went back on the tab in B172 ("generated packs on tab
 * view dont show which llm generated") but STACKED under the date, not beside
 * it: two short lines are ~115px wide where one long line is ~215px, so six
 * tabs still fit a single row. */
.version-history { margin-bottom: var(--sp-3); }
.version-history.empty { min-height: 28px; }
.version-tabs { display: flex; flex-wrap: wrap; align-items: center; gap: 3px; }
.version-tab {
  flex: 0 0 auto; display: inline-flex; align-items: center; gap: 5px;
  padding: 3px 8px; font-size: 10.5px; color: var(--muted);
  background: none; border: 1px solid transparent; border-radius: var(--r-sm);
  cursor: pointer; transition: border-color 0.15s, background 0.15s, color 0.15s;
}
.version-tab:hover { background: var(--surface-2); color: var(--text); }
.version-tab.on { background: var(--surface-2); border-color: var(--border-soft); color: var(--text); }
.version-tab-dot { width: 6px; height: 6px; border-radius: 50%; flex: none; background: var(--faint); }
.version-tab.live .version-tab-dot { background: var(--ok); box-shadow: 0 0 0 2px var(--ok-bg); }
.version-tab-when { white-space: nowrap; }
.version-tab-lines { display: flex; flex-direction: column; align-items: flex-start; gap: 1px; line-height: 1.2; }
.version-tab-top { display: inline-flex; align-items: center; gap: 4px; }
.version-tab-model { font-size: 9px; color: var(--faint); white-space: nowrap; }
.version-tab:hover .version-tab-model,
.version-tab.on .version-tab-model { color: var(--muted); }
.version-tab .version-new-tag { margin-left: 1px; }
.version-tab.version-more { color: var(--faint); }
.version-tab.version-more:hover { color: var(--text); }
/* The "+" of the tab bar — the one control in the strip that makes a new tab
 * instead of switching to one, so it reads as an action, not a generation. */
.version-tab.version-new-gen {
  color: var(--primary-bright); border-style: dashed; border-color: var(--border-soft);
}
.version-tab.version-new-gen:hover {
  background: color-mix(in oklch, var(--primary-bright) 12%, transparent);
  border-color: var(--primary-bright); color: var(--primary-bright);
}
.version-new-tag {
  font-size: 8.5px; font-weight: 700; letter-spacing: 0.03em; color: var(--primary-bright);
  background: color-mix(in oklch, var(--primary-bright) 16%, transparent); border-radius: 4px; padding: 1px 4px;
}

/* ── The focused tab's toolbar ────────────────────────────────────────────
 * Empty (and collapsed via :empty) on the normal live/pending view — it only
 * ever appears while previewing a generation other than the live one. */
.version-preview-bar:empty { display: none; }
.version-preview-toolbar {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
  margin: 6px 0; padding: 8px 10px;
  background: var(--warn-bg); border: 1px solid color-mix(in oklch, var(--warn) 35%, transparent);
  border-radius: var(--r-md);
}
.version-preview-label { font-size: 12px; font-weight: 600; color: var(--warn); margin-right: auto; }
/* Full text again (not B170's icon-only pill) — it has the whole toolbar to
 * itself now, so there's room to say what it does. */
.version-live {
  display: inline-flex; align-items: center; gap: 6px; padding: 4px 10px;
  font-size: 12px; font-weight: 600; color: var(--muted);
  border: 1px solid var(--border); border-radius: 999px; cursor: pointer; white-space: nowrap;
}
.version-live:hover { color: var(--text); background: var(--surface-2); }
.version-live.on { background: var(--ok-bg); color: var(--ok); border-color: transparent; cursor: default; }
.version-live.busy { opacity: 0.55; pointer-events: none; }
.version-live input { width: 12px; height: 12px; margin: 0; }
/* The read-only preview itself dims very slightly and loses the pointer
 * spotlight/hover-expand — a visual "look, don't touch" that needs no text. */
.pack-overview.previewing { opacity: 0.92; }
.pack-overview.previewing .pack-tl-card { cursor: default; }
.pack-overview.previewing .pack-tl-mark { cursor: default; }

/* The prompt list's own header, carrying the action that acts on the list
 * (B172) — "Select items…" used to sit in the card header, six buttons away
 * from the rows it puts checkboxes on. */
.item-list-head {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  margin: var(--sp-3) 0 6px;
}
.item-list-title {
  font-size: 11px; font-weight: 700; letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--muted);
}

/* ── The stage: everything one generation tab switches (B172) ───────────────
 * Sam: "everything after video classification should behave as one
 * component." So it is one element — tabs, toolbar, tally, readiness,
 * timeline, prompt list and the prompt detail all read the focused
 * generation, and the whole block carries the previewing state together
 * instead of eight widgets each deciding for themselves. */
.pack-stage.previewing .item-list { opacity: 0.92; }
/* A read-only row still opens (into the read-only detail), so it keeps the
 * pointer — but nothing on it suggests it can be changed. */
.preview-readonly-note {
  font-size: 12px; color: var(--warn); line-height: 1.45;
  background: var(--warn-bg); border: 1px solid color-mix(in oklch, var(--warn) 30%, transparent);
  border-radius: var(--r-sm); padding: 6px 9px; margin-bottom: 10px;
}
/* A disabled <fieldset> is how the read-only detail switches off every control
 * the item editor builds — one attribute instead of a second, drifting copy of
 * every field. It brings default chrome and a min-content min-width with it,
 * which would squeeze the editor, so reset both. */
.item-readonly { border: 0; margin: 0; padding: 0; min-width: 0; }
/* The editor embeds a mock of the viewer's own card; its buttons are part of
 * that picture, not controls, so the global disabled dimming (button:disabled
 * above) makes the preview look broken rather than read-only. */
.item-readonly .card-preview button:disabled { opacity: 1; cursor: default; }

.version-diff {
  margin-top: 4px; padding: 10px 12px;
  background: var(--surface-2); border: 1px solid var(--border-soft); border-radius: var(--r-md);
}
.version-diff-legend { font-size: 10px; color: var(--muted); margin-top: 8px; }
.version-compare { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 10px; }
.version-compare h4 { font-size: 11px; color: var(--muted); font-weight: 600; margin: 0 0 6px; }
.version-diff-add { color: var(--ok); }
.version-diff-changed { color: var(--warn); }
.version-diff-removed { color: var(--muted); text-decoration: line-through; }

/* Marks the pack card a Regenerate is running against — the stepper/status
 * area is a page-level singleton but Regenerate is per-card, so this plus the
 * status-line prefix (see setGenStatus) disambiguates which card is running. */
.pack.generating { border-color: var(--primary); box-shadow: 0 0 0 1px var(--primary), var(--shadow); }

/* Settings-panel rows — replaces repeated inline flex/gap style attributes. */
.key-row { display: flex; gap: 8px; margin-top: 4px; }
.key-row input { flex: 1; }
.key-row-status { display: flex; gap: 8px; align-items: center; }
.key-row-status > :first-child { flex: 1; }
.key-section { margin-top: 12px; border-top: 1px solid var(--border); padding-top: 10px; }

/* Draft preview list — partial results streamed in while generation runs. */
.gen-preview { margin-top: 8px; }
.gen-preview:empty { display: none; }
.gen-preview-head { font-size: 11px; text-transform: uppercase; letter-spacing: .04em; color: var(--muted); margin-bottom: 4px; }
.gen-preview-row {
  display: flex; align-items: baseline; gap: 8px; padding: 4px 8px;
  border-left: 2px solid var(--border); font-size: 12px; margin: 2px 0;
}
.gen-preview-row.new { animation: gen-preview-in 0.35s ease-out; }
@keyframes gen-preview-in { from { opacity: 0; transform: translateY(3px); } to { opacity: 1; transform: none; } }
.gen-preview-at { color: var(--primary-bright); font-variant-numeric: tabular-nums; flex: none; }
.gen-preview-kind { color: var(--muted); flex: none; font-size: 10px; text-transform: uppercase; }
.gen-preview-body { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* The transcript line a prompt was grounded in — read-only evidence, styled as
   a quotation rather than an input so it never reads as editable. */
.evidence-quote {
  font-style: italic; color: var(--muted); font-size: 12px; line-height: 1.5;
  border-left: 2px solid var(--border); padding: 4px 10px;
}

/* Generation notes — what degraded the pack without failing it. Advisory, not
   an error: the pack landed and is usable, these explain why it looks thinner
   or differently spread than the creator asked for. */
.gen-warnings { margin-top: 8px; }
.gen-warnings:empty { display: none; }
.gen-warn-head { font-size: 11px; text-transform: uppercase; letter-spacing: .04em; color: var(--muted); margin-bottom: 4px; }
.gen-warn-row {
  padding: 4px 8px; border-left: 2px solid var(--warn, var(--muted));
  font-size: 12px; margin: 2px 0; color: var(--muted);
}

/* Channel pillars editor — what the channel is about, and the dimensions within
   it. Two fields per row: the name a viewer sees, and an optional gloss. */
.pillar-domain-row { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; }
.pillar-domain-label { flex: none; font-size: 12px; color: var(--muted); }
.pillar-domain-row input { flex: 1; min-width: 0; }
.pillar-row { display: flex; gap: 8px; margin: 4px 0; }
.pillar-row .pillar-icon { flex: 0 0 3.5em; min-width: 0; text-align: center; }
.pillar-row .pillar-label { flex: 0 0 30%; min-width: 0; }
.pillar-row .pillar-desc { flex: 1; min-width: 0; }
@media (max-width: 560px) {
  .pillar-row { flex-direction: column; gap: 4px; }
  .pillar-row .pillar-icon, .pillar-row .pillar-label { flex: none; }
  .pillar-row .pillar-icon { text-align: left; }
}

/* Pillars: existing videos still on the old taxonomy. Advisory, and the reason
   a viewer's ring would otherwise read as empty after a pillar change. */
.pillars-stale { margin-top: 10px; }
.pillars-stale:empty { display: none; }
.pillars-stale-note {
  font-size: 12px; color: var(--muted); margin-bottom: 6px;
  border-left: 2px solid var(--warn, var(--muted)); padding: 4px 8px;
}

/* ── Preview player ──────────────────────────────────────────────────────── */
/* The video with this pack's prompts firing over it, directly above the
   timeline that schedules them. Collapsible: 16:9 costs ~450px of column. */
.preview { margin: 0 0 14px; }
.preview-head { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-bottom: 6px; }
.preview-toggle {
  background: none; border: 0; padding: 2px 0; font: inherit; font-size: 13px;
  font-weight: 600; color: var(--text); cursor: pointer;
}
.preview-toggle:hover { color: var(--primary-bright); }
.preview-note { font-size: 12px; color: var(--muted); }
.preview-note.warn { color: var(--warn); font-weight: 600; }
/* Positioned: #ce-root (overlay.css) fills it absolutely, iframe sits behind. */
.preview-stage {
  position: relative; aspect-ratio: 16 / 9; width: 100%;
  background: #000; border-radius: var(--r-lg); overflow: hidden;
}
.preview-stage #player,
.preview-stage iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
.preview-unavailable {
  position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
  padding: 24px; text-align: center; font-size: 13px; line-height: 1.5; color: var(--muted);
}

/* Playhead over the pack timeline — drag to scrub the preview. Plain clicks on
   the axis keep their existing meanings, so this needs its own grab target.
   Hidden until the player reports a duration: pinned at the left edge of an
   idle timeline it reads as an unexplained red stripe, not a playhead. */
.tl-playhead {
  position: absolute; top: 0; bottom: 0; width: 1px; margin-left: -0.5px;
  background: var(--brand-gold-bright); opacity: 0.8;
  z-index: 6; cursor: ew-resize; touch-action: none;
}
.tl-playhead:hover { opacity: 1; }
/* The time rides on the line, so the stripe reads as "you are here". */
.tl-playhead-time {
  position: absolute; top: -9px; left: 50%; transform: translateX(-50%);
  padding: 1px 5px; border-radius: 999px;
  background: var(--brand-gold-bright); color: oklch(0.2 0.03 90);
  font-family: var(--font-mono); font-variant-numeric: tabular-nums;
  font-size: 10px; font-weight: 700; line-height: 1.5; white-space: nowrap;
  pointer-events: none;
}
/* Widen the grab area without widening the line. */
.tl-playhead::after { content: ""; position: absolute; inset: 0 -7px; }

/* ── Jump bar ────────────────────────────────────────────────────────────── */
/* One chip per prompt, in time order: click to send the preview there. */
.jump-bar { display: flex; align-items: center; flex-wrap: wrap; gap: 5px; margin: 0 0 14px; }
.jump-bar:empty { display: none; }
.jump-bar-label {
  font-size: 10px; text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--muted); margin-right: 3px;
}
.jump-chip {
  display: inline-flex; align-items: center; gap: 3px;
  padding: 3px 8px; border-radius: 999px;
  border: 1px solid var(--border); background: var(--surface-2); color: var(--muted);
  font: inherit; font-size: 11px; cursor: pointer;
  transition: background 0.12s, color 0.12s, border-color 0.12s;
}
.jump-chip:hover { color: var(--text); border-color: var(--brand-gold); background: var(--surface-3); }
.jump-chip-time { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }
.jump-chip-mark { font-size: 10px; color: var(--brand-gold); }
/* The prompt currently on screen in the preview — lit in brand gold. */
.jump-chip.on {
  background: color-mix(in oklch, var(--brand-gold) 22%, var(--surface-2));
  border-color: var(--brand-gold); color: var(--brand-gold-bright); font-weight: 600;
}
.jump-chip.on .jump-chip-mark { color: var(--brand-gold-bright); }
.jump-chip.jt-action { border-style: dashed; }

/* ── Pack type tags ──────────────────────────────────────────────────────── */
/* What kind of generation this pack is, read off the items themselves. */
.pack-tags { display: inline-flex; flex-wrap: wrap; gap: 4px; margin-top: 6px; }
.pack-tag {
  font-size: 11px; font-weight: 700; letter-spacing: 0.02em;
  padding: 3px 8px; border-radius: 4px; white-space: nowrap;
  background: var(--surface-3); color: var(--text);
  border: 1px solid var(--border-soft);
}
.pack-tag.branching {
  background: color-mix(in oklch, var(--brand-gold) 16%, transparent);
  color: var(--brand-gold-bright);
  box-shadow: inset 0 0 0 1px color-mix(in oklch, var(--brand-gold) 35%, transparent);
}
.pack-tag.branching-empty { background: var(--warn-bg); color: var(--warn); }
.pack-tag.followups { background: oklch(0.7 0.16 252 / 0.18); color: var(--primary-bright); }
.pack-tag.quiz { background: var(--ok-bg); color: var(--ok); }
.pack-tag.promo { background: oklch(0.62 0.17 300 / 0.18); color: oklch(0.78 0.14 300); }
.pack-tag.intro { background: var(--surface-3); color: var(--muted); }

/* Per-card generation progress — the Creai engine at work.
   The stepper/status/preview/warnings elements live in the parked Generate
   form, hidden until opened as a drawer, so a per-card Regenerate rendered its
   stages into a hidden subtree and looked like it had done nothing. These hosts
   stand in for the duration of the run.
   Styled as a Creai moment rather than a generic accent-outlined box: a dark
   panel carrying the brand mark, with a green→gold filament that sweeps the top
   edge only while work is live. */
.card-gen-progress {
  position: relative; overflow: hidden;
  margin: 2px 0 16px; padding: 13px 15px 15px;
  border-radius: var(--r-lg);
  border: 1px solid color-mix(in oklch, var(--brand-green) 42%, var(--border));
  background:
    radial-gradient(120% 100% at 0% 0%, color-mix(in oklch, var(--brand-green) 16%, var(--field)) 0%, var(--field) 55%);
  box-shadow: inset 0 1px 0 oklch(1 0 0 / 0.03), var(--shadow);
}
/* Live-work filament. Present only while `.running`, so a finished or errored
   panel is calm — the animation IS the "it's happening" signal. */
.card-gen-progress::before {
  content: ""; position: absolute; inset: 0 0 auto 0; height: 2px;
  background: linear-gradient(90deg, transparent, var(--brand-green-bright) 35%, var(--brand-gold) 55%, transparent);
  background-size: 220% 100%; opacity: 0;
}
.card-gen-progress.running::before { opacity: 1; animation: gen-sweep 1.5s linear infinite; }
@keyframes gen-sweep { from { background-position: 130% 0; } to { background-position: -130% 0; } }
@media (prefers-reduced-motion: reduce) {
  .card-gen-progress.running::before { animation: none; opacity: 0.9; }
}
/* Brand mark: a gold ◆ and the Creai wordmark, so a run reads as "Creai is
   generating this," not as an anonymous progress bar. */
.gen-brand { display: flex; align-items: baseline; gap: 8px; margin-bottom: 11px; }
.gen-brand-mark {
  font-weight: 800; font-size: 13px; letter-spacing: -0.02em;
  color: var(--brand-gold-bright);
}
.gen-brand-mark::before {
  content: "◆"; margin-right: 6px; font-size: 9px; vertical-align: 2px;
  color: var(--brand-gold);
}
.gen-brand-tag {
  font-size: 10px; text-transform: uppercase; letter-spacing: 0.08em; color: var(--muted);
}
.card-gen-progress.running .gen-brand-tag { color: var(--brand-gold); }
/* The active stage wears brand gold instead of the default blue. */
.card-gen-progress .gen-step.active {
  color: var(--brand-gold-bright); background: color-mix(in oklch, var(--brand-gold) 14%, transparent);
  border-color: color-mix(in oklch, var(--brand-gold) 55%, transparent);
}
.card-gen-progress .gen-stepper:empty,
.card-gen-progress .gen-preview:empty,
.card-gen-progress .gen-warnings:empty { display: none; }

/* ── Surface polish ─────────────────────────────────────────────────────────
   Glass, depth and hover response for the shared components. Kept together at
   the end so it layers over the structural rules above without editing them. */
.rail {
  background: var(--glass), var(--surface); backdrop-filter: blur(18px) saturate(1.3);
  box-shadow: var(--shadow-lg), var(--glass-hi);
}
.rail-row { transition: background 0.2s; }
.rail-thumb { border-radius: 6px; box-shadow: 0 2px 8px oklch(0 0 0 / 0.4); }
.rail-row.on {
  background: linear-gradient(90deg, oklch(0.82 0.12 86 / 0.14), oklch(0.76 0.14 160 / 0.08));
  box-shadow: inset 0 0 0 1px oklch(0.82 0.12 86 / 0.34), 0 0 24px oklch(0.82 0.12 86 / 0.08);
}
.editor-col > *, .pack-col .pack {
  border-radius: var(--r-lg);
}
.ce-drawer-backdrop { background: oklch(0.08 0.02 168 / 0.55); backdrop-filter: blur(6px) saturate(1.2); transition: opacity 0.3s ease; }
.ce-drawer-panel {
  background: var(--glass), oklch(0.18 0.016 166 / 0.9); backdrop-filter: blur(24px) saturate(1.4);
  box-shadow: 0 30px 80px oklch(0.03 0.02 168 / 0.6), var(--glass-hi);
  transform: translateX(calc(100% + var(--sp-4))) scale(0.98);
  transition: transform 0.5s var(--ease-out), opacity 0.3s ease;
}
.ce-drawer-panel.open { transform: none; }
/* Fills the viewport (minus a small margin) instead of the intrinsic-width
 * side panel — for the timeline's fullscreen view (B170), which wants real
 * room, not a form. Same open/close animation as the side variant. */
.ce-drawer-panel.fullscreen {
  left: var(--sp-3); right: var(--sp-3); width: auto; max-width: none;
  display: flex; flex-direction: column;
}
.ce-drawer-panel.fullscreen .ce-drawer-body { flex: 1 1 auto; overflow-y: auto; }
.ce-drawer-head {
  background:
    radial-gradient(120% 140% at 0% 0%, oklch(0.56 0.12 160 / 0.22) 0%, transparent 55%),
    radial-gradient(80% 120% at 100% 0%, oklch(0.82 0.12 86 / 0.1) 0%, transparent 60%),
    oklch(0.18 0.016 166 / 0.85);
}
.item-summary {
  background: var(--glass), var(--surface-2); border-color: var(--border-soft);
  transition: border-color 0.2s, background 0.2s;
}
.item-summary:hover { border-color: oklch(0.76 0.14 160 / 0.45); background: var(--glass), var(--surface-3); }
.cta-card, .reco, .pack-tl-card {
  background: var(--glass), var(--surface-2); box-shadow: var(--shadow);
  transition: border-color 0.2s, background 0.2s;
}
.cta-card:hover, .reco:hover { border-color: var(--border); }
.pack-tl-card { background: var(--glass), oklch(0.22 0.018 166); border-radius: var(--r-sm); }
.pack-tl-card:hover, .pack-tl-card.sel { box-shadow: var(--shadow-lg), 0 0 0 1px oklch(0.76 0.14 160 / 0.3); z-index: 4; }
.gen-step.active { position: relative; overflow: hidden; box-shadow: 0 0 16px oklch(0.84 0.13 162 / 0.25); }
.gen-step.active::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(100deg, transparent 20%, oklch(1 0 0 / 0.25) 50%, transparent 80%);
  animation: ce-shimmer 1.6s ease-in-out infinite;
}
@keyframes ce-shimmer { from { transform: translateX(-100%); } to { transform: translateX(100%); } }
.pack.generating { animation: ce-breathe 2.4s ease-in-out infinite; }
@keyframes ce-breathe { 50% { box-shadow: 0 0 0 1px var(--primary), 0 0 36px oklch(0.76 0.14 160 / 0.35); } }
.pack-readiness-donut { box-shadow: 0 0 22px color-mix(in oklch, var(--ring-color, var(--primary-bright)) 30%, transparent); transition: --pct 1s var(--ease-out); }
.pack-readiness-donut-hole { background: var(--bg-solid); }
.preview-stage { box-shadow: var(--shadow-lg), 0 0 0 1px var(--border-soft); }
.ce-toast-action { background: oklch(1 0 0 / 0.1); }
.ce-toast-action:hover { background: oklch(1 0 0 / 0.2); }
table { border-collapse: collapse; }
@property --pct { syntax: "<number>"; inherits: true; initial-value: 0; }

/* Stat tiles (Analytics summary, Impact report) — the numbers are the hero.
   Two-class selectors so they layer over each page's own .card rule. */
.cards > .card {
  position: relative; overflow: hidden; isolation: isolate;
  background: var(--glass), oklch(0.2 0.018 166 / 0.7);
  border: 1px solid var(--border-soft); border-radius: var(--r-lg);
  padding: 18px 20px 16px; min-width: 140px; flex: 1; text-align: left;
  box-shadow: var(--shadow); backdrop-filter: blur(16px) saturate(1.3);
  transition: border-color 0.2s;
  animation: ce-rise 0.4s ease both;
}
.cards > .card::after {
  content: ""; position: absolute; left: 16px; right: 16px; top: 0; height: 1px;
  background: linear-gradient(90deg, transparent, oklch(0.82 0.12 86 / 0.55), transparent);
}
.cards > .card:hover { border-color: var(--border); }
.cards > .card b {
  display: block; font-size: clamp(26px, 2.6vw, 34px); font-weight: 750; letter-spacing: -0.03em; line-height: 1.1;
  font-variant-numeric: tabular-nums; color: var(--text);
}
.cards > .card span { display: block; margin-top: 4px; font-size: var(--fs-sm); color: var(--muted); }
.cards > .card .delta, .cards > .card .qual { display: inline-block; }
@media print {
  body::before, body::after { display: none; }
  .cards > .card { backdrop-filter: none; box-shadow: none; animation: none; }
  h1 { animation: none; }
}
.ce-brand { grid-column: 1 / -1; justify-self: start; }

/* ════════════════════════════════════════════════════════════════════════════
   Shell v2 — the console as an app, not a page
   ════════════════════════════════════════════════════════════════════════════ */

/* Each section has its own colour, so where you are is legible at a glance.
   ambient.js sets <html data-page>; the WebGL field reads --amb-*. */
:root {
  --page-accent: var(--primary-bright);
  --amb-a: oklch(0.46 0.11 162);
  --amb-b: oklch(0.6 0.11 86);
  --amb-c: oklch(0.42 0.08 205);
  --ce-count-fill: var(--text);
}
:root[data-page="impact"]    { --page-accent: oklch(0.87 0.12 88);  --amb-a: oklch(0.58 0.11 82);  --amb-b: oklch(0.45 0.1 160);  --amb-c: oklch(0.42 0.1 35); }
:root[data-page="analytics"] { --page-accent: oklch(0.84 0.1 200);  --amb-a: oklch(0.46 0.09 212); --amb-b: oklch(0.46 0.11 162); --amb-c: oklch(0.42 0.1 290); }
:root[data-page="timeline"]  { --page-accent: oklch(0.82 0.11 300); --amb-a: oklch(0.44 0.11 295); --amb-b: oklch(0.46 0.09 205); --amb-c: oklch(0.45 0.1 162); }
:root[data-page="live"]      { --page-accent: oklch(0.8 0.14 32);   --amb-a: oklch(0.5 0.14 30);   --amb-b: oklch(0.58 0.11 80);  --amb-c: oklch(0.4 0.1 300); }

/* With the WebGL field running (the sign-in scene), the still tint steps aside. */
.ce-gl body::before { display: none; }

h1 { font-size: clamp(28px, 3vw, 38px); letter-spacing: -0.04em; margin-bottom: 20px; }
h2::before { background: linear-gradient(135deg, var(--page-accent), var(--brand-gold-bright)); box-shadow: 0 0 12px var(--page-accent); }

/* ── Sidebar rail (desktop) ─────────────────────────────────────────────── */
:is(.page, .layout) > nav a::before {
  content: ""; width: 22px; height: 22px; flex: none;
  background: currentColor;
  -webkit-mask: var(--ico) center / contain no-repeat; mask: var(--ico) center / contain no-repeat;
}
nav a[href="impact.html"]    { --ico: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 3.2l2.5 5.1 5.6.8-4.05 3.95.96 5.58L12 16l-5.01 2.63.96-5.58L3.9 9.1l5.6-.8z'/%3E%3C/svg%3E"); }
nav a[href="analytics.html"] { --ico: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 20h16'/%3E%3Cpath d='M7 16v-5'/%3E%3Cpath d='M12 16V6'/%3E%3Cpath d='M17 16v-8'/%3E%3C/svg%3E"); }
nav a[href="review.html"]    { --ico: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3.8' y='3.8' width='16.4' height='16.4' rx='4.5'/%3E%3Cpath d='M8.3 12.4l2.6 2.6 5-5.6'/%3E%3C/svg%3E"); }
nav a[href="live.html"]      { --ico: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='2.2'/%3E%3Cpath d='M8 8a5.6 5.6 0 0 0 0 8M16 8a5.6 5.6 0 0 1 0 8M5.2 5.2a9.6 9.6 0 0 0 0 13.6M18.8 5.2a9.6 9.6 0 0 1 0 13.6'/%3E%3C/svg%3E"); }
:is(.page, .layout) > nav a[aria-current="page"] {
  color: var(--text);
  background: linear-gradient(160deg, color-mix(in oklch, var(--page-accent) 30%, transparent), color-mix(in oklch, var(--page-accent) 10%, transparent));
  border-color: color-mix(in oklch, var(--page-accent) 45%, transparent);
  box-shadow: 0 0 28px color-mix(in oklch, var(--page-accent) 28%, transparent), inset 0 1px 0 oklch(1 0 0 / 0.14);
}
:is(.page, .layout) > nav a[aria-current="page"]::before {
  background: linear-gradient(135deg, var(--text), var(--page-accent));
  filter: drop-shadow(0 0 6px var(--page-accent));
}
/* The live tab carries a still red dot. */
nav a[href="live.html"] { position: relative; }
nav a[href="live.html"]::after {
  content: ""; position: absolute; width: 7px; height: 7px; border-radius: 50%;
  background: oklch(0.72 0.17 30);
}

@media (min-width: 900px) {
  body:has(> :is(.page, .layout) > nav) { padding-left: 104px; }
  :is(.page, .layout) > nav {
    position: fixed; z-index: 30; left: 14px; top: 14px; bottom: 14px; width: 78px; max-width: none; margin: 0;
    flex-direction: column; flex-wrap: nowrap; align-items: stretch; gap: 4px;
    padding: 92px 7px 12px; border-radius: 28px; overflow-y: auto; scrollbar-width: none;
    background: linear-gradient(180deg, oklch(1 0 0 / 0.07), oklch(1 0 0 / 0.02)), oklch(0.14 0.014 168 / 0.62);
    box-shadow: 0 30px 60px oklch(0.03 0.02 168 / 0.5), var(--glass-hi);
  }
  :is(.page, .layout) > nav a {
    display: flex; flex-direction: column; align-items: center; gap: 6px;
    padding: 12px 2px 10px; border-radius: 20px; font-size: 10.5px; font-weight: 600; line-height: 1.15; text-align: center;
  }
  nav a[href="live.html"]::after { top: 9px; right: 17px; }
  :is(.page, .layout) > nav button { margin: auto 0 0 !important; min-height: 32px; font-size: 10.5px; padding: 6px 2px; border-radius: 14px; }
  :is(.page, .layout) > .ce-brand:has(~ nav) {
    position: fixed; z-index: 31; left: 14px; top: 14px; width: 78px; height: 84px; margin: 0;
    flex-direction: column; justify-content: center; gap: 3px; font-size: 12px;
  }
  .ce-brand:has(~ nav) .ce-mark { width: 38px; height: 38px; filter: drop-shadow(0 0 14px color-mix(in oklch, var(--page-accent) 55%, transparent)); }
  .page { padding-top: 38px; }
  .layout { padding-top: 38px; }
}

/* ── Bottom tab bar (phone) ─────────────────────────────────────────────── */
@media (max-width: 899px) {
  body:has(> :is(.page, .layout) > nav) { padding-bottom: 100px; }
  :is(.page, .layout) > nav {
    position: fixed; z-index: 30; left: 10px; right: 10px; bottom: 10px; width: auto; margin: 0;
    flex-wrap: nowrap; justify-content: space-between; gap: 2px; padding: 6px; border-radius: 24px;
    background: linear-gradient(180deg, oklch(1 0 0 / 0.08), oklch(1 0 0 / 0.02)), oklch(0.14 0.014 168 / 0.8);
    box-shadow: 0 -10px 40px oklch(0.03 0.02 168 / 0.6), var(--glass-hi);
  }
  :is(.page, .layout) > nav a { flex: 1 1 0; min-width: 0; display: flex; flex-direction: column; align-items: center; gap: 3px; padding: 8px 2px 6px; font-size: 9.5px; line-height: 1.1; text-align: center; border-radius: 18px; }
  nav a[href="live.html"]::after { top: 5px; right: calc(50% - 16px); }
  :is(.page, .layout) > nav button { display: none; }
  .ce-toast-container { bottom: 96px; }
}

/* ── Pointer spotlight on glass ─────────────────────────────────────────── */
:is(.cards > .card, .panel, .rail, .controls, .cta-card, .reco, .item-summary, .pack, .top-vid, .alert, .act, .insight, .gap-cat, .quality-item, .mix, .issue) {
  background-image:
    radial-gradient(380px circle at var(--mx, -999px) var(--my, -999px), color-mix(in oklch, var(--page-accent) 13%, transparent), transparent 55%),
    var(--glass);
}

/* ── Loading and empty placeholders (tagged by ambient.js) ──────────────── */
:is(div, p).empty[data-ce-state="loading"] {
  color: transparent; user-select: none; min-height: 76px; margin-top: 10px;
  border-radius: var(--r-lg); border: 1px solid var(--border-soft);
  background:
    linear-gradient(100deg, transparent 25%, oklch(1 0 0 / 0.08) 45%, color-mix(in oklch, var(--page-accent) 12%, transparent) 50%, transparent 70%) 0 0 / 260% 100% no-repeat,
    linear-gradient(oklch(1 0 0 / 0.05), oklch(1 0 0 / 0.05)) 18px 22px / 42% 10px no-repeat,
    linear-gradient(oklch(1 0 0 / 0.035), oklch(1 0 0 / 0.035)) 18px 44px / 68% 10px no-repeat,
    var(--glass);
  animation: ce-skeleton 1.7s ease-in-out infinite;
}
@keyframes ce-skeleton { from { background-position: 160% 0, 18px 22px, 18px 44px, 0 0; } to { background-position: -60% 0, 18px 22px, 18px 44px, 0 0; } }
:is(div, p).empty[data-ce-state="empty"] {
  display: flex; align-items: center; gap: 14px; margin-top: 10px; padding: 14px 18px;
  border-radius: var(--r-lg); border: 1px dashed oklch(1 0 0 / 0.13); background: var(--glass);
  color: var(--muted); line-height: 1.5;
  animation: ce-fade-in 0.5s ease both;
}
:is(div, p).empty[data-ce-state="empty"]::before {
  content: ""; flex: none; width: 38px; height: 38px;
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 40 40' fill='none'%3E%3Ccircle cx='20' cy='20' r='15' stroke='%23ffffff' stroke-opacity='.14' stroke-width='1.5' stroke-dasharray='3 5' class='s'/%3E%3Ccircle cx='20' cy='20' r='6' fill='%236fe3b0' fill-opacity='.25'/%3E%3Ccircle cx='20' cy='20' r='3' fill='%236fe3b0'/%3E%3Cg class='p'%3E%3Ccircle cx='20' cy='5' r='2.4' fill='%23f0d68a'/%3E%3C/g%3E%3C/svg%3E") center / contain no-repeat;
}

@media print {
  .ce-ambient, :is(.page, .layout) > nav, .ce-brand { display: none !important; }
  body { padding: 0 !important; }
  .mix, .issue { background-image: none !important; }
}
