/* ═══════════════════════════════════════════════════════════════════════════
   AutoReply Pro — Dashboard
   ═══════════════════════════════════════════════════════════════════════════

   RTL-first. `tokens.css` carries every value; this file only composes them.

   Three rules hold the whole file together:

   1.  LOGICAL PROPERTIES ONLY. `margin-inline-start`, `inset-inline-end`,
       `padding-block`, `border-inline-start`, `text-align: start`. There are
       seven physical exceptions in this file, each marked, where a
       transform has to know which way the page runs.
   2.  NO LETTER-SPACING ON ARABIC. Arabic is a joining script; letter-spacing
       breaks the ligatures apart. The old table headers had `letter-spacing:
       0.5px` and `text-transform: uppercase`, which is meaningless for Arabic
       and mangles mixed strings. The HIG pass added micro-label tracking, and it
       is scoped to `html[lang='en']` plus the monospace id cells that are Latin
       whatever the UI language - see the block at the end of this file.
       `text-transform: uppercase` was deliberately NOT reintroduced.
   3.  ONLY INTERACTIVE SURFACES REACT. `.surface` is a static panel. Lifting
       on hover is opt-in via `.surface--interactive`; the old global
       `.glass-card:hover { transform: translateY(-2px) }` made modals and
       static panels jump when the mouse passed over them.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ─── Reset & base ───────────────────────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

html {
    font-size: 100%;
    -webkit-text-size-adjust: 100%;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}

body {
    font-family: var(--font-ui);
    font-size: var(--fs-200);
    line-height: var(--lh-normal);
    /* ─── An ambient ground, so the glass has something to blur ────────────────
       Translucent surfaces over a SINGLE FLAT COLOUR are indistinguishable from
       flat opaque ones: the blur has nothing to reveal and you pay for it in GPU
       anyway. Apple's vibrancy reads because there is a desktop behind the
       window; the web equivalent is a large, very soft gradient on the canvas.

       `--surface-ambient` is arranged so that `--surface-page` stays the WORST
       CASE for contrast, which is what keeps scripts/check-contrast.mjs honest
       while it still measures a flat value:

         - dark mode gradients only DARKER than --surface-page, so light text can
           only gain contrast away from the measured point;
         - light mode only LIGHTER, so dark text can only gain.

       Change one of those directions and the guard starts reporting a number
       better than the real worst case, which is the failure mode it exists to
       prevent.

       The gradient itself is painted by `body::before` below. `body` keeps the
       flat colour: it propagates to the canvas, so it is what shows under the
       layer, and it is the value the guard measures. */
    background: var(--surface-page);
    color: var(--text-default);
    min-height: 100vh;
    min-height: 100dvh;
    overflow-x: hidden;
    /* Never letter-space: Arabic glyphs join. */
    letter-spacing: normal;
}
/* The ambient gradient on its own fixed layer. It used to be `body { background-attachment:
   fixed }`, which under the glass's backdrop-filter makes Chromium repaint the whole canvas
   on every scroll frame and judders on iOS Safari. A fixed box is composited once and the
   page scrolls over it; z-index -1 puts it under all content and above the canvas. */
body::before {
    content: '';
    position: fixed;
    inset: 0;
    z-index: -1;
    background: var(--surface-ambient, var(--surface-page));
    pointer-events: none;
}

h1, h2, h3, h4, h5 {
    color: var(--text-strong);
    line-height: var(--lh-tight);
    font-weight: var(--fw-bold);
}

a { color: inherit; text-decoration: none; }
button {
    cursor: pointer; border: none; background: none;
    font-family: inherit; font-size: inherit; color: inherit;
    line-height: inherit;
}
input, textarea, select { font-family: inherit; font-size: inherit; color: inherit; }
code, kbd, samp, .mono { font-family: var(--font-mono); font-size: 0.92em; }
img, video, canvas { display: block; max-width: 100%; }
table { border-collapse: collapse; }

.hidden { display: none !important; }

/* ─── Bidi helpers ───────────────────────────────────────────────────────────
   Latin runs — IDs, URLs, @handles, tokens — inside an Arabic sentence must be
   isolated or the surrounding punctuation lands at the wrong end. `<bdi>` does
   the isolation; `.ltr-text` pins the direction for a value that is always
   Latin regardless of what the browser guesses. */
.ltr-text { direction: ltr; unicode-bidi: isolate; text-align: start; }
bdi { unicode-bidi: isolate; }

/* Multi-line user content: per-LINE direction. This is what keeps the
   "🔗 https://…" line of an Arabic DM template at the correct end. */
.user-content {
    unicode-bidi: plaintext;
    white-space: pre-wrap;
    overflow-wrap: anywhere;
    line-height: var(--lh-loose);
}

/* ─── Accessibility ──────────────────────────────────────────────────────── */
.sr-only {
    position: absolute;
    width: 1px; height: 1px;
    padding: 0; margin: -1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
}

:focus-visible {
    /* HIG focus is a soft ring that glows, not a hard rule. `--accent-ring` is the
       accent at ~30% alpha, so the halo reads as light around the control rather
       than a border drawn on it. The solid 2px outline stays underneath at a
       narrow width so the affordance survives forced-colors and high-contrast
       modes, where box-shadow is dropped entirely. */
    outline: 2px solid var(--accent);
    outline-offset: 2px;
    box-shadow: 0 0 0 var(--ring) var(--accent-ring);
    transition: box-shadow var(--motion-fast) var(--ease-standard);
    border-radius: var(--radius-xs);
}
.field:focus-visible,
.field-textarea:focus-visible,
.select:focus-visible { outline-offset: 0; border-radius: var(--radius-sm); }
.switch input:focus-visible + .switch-track { outline: 2px solid var(--accent); outline-offset: 2px; }

.skip-link {
    position: absolute;
    inset-block-start: -100px;
    inset-inline-start: var(--space-3);
    z-index: var(--z-skip);
    padding: var(--space-3) var(--space-4);
    /* Opaque: it lands on top of whatever the page is showing, with no blur behind it. */
    background: var(--surface-solid);
    border: 1px solid var(--accent);
    border-radius: var(--radius-sm);
    color: var(--text-strong);
    font-size: var(--fs-200);
    font-weight: var(--fw-semibold);
    transition: inset-block-start var(--motion-base) var(--ease-standard);
}
.skip-link:focus-visible { inset-block-start: var(--space-3); }

/* ─── Surfaces ───────────────────────────────────────────────────────────── */
.surface {
    background: var(--surface-raised);
    backdrop-filter: blur(var(--blur-glass)) saturate(180%);
    -webkit-backdrop-filter: blur(var(--blur-glass)) saturate(180%);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-lg);
    box-shadow: var(--elev-1);
}
/* The opt-out from glass. `--surface-raised` is translucent now, so pointing this at it
   while also disabling the blur would give a see-through panel with nothing behind it —
   the worst of both. `--surface-solid` exists for exactly this. */
.surface--interactive {
    transition: transform var(--motion-base) var(--ease-standard),
                border-color var(--motion-base) var(--ease-standard),
                box-shadow var(--motion-base) var(--ease-standard);
}
.surface--interactive:hover {
    transform: translateY(-2px);
    border-color: var(--border-strong);
    box-shadow: var(--elev-2);
}
.surface--solid { background: var(--surface-solid); backdrop-filter: none; -webkit-backdrop-filter: none; }
/* Cards in a grid are content, not chrome: blur is for the surfaces content scrolls BEHIND
   (DESIGN §1) — the sidebar, the top bar, modals, toasts, the sticky save bars. Posts alone
   renders ~36 of these, and a backdrop-filter per tile is a blur pass per tile on every
   scroll frame. They keep `.surface`'s border, radius and elevation and take the opaque
   colour `--surface-raised` composites to, so they read the same without the cost.
   Same specificity as `.surface`, and later, so it wins without `!important`. */
.post-card, .campaign-card, .draft-card, .stat-card, .chart-card {
    background: var(--surface-solid);
    backdrop-filter: none; -webkit-backdrop-filter: none;
}
.surface--flush { border-radius: var(--radius-md); }

/* `glass-card` is kept as a plain alias so any markup that still emits it
   keeps its background — but it no longer implies a hover transform. */
.glass-card {
    background: var(--surface-raised);
    backdrop-filter: blur(var(--blur-glass)) saturate(180%);
    -webkit-backdrop-filter: blur(var(--blur-glass)) saturate(180%);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-lg);
}

.pad-4 { padding: var(--space-4); }
.pad-5 { padding: var(--space-5); }

/* ─── Layout primitives (these replace ~200 inline style attributes) ─────── */
.stack { display: flex; flex-direction: column; }
.row { display: flex; flex-direction: row; align-items: center; }
.row--start { align-items: flex-start; }
.row--between { justify-content: space-between; }
.row--end { justify-content: flex-end; }
.row--center { justify-content: center; }
.row--wrap { flex-wrap: wrap; }
.gap-1 { gap: var(--space-1); }
.gap-2 { gap: var(--space-2); }
.gap-3 { gap: var(--space-3); }
.gap-4 { gap: var(--space-4); }
.gap-5 { gap: var(--space-5); }
.grow { flex: 1; min-width: 0; }
.shrink-0 { flex-shrink: 0; }
.nowrap { white-space: nowrap; }
.truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.full-width { width: 100%; }
.text-muted { color: var(--text-muted); }
.text-strong { color: var(--text-strong); }
.text-success { color: var(--success-text); }
.text-danger { color: var(--danger-text); }
.text-warning { color: var(--warning-text); }
.text-meta { font-size: var(--fs-100); color: var(--text-muted); }
.text-end { text-align: end; }
.mbe-2 { margin-block-end: var(--space-2); }
.mbe-3 { margin-block-end: var(--space-3); }
.mbe-4 { margin-block-end: var(--space-4); }
.mbe-5 { margin-block-end: var(--space-5); }
.mbs-4 { margin-block-start: var(--space-4); }

.section { margin-block-end: var(--space-6); }
.section-title {
    font-size: var(--fs-400);
    margin-block-end: var(--space-4);
    color: var(--text-strong);
}

/* ─── App shell ──────────────────────────────────────────────────────────── */
.app { display: flex; min-height: 100vh; min-height: 100dvh; }

.sidebar {
    width: var(--sidebar-width);
    /* Chrome, not a panel: translucent so the canvas reads through it, which is
       what makes a macOS sidebar feel attached to the window rather than stacked
       on top of it. The same token as `.page-header`, so the L where they meet
       is one surface, not two recipes with a seam between them. */
    background: var(--surface-chrome);
    backdrop-filter: blur(var(--blur-chrome)) saturate(180%);
    -webkit-backdrop-filter: blur(var(--blur-chrome)) saturate(180%);
    border-inline-end: 1px solid var(--border-subtle);
    display: flex; flex-direction: column;
    position: fixed;
    inset-block: 0;
    inset-inline-start: 0;
    z-index: var(--z-drawer);
    transition: transform var(--motion-slow) var(--ease-standard);
}
.sidebar-header {
    padding: var(--space-5) var(--space-4);
    border-block-end: 1px solid var(--border-subtle);
}
.sidebar-logo { display: flex; align-items: center; gap: var(--space-3); }
.logo-mark {
    width: 38px; height: 38px;
    background: var(--gradient-brand);
    border-radius: var(--radius-sm);
    display: flex; align-items: center; justify-content: center;
    flex-shrink: 0;
}
.logo-mark [data-lucide] { inline-size: var(--icon-lg); block-size: var(--icon-lg); }
.logo-mark svg { color: var(--text-on-accent); }
.sidebar-brand { font-weight: var(--fw-bold); font-size: var(--fs-300); color: var(--text-strong); }

.sidebar-nav {
    flex: 1;
    padding: var(--space-3) var(--space-2);
    display: flex; flex-direction: column; gap: 2px;
    overflow-y: auto;
    /* Containing block for the indicator below. */
    position: relative;
}

/* ─── The active marker ──────────────────────────────────────────────────────
   One element that MOVES to the current item, instead of a border that blinks
   on and off eight different items. Positioned by App.moveNavIndicator() with
   a `translateY` and a height — no horizontal component, so the same code is
   correct in Arabic and in English; the inline edges come from logical
   properties here. `view-transition-name` lets the browser morph it between
   its old and new geometry during a page transition, which is smoother than
   any JS interpolation and is what `.is-transitioning` stands down for.
   `--motion-base` is 1ms under prefers-reduced-motion, so the marker teleports
   rather than slides for operators who asked for that. */
.nav-indicator {
    position: absolute;
    inset-block-start: 0;
    inset-inline: var(--space-2);
    block-size: var(--tap-target);
    border-radius: var(--radius-sm);
    background: var(--accent-soft);
    border-inline-start: 2px solid var(--accent);
    opacity: 0;
    pointer-events: none;
    z-index: 0;
    view-transition-name: nav-active;
    transition: transform var(--motion-base) var(--ease-standard),
                block-size var(--motion-base) var(--ease-standard),
                opacity var(--motion-fast) var(--ease-standard);
}
.nav-indicator.is-visible { opacity: 1; }
/* First placement, and re-measurements after a resize or a font load: there is
   no previous position to travel from, so do not travel. */
.nav-indicator.no-transition { transition: none; }
/* The browser is morphing the snapshot; animating the live element too would
   be the same move played twice. */
.is-transitioning .nav-indicator { transition: none; }

.nav-item {
    /* Above the marker, which is painted behind the row. */
    position: relative;
    z-index: 1;
    display: flex; align-items: center; gap: var(--space-3);
    padding: var(--space-3);
    min-height: var(--tap-target);
    border-radius: var(--radius-sm);
    font-size: var(--fs-200);
    font-weight: var(--fw-medium);
    color: var(--text-default);
    text-align: start;
    transition: color var(--motion-fast) var(--ease-standard),
                background var(--motion-fast) var(--ease-standard);
}
.nav-item [data-lucide] { inline-size: var(--icon-lg); block-size: var(--icon-lg); flex-shrink: 0; }
.nav-item:hover { color: var(--text-strong); background: var(--surface-glass-hover); }
/* The background and the accent bar now belong to .nav-indicator, which moves.
   This rule only says what the current item's TEXT does — and the old physical
   exception for the bar's reading edge is gone with it, because `inset-inline`
   on the indicator needs no direction-specific override. */
.nav-item.active { color: var(--text-strong); }
.nav-item.active:hover { background: transparent; }

.sidebar-footer { padding: var(--space-2); border-block-start: 1px solid var(--border-subtle); }
.nav-logout { width: 100%; }
.nav-logout:hover { color: var(--danger-text); background: var(--danger-soft); }

.main-content {
    flex: 1;
    min-width: 0;
    margin-inline-start: var(--sidebar-width);
    display: flex; flex-direction: column;
    min-height: 100vh;
    min-height: 100dvh;
}

/* ─── Header ─────────────────────────────────────────────────────────────── */
.page-header {
    min-height: var(--header-height);
    padding-inline: var(--space-6);
    padding-block: var(--space-3);
    display: flex; align-items: center; justify-content: space-between;
    gap: var(--space-3);
    border-block-end: 1px solid var(--border-subtle);
    background: var(--surface-chrome);
    /* The top bar is chrome: content scrolls softly behind it. */
    backdrop-filter: blur(var(--blur-chrome)) saturate(180%);
    -webkit-backdrop-filter: blur(var(--blur-chrome)) saturate(180%);
    position: sticky;
    inset-block-start: 0;
    z-index: var(--z-header);
}
.header-start { display: flex; align-items: center; gap: var(--space-3); min-width: 0; }
.header-end { display: flex; align-items: center; gap: var(--space-2); min-width: 0; }
.page-title { font-size: var(--fs-500); }
.page-subtitle { font-size: var(--fs-100); color: var(--text-muted); }

.icon-button {
    display: inline-flex; align-items: center; justify-content: center;
    min-width: var(--tap-target); min-height: var(--tap-target);
    border-radius: var(--radius-sm);
    color: var(--text-default);
    transition: background var(--motion-fast) var(--ease-standard), color var(--motion-fast) var(--ease-standard);
}
.icon-button:hover:not(:disabled) { background: var(--surface-glass-hover); color: var(--text-strong); }
.icon-button:active:not(:disabled) { background: color-mix(in srgb, var(--text-strong) 14%, transparent); }
/* It had no disabled state at all, so a chrome button that had been turned off
   looked, and hovered, exactly like one that worked. */
.icon-button:disabled { opacity: 0.35; cursor: not-allowed; }
.icon-button [data-lucide] { inline-size: var(--icon-lg); block-size: var(--icon-lg); }
.menu-button { display: none; }

/* The "System online" badge that used to live here is gone, along with its
   pulsing dot. It was a pill that was green whenever the page had rendered,
   which is exactly when the operator already knew the app was up — and the
   Overview briefing is where a real problem belongs. Health is silent when
   fine and loud when broken. */

/* ─── Language toggle ────────────────────────────────────────────────────────
   A rarely-touched preference next to the primary navigation, so it is a plain
   text button rather than a pill with a glyph. The label is the target
   language's autonym and carries its own `lang`, so العربية is shaped by the Arabic face
   and "English" set in Inter regardless of the interface language. */
.lang-toggle {
    display: inline-flex; align-items: center; justify-content: center;
    min-height: var(--tap-target);
    padding-inline: var(--space-3);
    border-radius: var(--radius-sm);
    font-size: var(--fs-100);
    font-weight: var(--fw-semibold);
    color: var(--text-muted);
    white-space: nowrap;
    transition: color var(--motion-fast) var(--ease-standard),
                background var(--motion-fast) var(--ease-standard);
}
.lang-toggle:lang(en) { font-family: var(--font-latin); }
.lang-toggle:hover { color: var(--text-strong); background: var(--surface-glass-hover); }

/* Compact popover-ish selects in the header */
.chip-select {
    display: flex; align-items: center; gap: var(--space-2);
    padding-inline: var(--space-3) var(--space-2);
    background: var(--surface-glass);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-pill);
    min-width: 0;
}
.chip-select:focus-within { border-color: var(--accent); box-shadow: 0 0 0 var(--ring) var(--accent-ring); }
.chip-select [data-lucide] { inline-size: var(--icon-md); block-size: var(--icon-md); flex-shrink: 0; }
.chip-select svg { color: var(--text-muted); }
.chip-select select {
    background: transparent; border: none;
    outline: 2px solid transparent; /* not `none`: still renders in forced-colors mode */
    color: var(--text-strong);
    font-size: var(--fs-100); font-weight: var(--fw-semibold);
    /* A 44px target inside a pill that draws at ~30px: 7px more padding each way and 7px
       of negative margin, so the select overhangs the pill invisibly and the pill's own
       box, and the text inside it, stay exactly where they were. */
    padding-block: calc(var(--space-2) + 7px);
    margin-block: -7px;
    padding-inline: 0 var(--space-1);
    min-block-size: var(--tap-target);
    max-width: 180px;
    appearance: none; cursor: pointer;
    text-overflow: ellipsis;
}
/* A native <option> is drawn by the OS popup, which cannot be translucent or blurred —
   a glass value here renders unpredictably per platform. Always opaque. */
.chip-select select option { background: var(--surface-solid); color: var(--text-strong); font-weight: var(--fw-regular); }
.chip-select select:disabled { opacity: 0.5; cursor: progress; }

/* ─── Page container ─────────────────────────────────────────────────────── */
.page-container {
    padding: var(--space-5) var(--space-6);
    flex: 1;
    animation: pageIn var(--motion-slow) var(--ease-standard);
    /* Navigation replaces this container's contents. Naming it gives it its
       own snapshot pair, so the browser cross-fades the outgoing page into the
       incoming page's skeleton instead of showing an empty box between them.
       Everything else — the page title, the nav labels — stays in the root
       snapshot and cross-fades with it. */
    view-transition-name: page;
}
@keyframes pageIn { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) { .page-container { animation: none; } }

/* ─── View transitions ───────────────────────────────────────────────────────
   Same-document view transitions have been Baseline since October 2025
   (Chrome 111, Safari 18, Firefox 144). Motion.transition() feature-detects
   `document.startViewTransition` and runs the DOM update directly where it is
   absent, so the fallback is simply the behaviour this dashboard had before.

   The page transition is a cross-fade plus a small VERTICAL rise. Nothing
   slides horizontally: a slide that reads as "forward" in Arabic reads as
   "back" in English, and this app ships both from the same stylesheet.
   Vertical is the only direction that means the same thing in both. */
::view-transition-old(page),
::view-transition-new(page) {
    animation-duration: var(--motion-base);
    animation-timing-function: var(--ease-standard);
}
::view-transition-new(page) { animation-name: pageVtIn; }
::view-transition-old(page) { animation-name: pageVtOut; }
@keyframes pageVtIn { from { opacity: 0; transform: translateY(8px); } }
@keyframes pageVtOut { to { opacity: 0; transform: translateY(-4px); } }

::view-transition-group(nav-active) {
    animation-duration: var(--motion-base);
    animation-timing-function: var(--ease-standard);
}

/* Belt and braces: tokens.css already collapses --motion-* to 1ms under
   reduced motion, and Motion.transition() skips startViewTransition entirely,
   but a transition started before the setting changed must not keep playing. */
@media (prefers-reduced-motion: reduce) {
    ::view-transition-group(*),
    ::view-transition-old(*),
    ::view-transition-new(*) { animation: none !important; }
}

/* ─── Skeletons ──────────────────────────────────────────────────────────────
   Shapes, not spinners, and shapes that match the real layout row-for-row so
   the content that lands does not move anything. A spinner tells the operator
   only that time is passing, which is the one thing they can already see; a
   skeleton tells them what is coming and roughly how much of it.

   They are never painted for a request that beats ~150ms — see
   Motion.beginLoad(). A skeleton that appears and vanishes inside a blink
   reads as a glitch, not as speed. */
.skel {
    display: block;
    background: var(--surface-glass-hover);
    border-radius: var(--radius-xs);
    position: relative;
    overflow: hidden;
}
.skel::after {
    content: '';
    position: absolute;
    inset: 0;
    background: linear-gradient(90deg, transparent, var(--surface-glass), transparent);
    transform: translateX(-100%);
    animation: skelSweep 1.4s linear infinite; /* linear: an infinite sweep hitches at every loop boundary on an easing curve */
}
@keyframes skelSweep { to { transform: translateX(100%); } }
/* The sweep is the only thing here that moves. Without it the shapes still
   read as "not yet content", so reduced motion loses nothing. */
@media (prefers-reduced-motion: reduce) { .skel::after { animation: none; } }

/* The line is an INLINE-BLOCK, 0.7em tall, with no margin. That is what makes
   a skeleton row exactly as tall as the real row it stands in for: it drops
   into the text flow and the line box keeps the height the element's own
   line-height gives it, instead of the skeleton inventing a box of its own.
   Skeletons built from the real component classes (.stat-card, .data-table,
   .chart-card) therefore occupy the real component's box, and the content that
   lands does not move anything. */
.skel-line {
    display: inline-block;
    vertical-align: middle;
    block-size: 0.7em;
    margin: 0;
}
.skel-block { display: block; block-size: 100%; inline-size: 100%; }
.skel-btn { display: inline-block; block-size: var(--tap-target); inline-size: 120px; border-radius: var(--radius-sm); }
.skel-w-xs { inline-size: 20%; }
.skel-w-sm { inline-size: 35%; }
.skel-w-md { inline-size: 55%; }
.skel-w-lg { inline-size: 75%; }
.skel-w-full { inline-size: 100%; }
/* For skeletons that stand in for a whole page rather than a fixed region,
   where there is no real box to match. */
.skel-stack { display: flex; flex-direction: column; gap: var(--space-3); }
.skel-card { padding: var(--space-5); }
.skel-card-tall { min-block-size: 220px; }
.skel-thread { pointer-events: none; }

/* ─── Press feedback ─────────────────────────────────────────────────────────
   Every interactive surface acknowledges the press in the same frame as the
   press, before any network work starts. `--motion-fast` is 1ms under reduced
   motion, so the acknowledgement survives as a state change without the
   travel. `touch-action: manipulation` removes the ~300ms double-tap wait that
   mobile browsers otherwise insert before a click, which is the single largest
   source of input latency on a phone and costs nothing to remove. */
.btn, .icon-button, .icon-btn, .nav-item, .thread-item, .post-picker-item,
.page-btn, .alert-link, .modal-close, .lang-toggle, .login-alt-link, .switch {
    touch-action: manipulation;
    -webkit-tap-highlight-color: transparent;
}
/* `:not(:disabled)` on every one of these that can BE disabled. `.icon-button`
   and `.modal-close` were the two that were not, which meant a disabled control
   still shrank under the pointer — the one piece of feedback that says "this
   press registered", given for a press that did nothing. Both now have real
   `:disabled` rules further down, so leaving them unguarded would have been a
   contradiction introduced by this change rather than one inherited. */
.btn:active:not(:disabled),
.icon-button:active:not(:disabled),
.icon-btn:active:not(:disabled),
.post-picker-item:active,
.page-btn:active:not(:disabled),
.modal-close:active:not(:disabled),
.lang-toggle:active {
    transform: scale(0.97);
    transition-duration: var(--motion-fast);
}
/* A full-width row shrinking by 3% looks like a wobble; darken it instead. */
.nav-item:active, .thread-item:active { background: var(--surface-glass-hover); }
.alert-link:active { opacity: 0.7; }

/* ─── Icon box ───────────────────────────────────────────────────────────────
   Lucide is deferred, so `<i data-lucide>` exists for a moment before it is
   replaced by the `<svg>` it becomes, and lucide@0.577 copies `data-lucide`
   onto that svg. So every icon SIZE in this file is written on `[data-lucide]`,
   which matches both phases: the placeholder occupies the box the icon will by
   construction. (It used to be a parallel list of `i[data-lucide]` rules kept
   in step with the `svg` rules by hand; 22 had drifted, and every one of those
   icons jumped when the library landed.) Colour stays on `svg`: an empty `<i>`
   has nothing to paint.

   `:where()` is the default for an icon no context sizes — zero
   specificity, so any `.x [data-lucide]` rule wins. */
:where([data-lucide]) { inline-size: var(--icon-md); block-size: var(--icon-md); }
/* An empty inline `<i>` ignores a size; the replaced `<svg>` does not need this. */
i[data-lucide] { display: inline-block; flex-shrink: 0; }

.page-toolbar {
    display: flex; align-items: center; justify-content: space-between;
    gap: var(--space-4); flex-wrap: wrap;
    margin-block-end: var(--space-5);
}
.page-toolbar-count { font-size: var(--fs-200); color: var(--text-muted); }
.toolbar-actions { display: flex; gap: var(--space-2); flex-wrap: wrap; }

/* ─── Buttons ────────────────────────────────────────────────────────────── */
.btn {
    display: inline-flex; align-items: center; justify-content: center;
    gap: var(--space-2);
    padding: var(--space-3) var(--space-5);
    min-height: var(--tap-target);
    border-radius: var(--radius-sm);
    font-weight: var(--fw-semibold);
    font-size: var(--fs-200);
    transition: background var(--motion-fast) var(--ease-standard),
                border-color var(--motion-fast) var(--ease-standard),
                box-shadow var(--motion-fast) var(--ease-standard),
                transform var(--motion-fast) var(--ease-standard);
}
.btn [data-lucide] { inline-size: var(--icon-md); block-size: var(--icon-md); flex-shrink: 0; }
/* The busy spinner is a <div>, not an <svg>, so it needs its own reservation or
   the flex row squeezes it into an ellipse while the label is still beside it. */
.btn .spinner { flex-shrink: 0; }
/* `box-shadow: none` here is what turns the primary button's glow off when it is
   disabled: a control that still glows reads as available. */
.btn:disabled { opacity: 0.5; cursor: not-allowed; box-shadow: none; transform: none; }

/* ─── The state matrix ───────────────────────────────────────────────────────
   Four variants x four states, and HOVER ALWAYS CHANGES COLOUR.
   `.btn-primary:hover` used to be `transform: translateY(-1px)` and nothing
   else, which means it said nothing at all under `prefers-reduced-motion`:
   --motion-fast collapses to 1ms there, so the lift lands in one frame and a
   mouse user watching for feedback sees a button that never acknowledged them.
   Colour is the state; the lift is the flourish on top of it.

   `--accent-hover` and `--accent-active` have been in tokens.css since the HIG
   pass with exactly one consumer between them (`.username-link:hover`). They
   are the primary button's hover and press, which is what they were named for. */
.btn-primary {
    background: var(--gradient-brand);
    color: var(--text-on-accent);
    box-shadow: var(--elev-glow);
}
/* Deeper at every stop than the resting gradient (#0a7cf0 -> #0077ed at the lit
   edge, #0064cc -> #005bb5 at the foot), so white-on-blue can only GAIN contrast
   on hover. --accent-hover as a FLAT fill would have taken the whole face to
   4.32:1, under the 4.5:1 floor the rest of this palette is held to. */
.btn-primary:hover:not(:disabled) {
    background: linear-gradient(180deg, var(--accent-hover), var(--accent) 70%, var(--accent-active));
    transform: translateY(-1px);
}
/* Pressed is flat: a control being pushed in is not lit from above. This also
   restores the shared `scale(0.97)` press, which the old hover rule was
   silently beating — same specificity, and `:hover` was written later, so a
   primary button was the one button in the product that did not acknowledge a
   click. */
.btn-primary:active:not(:disabled) {
    background: var(--accent-active);
    box-shadow: none;
    transform: scale(0.97);
}
.btn-primary:focus-visible { box-shadow: var(--elev-glow), 0 0 0 var(--ring) var(--accent-ring); }

.btn-secondary {
    background: var(--surface-glass);
    color: var(--text-strong);
    border: 1px solid var(--border-subtle);
}
.btn-secondary:hover:not(:disabled) { background: var(--surface-glass-hover); border-color: var(--border-strong); }
/* One step past the hover tint, derived from --text-strong so it darkens in the
   light theme and lightens in the dark one — which is exactly what
   --surface-glass-hover does, just not far enough to read as a second step. */
.btn-secondary:active:not(:disabled) {
    background: color-mix(in srgb, var(--text-strong) 14%, transparent);
    border-color: var(--border-strong);
}
.btn-secondary:focus-visible { box-shadow: 0 0 0 var(--ring) var(--accent-ring); }

.btn-danger {
    background: var(--danger-soft);
    color: var(--danger-text);
    border: 1px solid var(--border-danger);
}
.btn-danger:hover:not(:disabled) {
    background: color-mix(in srgb, var(--danger) 22%, transparent);
    border-color: color-mix(in srgb, var(--danger) 45%, transparent);
}
.btn-danger:active:not(:disabled) { background: color-mix(in srgb, var(--danger) 30%, transparent); }
/* The one variant that must NOT take the global accent ring. Every other button
   in the product falls through to a blue halo, and a blue halo around "delete
   permanently" is the interface contradicting itself at the single moment the
   operator most needs it to be consistent. 32% matches --accent-ring's alpha,
   so the two rings have the same weight and only differ in hue. */
.btn-danger:focus-visible,
.icon-btn-danger:focus-visible {
    outline-color: var(--danger);
    box-shadow: 0 0 0 var(--ring) color-mix(in srgb, var(--danger) 32%, transparent);
}

.btn-ghost { color: var(--text-default); }
.btn-ghost:hover:not(:disabled) { background: var(--surface-glass-hover); color: var(--text-strong); }
.btn-ghost:active:not(:disabled) {
    background: color-mix(in srgb, var(--text-strong) 14%, transparent);
    color: var(--text-strong);
}
.btn-ghost:focus-visible { box-shadow: 0 0 0 var(--ring) var(--accent-ring); }

.btn-full { width: 100%; }
.btn-sm { padding: var(--space-2) var(--space-4); font-size: var(--fs-100); }

/* ─── Busy ───────────────────────────────────────────────────────────────────
   There was no in-flight state in this stylesheet at all. The only busy
   affordance in the product was JS swapping a button's innerHTML for
   `UI.buttonSpinner()`, which changes the CONTENT and leaves the control
   looking pressable — so a slow serverless cold start showed a live-looking
   button with a spinner in it, and the operator clicked again.

   `aria-busy` is the state; `.is-loading` is the same state for anything that
   cannot carry the attribute. Both say the same thing, so both are styled here.

   On non-interactivity: `UI.actionBusy`/`UI.formBusy` also set `disabled`, and a
   DISABLED element still takes its own `cursor`, while a `pointer-events: none`
   one hands the cursor to whatever is underneath. So the properly-driven path
   gets `cursor: progress` and the browser's own click suppression, and
   `pointer-events: none` is the belt for anyone who sets the class alone. */
.btn[aria-busy='true'],
.btn.is-loading,
.icon-btn[aria-busy='true'],
.icon-btn.is-loading,
.icon-button[aria-busy='true'],
.icon-button.is-loading {
    cursor: progress;
    /* Dimmed, but not to the 0.5 of :disabled — this button is working, not
       unavailable, and the label has to stay readable while it works. */
    opacity: var(--opacity-busy);
    /* The label is mid-swap; a drag across it selects a string that is about to
       stop existing. */
    user-select: none;
}
.btn[aria-busy='true']:not(:disabled),
.btn.is-loading:not(:disabled),
.icon-btn[aria-busy='true']:not(:disabled),
.icon-btn.is-loading:not(:disabled),
.icon-button[aria-busy='true']:not(:disabled),
.icon-button.is-loading:not(:disabled) { pointer-events: none; }
/* Beats `.btn:disabled` / `.icon-btn:disabled` on source order at equal
   specificity — those are 0.5 and 0.35, which read as "switched off". */
.btn[aria-busy='true']:disabled,
.btn.is-loading:disabled,
.icon-btn[aria-busy='true']:disabled,
.icon-btn.is-loading:disabled,
.icon-button[aria-busy='true']:disabled,
.icon-button.is-loading:disabled { opacity: var(--opacity-busy); cursor: progress; }
/* Busy is a state, not a hover target: no lift, no press, no ring-on-hover. */
.btn[aria-busy='true']:hover,
.btn.is-loading:hover,
.btn[aria-busy='true']:active,
.btn.is-loading:active { transform: none; }
/* `.spinner`'s own colours are --border-subtle on --accent, which is invisible
   on a blue primary button and nearly so on a red danger one. Inside a button it
   borrows the label's colour instead, so one spinner reads on every variant. */
.btn .spinner,
.icon-btn .spinner,
.icon-button .spinner {
    border-color: color-mix(in srgb, currentColor 25%, transparent);
    border-top-color: currentColor;
}

/* Icon-only row actions. 44px everywhere, not only on touch: a 34px target is
   not reachable with a trackpad either. */
.icon-btn {
    display: inline-flex; align-items: center; justify-content: center;
    width: var(--tap-target); height: var(--tap-target);
    border-radius: var(--radius-sm);
    border: 1px solid var(--border-subtle);
    background: var(--surface-glass);
    color: var(--text-default);
    transition: color var(--motion-fast) var(--ease-standard),
                border-color var(--motion-fast) var(--ease-standard),
                background var(--motion-fast) var(--ease-standard);
}
.icon-btn [data-lucide] { inline-size: var(--icon-md); block-size: var(--icon-md); }
.icon-btn:hover:not(:disabled) { color: var(--text-strong); border-color: var(--border-strong); background: var(--surface-glass-hover); }
.icon-btn:active:not(:disabled) { background: color-mix(in srgb, var(--text-strong) 14%, transparent); }
/* A rest state, which it did not have. `.icon-btn-danger` was a neutral grey
   trash icon that only turned red once the pointer was already on it — so the
   one control in a row that destroys something looked exactly like the ones
   that do not, and a keyboard user tabbing through never saw the difference at
   all. The destructive one now says so before it is touched. */
.icon-btn-danger {
    color: var(--danger-text);
    border-color: color-mix(in srgb, var(--danger) 24%, transparent);
}
.icon-btn-danger:hover:not(:disabled) {
    color: var(--danger-text);
    background: var(--danger-soft);
    border-color: color-mix(in srgb, var(--danger) 40%, transparent);
}
.icon-btn-danger:active:not(:disabled) { background: color-mix(in srgb, var(--danger) 24%, transparent); }
.icon-btn:disabled { opacity: 0.35; cursor: not-allowed; }
.row-actions { display: flex; gap: var(--space-2); justify-content: flex-end; }

/* Segmented control (tabs) */
.segmented {
    display: flex; gap: var(--space-1);
    background: var(--surface-sunken);
    padding: var(--space-1);
    border-radius: var(--radius-sm);
    border: 1px solid var(--border-subtle);
}
.segmented .btn { box-shadow: none; border: none; }
/* The pressed segment is a solid tile lifted off the sunken track, whatever
   `.btn-*` variant the page happened to emit: (0,3,0) outranks `.btn-primary`
   and `.btn-ghost`, and sits after their `:hover`/`:active` rules of equal
   specificity, so a pressed segment does not change on hover. Nav tabs mark
   the current one with `aria-current` instead of `aria-pressed`. */
.segmented .btn[aria-pressed='true'],
.segmented .btn[aria-current='page'] {
    background: var(--surface-solid); color: var(--text-strong);
    box-shadow: var(--elev-1);
}
.segmented .btn[aria-pressed='false'] { color: var(--text-default); }

/* ─── Forms ──────────────────────────────────────────────────────────────── */
.form-group { margin-block-end: var(--space-4); }
.form-label {
    display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap;
    font-size: var(--fs-200);
    font-weight: var(--fw-semibold);
    color: var(--text-strong);
    margin-block-end: var(--space-2);
}
.label-optional { font-weight: var(--fw-regular); color: var(--text-muted); font-size: var(--fs-100); }
.field, .field-textarea, .select {
    width: 100%;
    padding: var(--space-3);
    min-height: var(--tap-target);
    background: var(--surface-sunken);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-sm);
    color: var(--text-strong);
    font-size: var(--fs-200);
    line-height: var(--lh-normal);
    outline: 2px solid transparent; /* not `none`: a transparent outline still renders in forced-colors mode, where box-shadow and border-color are discarded */
    transition: border-color var(--motion-fast) var(--ease-standard),
                box-shadow var(--motion-fast) var(--ease-standard);
}
.field::placeholder, .field-textarea::placeholder { color: var(--text-muted); opacity: 1; }
.field:focus, .field-textarea:focus, .select:focus {
    border-color: var(--accent);
    box-shadow: 0 0 0 var(--ring) var(--accent-ring);
}
/* Invalid: a red ring in place of the accent one. Same specificity as `:focus` above and
   written after it, so a focused invalid field stays red instead of turning blue. */
.field[aria-invalid='true'], .field-textarea[aria-invalid='true'], .select[aria-invalid='true'] {
    border-color: var(--danger);
    box-shadow: 0 0 0 var(--ring) color-mix(in srgb, var(--danger) 32%, transparent);
}
/* Disabled: faded and refusing the cursor, the same treatment `.icon-btn:disabled` gets. */
.field:disabled, .field-textarea:disabled, .select:disabled { opacity: var(--opacity-disabled); cursor: not-allowed; }
.field-textarea { min-height: 110px; resize: vertical; line-height: var(--lh-loose); }
.field-mono { font-family: var(--font-mono); font-size: var(--fs-100); word-break: break-all; }
.select {
    appearance: none; cursor: pointer; padding-inline-end: var(--space-6);
    /* Scoped to .select only: .field/.field-textarea share the base rule above and must not
       show a dropdown chevron. This used to live on the shared rule, where its own
       `background: var(--surface-sunken)` shorthand immediately reset it back to none. */
    background-image: var(--select-chevron); /* themed in tokens.css: a fixed grey stroke measured under 3:1 on the light sunken well */
    background-repeat: no-repeat;
    background-position: right var(--space-3) center;
    background-size: 16px 16px;
}
.form-hint { font-size: var(--fs-100); color: var(--text-muted); margin-block-start: var(--space-2); line-height: var(--lh-normal); }
/* `.text-warning` is declared earlier, so on source order `.form-hint`'s muted
   colour beat it and every "form-hint text-warning" rendered grey. */
.form-hint.text-warning { color: var(--warning-text); }
.form-actions { margin-block-start: var(--space-5); display: flex; gap: var(--space-2); flex-wrap: wrap; }
.field-row { display: flex; gap: var(--space-2); align-items: stretch; }
.field-row .field { flex: 1; }
.form-grid { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-4); }

input[type='range'] { width: 100%; accent-color: var(--accent); min-height: var(--tap-target); }
input[type='file'].field { padding: var(--space-2); line-height: 2; }
input[type='checkbox'] { accent-color: var(--accent); inline-size: 24px; block-size: 24px; flex-shrink: 0; cursor: pointer; }

/* Toggle switch */
.switch { position: relative; display: inline-block; width: 46px; height: 26px; flex-shrink: 0; }
.switch input { opacity: 0; width: 0; height: 0; }
.switch-track {
    position: absolute; inset: 0;
    cursor: pointer;
    /* HIG "secondary fill" rather than a surface: an off switch is a tinted well,
       not a floating panel, and it must not blur what is behind it. */
    background: var(--surface-glass);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-pill);
    transition: background var(--motion-base) var(--ease-standard);
}
.switch-track::before {
    content: '';
    position: absolute;
    width: 18px; height: 18px;
    inset-block-start: 3px;
    inset-inline-start: 3px;
    background: var(--text-muted);
    border-radius: 50%;
    transition: transform var(--motion-base) var(--ease-standard), background var(--motion-base) var(--ease-standard);
}
/* Physical exception 1/3: the knob travels toward the inline end. */
.switch input:checked + .switch-track { background: var(--accent); border-color: var(--accent); }
.switch input:checked + .switch-track::before { transform: translateX(20px); background: var(--text-on-accent); }
html[dir='rtl'] .switch input:checked + .switch-track::before { transform: translateX(-20px); }
/* A 46x26 control with a 44px hit area. */
.switch-track::after { content: ''; position: absolute; inset-block: -9px; inset-inline: 0; }
/* The same idea for controls whose drawn size must stay small: a chip's remove button, a
   heading's anchor, a sort header, an inline permalink, a card's title link, a KPI tip.
   The ::after is centred on the control and grows it to 44px in each axis it falls short
   in, never shrinking it (`min(0px, …)`): percentages here are of the control's own box,
   because the control is the containing block. */
.growth-chip-x, .help-anchor, .th-sort, .growth-permalink, .draft-title a, .growth-tip > summary { position: relative; }
.growth-chip-x::after, .help-anchor::after, .th-sort::after, .growth-permalink::after,
.draft-title a::after, .growth-tip > summary::after {
    content: '';
    position: absolute;
    inset-block: min(0px, (100% - var(--tap-target)) / 2);
    inset-inline: min(0px, (100% - var(--tap-target)) / 2);
}
.switch-row { display: flex; align-items: center; gap: var(--space-3); }
.switch-label { font-size: var(--fs-200); color: var(--text-default); }
/* A switch the operator cannot change (TikTok has the interaction turned off
   for the account). Greyed, not hidden, and the row says why — so the track
   fades, the knob stops at "off", and the label drops to muted text. The
   label stays a real text colour rather than taking the opacity, so the
   reason beside it is still readable. */
.switch input:disabled + .switch-track { opacity: var(--opacity-disabled); cursor: not-allowed; }
.switch-row.is-disabled .switch-label { color: var(--text-muted); }
/* Label with a hint under it, beside a switch. */
.switch-text { display: flex; flex-direction: column; gap: var(--space-1); min-inline-size: 0; }
.switch-text .form-hint { margin: 0; }

/* Checkbox with a visible label and a hint under it. The box aligns with the
   label's first line, so a two-line hint does not drag it to the middle. */
.check-row { display: flex; align-items: flex-start; gap: var(--space-3); }
.check-text { display: flex; flex-direction: column; gap: var(--space-1); min-inline-size: 0; padding-block-start: 2px; }
.check-label { font-size: var(--fs-200); color: var(--text-default); cursor: pointer; }
.check-text .form-hint { margin: 0; }
input[type='checkbox']:disabled { cursor: not-allowed; opacity: var(--opacity-disabled); }
.check-row.is-disabled .check-label { color: var(--text-muted); cursor: not-allowed; }

/* A fieldset that groups without drawing a box. */
.fieldset-plain { border: 0; padding: 0; margin-inline: 0; min-inline-size: 0; }
.fieldset-plain > legend { padding: 0; }
.toggle-block {
    display: flex; align-items: center; justify-content: space-between;
    gap: var(--space-4);
    margin-block-end: var(--space-4);
}
.toggle-block h4 { font-size: var(--fs-200); font-weight: var(--fw-semibold); }
.toggle-block p { font-size: var(--fs-100); color: var(--text-muted); }

.divider { border: none; block-size: 1px; background: var(--border-subtle); margin-block: var(--space-5); }

/* ─── Badges, pills, chips ───────────────────────────────────────────────── */
.badge {
    display: inline-flex; align-items: center; gap: var(--space-1);
    padding: var(--space-1) var(--space-2);
    font-size: var(--fs-100);
    font-weight: var(--fw-semibold);
    border-radius: var(--radius-pill);
    white-space: nowrap;
}
.badge [data-lucide] { inline-size: var(--icon-xs); block-size: var(--icon-xs); }
.badge-success { background: var(--success-soft); color: var(--success-text); }
.badge-warning { background: var(--warning-soft); color: var(--warning-text); }
.badge-danger { background: var(--danger-soft); color: var(--danger-text); }
.badge-info { background: var(--info-soft); color: var(--info-text); }
.badge-neutral { background: var(--surface-glass-hover); color: var(--text-default); }
.badge-instagram { background: var(--brand-instagram-soft); color: var(--brand-instagram-text); }
.badge-facebook { background: var(--brand-facebook-soft); color: var(--brand-facebook-text); }
.badge-tiktok { background: var(--brand-tiktok-soft); color: var(--text-strong); }
.badge-youtube { background: var(--brand-youtube-soft); color: var(--text-strong); }

.status-pill {
    display: inline-flex; align-items: center; gap: var(--space-1);
    padding: var(--space-1) var(--space-3);
    border-radius: var(--radius-pill);
    font-size: var(--fs-100);
    font-weight: var(--fw-semibold);
    white-space: nowrap;
}
.status-pill.sent, .status-pill.published { background: var(--success-soft); color: var(--success-text); }
.status-pill.failed { background: var(--danger-soft); color: var(--danger-text); }
.status-pill.pending, .status-pill.publishing { background: var(--warning-soft); color: var(--warning-text); }
.status-pill.user_blocked_dms { background: var(--warning-soft); color: var(--warning-text); }

.health-pill {
    display: inline-flex; align-items: center; gap: var(--space-2);
    padding: var(--space-2) var(--space-3);
    border-radius: var(--radius-pill);
    font-size: var(--fs-100);
    font-weight: var(--fw-semibold);
    white-space: nowrap;
}
.health-pill [data-lucide] { inline-size: var(--icon-sm); block-size: var(--icon-sm); }
.health-fresh { background: var(--success-soft); color: var(--success-text); }
.health-warn { background: var(--warning-soft); color: var(--warning-text); }
.health-stale { background: var(--danger-soft); color: var(--danger-text); }
.health-dot { width: 7px; height: 7px; border-radius: 50%; background: currentColor; }

.chip {
    display: inline-flex; align-items: center; gap: var(--space-1);
    padding: var(--space-1) var(--space-3);
    border-radius: var(--radius-pill);
    background: var(--surface-glass-hover);
    border: 1px solid var(--border-subtle);
    font-size: var(--fs-100);
    color: var(--text-default);
}
.chip [data-lucide] { inline-size: var(--icon-xs); block-size: var(--icon-xs); }
.chip-accent { background: var(--accent-soft); color: var(--accent-text); border-color: color-mix(in srgb, var(--accent) 30%, transparent); }
.chip-danger { background: var(--danger-soft); color: var(--danger-text); border-color: var(--border-danger); }

.platform-tag {
    display: inline-flex; align-items: center;
    font-size: var(--fs-100);
    font-weight: var(--fw-semibold);
    color: var(--brand-on-solid);
    padding: 2px var(--space-2);
    border-radius: var(--radius-xs);
    margin-inline-start: var(--space-2);
}
/* Solid brand + white measured 4.34:1 (ig) / 4.23:1 (fb) at this size — below AA, in every
   log row. The soft/text pair is the same treatment `.badge-instagram` already uses. */
.platform-tag.ig { background: var(--brand-instagram-soft); color: var(--brand-instagram-text); }
.platform-tag.fb { background: var(--brand-facebook-soft); color: var(--brand-facebook-text); }

.username-link {
    color: var(--accent-text);
    font-weight: var(--fw-medium);
    /* An inline link in a table cell is still a tap target: 26px of line box
       is not one, so the link itself carries the 44px minimum. */
    display: inline-flex;
    align-items: center;
    min-height: var(--tap-target);
}
.username-link:hover { color: var(--accent-hover); text-decoration: underline; }

.dot-blink { width: 7px; height: 7px; border-radius: 50%; display: inline-block; background: currentColor; animation: blink 1.5s infinite alternate; }
@keyframes blink { 0% { opacity: 0.3; } 100% { opacity: 1; } }
@media (prefers-reduced-motion: reduce) { .dot-blink { animation: none; } }

/* ─── Tables ─────────────────────────────────────────────────────────────── */
.table-card { padding: var(--space-5); }
.table-header {
    display: flex; align-items: center; justify-content: space-between;
    gap: var(--space-4); flex-wrap: wrap;
    margin-block-end: var(--space-4);
}
.table-title { font-size: var(--fs-400); font-weight: var(--fw-semibold); color: var(--text-strong); }
.table-wrapper { overflow-x: auto; }
.data-table { width: 100%; }
.data-table th {
    text-align: start;
    padding: var(--space-2) var(--space-3);
    font-size: var(--fs-100);
    font-weight: var(--fw-semibold);
    /* No uppercase, no letter-spacing: both are meaningless for Arabic, and
       `--text-muted` here is now 5.7:1 rather than the old 2.6:1. */
    color: var(--text-muted);
    border-block-end: 1px solid var(--border-subtle);
    white-space: nowrap;
}
.data-table td {
    padding: var(--space-3);
    font-size: var(--fs-200);
    border-block-end: 1px solid var(--border-subtle);
    color: var(--text-default);
    vertical-align: middle;
}
.data-table tbody tr:hover td { background: var(--surface-glass-hover); }
.data-table tbody tr:last-child td { border-block-end: none; }
.cell-num { text-align: end; font-variant-numeric: tabular-nums; }
.cell-id { font-size: var(--fs-100); color: var(--text-muted); max-width: 140px; }
.cell-error { max-width: 220px; font-size: var(--fs-100); color: var(--danger-text); }

/* ─── Empty / error / loader ─────────────────────────────────────────────── */
.empty-state > [data-lucide] { inline-size: 44px; block-size: 44px; }
.empty-state > svg { color: var(--text-muted); }
.empty-state h3 { font-size: var(--fs-400); color: var(--text-strong); }
.empty-state p { font-size: var(--fs-200); color: var(--text-muted); max-width: 42ch; }

.error-panel {
    display: flex; flex-direction: column; align-items: center;
    text-align: center; gap: var(--space-3);
    padding: var(--space-6) var(--space-5);
}
.error-panel-icon {
    width: 48px; height: 48px;
    border-radius: var(--radius-md);
    background: var(--danger-soft);
    color: var(--danger-text);
    display: flex; align-items: center; justify-content: center;
}
.error-panel-icon [data-lucide] { inline-size: var(--icon-xl); block-size: var(--icon-xl); }
.error-panel h3 { font-size: var(--fs-400); }
.error-panel-message { font-size: var(--fs-200); color: var(--text-default); max-width: 52ch; }
.error-panel-hint { font-size: var(--fs-100); color: var(--text-muted); }

.inline-error {
    display: flex; gap: var(--space-3); align-items: flex-start;
    padding: var(--space-3);
    margin-block-end: var(--space-4);
    border-radius: var(--radius-sm);
    background: var(--danger-soft);
    border: 1px solid var(--border-danger);
    font-size: var(--fs-200);
    color: var(--text-strong);
}
.inline-error [data-lucide] { inline-size: 18px; block-size: 18px; flex-shrink: 0; margin-block-start: 2px; }
.inline-error svg { color: var(--danger-text); }
.inline-error strong { display: block; font-weight: var(--fw-semibold); }
.inline-error-hint { display: block; margin-block-start: var(--space-1); font-size: var(--fs-100); color: var(--text-default); }

.loader { display: flex; align-items: center; justify-content: center; padding: var(--space-6); }
.spinner {
    width: 30px; height: 30px;
    border: 3px solid var(--border-subtle);
    border-top-color: var(--accent);
    border-radius: 50%;
    animation: spin 0.8s linear infinite;
}
.spinner-sm { width: 16px; height: 16px; border-width: 2px; }
.spinner-inline { display: inline-block; vertical-align: middle; }
@keyframes spin { to { transform: rotate(360deg); } }

/* ─── Modal ──────────────────────────────────────────────────────────────── */
.modal-overlay {
    position: fixed; inset: 0; z-index: var(--z-modal);
    background: var(--surface-scrim);
    backdrop-filter: blur(4px);
    display: flex; align-items: center; justify-content: center;
    padding: var(--space-5);
    animation: fadeIn var(--motion-base) var(--ease-standard);
}
.modal-content {
    width: 100%;
    max-width: 560px;
    max-height: calc(100dvh - var(--space-7));
    overflow-y: auto;
    padding: var(--space-6);
    /* The elevated glass: a deeper blur than a card, because a modal sits further
       from the content it covers. --elev-3 carries the depth. */
    backdrop-filter: blur(var(--blur-elevated)) saturate(180%);
    -webkit-backdrop-filter: blur(var(--blur-elevated)) saturate(180%);
    background: var(--surface-overlay);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-lg);
    box-shadow: var(--elev-3);
    animation: slideUp var(--motion-slow) var(--ease-standard);
}
.modal-header {
    display: flex; align-items: center; justify-content: space-between;
    gap: var(--space-3);
    margin-block-end: var(--space-5);
}
.modal-title { font-size: var(--fs-400); }
.modal-close {
    display: inline-flex; align-items: center; justify-content: center;
    min-width: var(--tap-target); min-height: var(--tap-target);
    border-radius: var(--radius-sm);
    color: var(--text-muted);
}
.modal-close:hover:not(:disabled) { background: var(--surface-glass-hover); color: var(--text-strong); }
.modal-close:active:not(:disabled) { background: color-mix(in srgb, var(--text-strong) 14%, transparent); }
/* Nothing disables the close button today, but `Admin.confirm` now keeps the
   dialog alive while a destructive action is in flight — and a dialog that
   cannot be dismissed mid-request is the case this exists for. */
.modal-close:disabled { opacity: 0.35; cursor: not-allowed; }
.modal-close [data-lucide] { inline-size: var(--icon-lg); block-size: var(--icon-lg); }
.modal-actions {
    display: flex; justify-content: flex-end; gap: var(--space-2);
    margin-block-start: var(--space-5);
    flex-wrap: wrap;
}
.modal-body-text { font-size: var(--fs-200); color: var(--text-default); margin-block-end: var(--space-3); }

@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
@keyframes slideUp { from { opacity: 0; transform: translateY(16px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) {
    .modal-overlay, .modal-content { animation: none; }
}

/* ─── Toast ──────────────────────────────────────────────────────────────── */
.toast-container {
    position: fixed;
    inset-block-end: var(--space-5);
    inset-inline-end: var(--space-5);
    z-index: var(--z-toast);
    display: flex; flex-direction: column; gap: var(--space-2);
    max-width: min(420px, calc(100vw - var(--space-6)));
}
.toast {
    display: flex; align-items: center; gap: var(--space-3);
    padding: var(--space-3) var(--space-4);
    border-radius: var(--radius-sm);
    font-size: var(--fs-200);
    background: var(--surface-overlay);
    backdrop-filter: blur(var(--blur-glass)) saturate(180%);
    -webkit-backdrop-filter: blur(var(--blur-glass)) saturate(180%);
    border: 1px solid var(--border-subtle);
    box-shadow: var(--elev-3);
    color: var(--text-strong);
    animation: toastIn var(--motion-slow) var(--ease-standard);
    transition: opacity var(--motion-slow) var(--ease-exit),
                transform var(--motion-slow) var(--ease-exit);
}
/* The exit lives here with the entrance, instead of an inline opacity written
   from JS, so both collapse to 1ms together under reduced motion. */
.toast.is-leaving { opacity: 0; transform: scale(0.96); }
.toast [data-lucide] { inline-size: 18px; block-size: 18px; flex-shrink: 0; }
.toast.success { border-color: var(--border-success); }
.toast.success svg { color: var(--success-text); }
.toast.error { border-color: var(--border-danger); }
.toast.error svg { color: var(--danger-text); }
/* Physical exception 2/3: toasts slide in from the inline end. */
@keyframes toastIn { from { opacity: 0; transform: translateX(30px); } to { opacity: 1; transform: none; } }
html[dir='rtl'] .toast { animation-name: toastInRtl; }
@keyframes toastInRtl { from { opacity: 0; transform: translateX(-30px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) { .toast { animation: none; } }

/* ─── Login ──────────────────────────────────────────────────────────────── */
.login-screen {
    min-height: 100vh; min-height: 100dvh;
    display: flex; align-items: center; justify-content: center;
    background: var(--surface-page);
    position: relative; overflow: hidden;
    padding: var(--space-5);
}
.login-glow {
    position: fixed;
    inset-block-start: 50%; inset-inline-start: 50%;
    transform: translate(-50%, -50%);
    width: min(600px, 120vw); height: min(600px, 120vw);
    background: radial-gradient(circle, var(--accent-soft) 0%, transparent 70%);
    pointer-events: none;
}
/* The glow used to breathe on a 4s loop. It marked no state and explained no
   change — it was motion for its own sake on the one screen where the operator
   is trying to type a password. The gradient stays as part of the identity;
   the animation is gone, which also retires its reduced-motion escape hatch. */
.login-container { position: relative; z-index: 1; width: 100%; max-width: 420px; }
.login-card { padding: var(--space-6); text-align: center; background: var(--surface-raised); position: relative; }
/* The language toggle on the login card: same control as the header's, pinned to the
   card's top inline-end corner so the one-field form stays one field. */
.login-lang { position: absolute; inset-block-start: var(--space-3); inset-inline-end: var(--space-3); }
.login-logo { margin-block-end: var(--space-6); }
.login-mark {
    width: 62px; height: 62px;
    background: var(--gradient-brand);
    border-radius: var(--radius-lg);
    display: inline-flex; align-items: center; justify-content: center;
    margin-block-end: var(--space-4);
    box-shadow: var(--elev-glow);
}
.login-mark [data-lucide] { inline-size: 30px; block-size: 30px; }
.login-mark svg { color: var(--text-on-accent); }
.login-title { font-size: var(--fs-600); color: var(--text-strong); }
.login-subtitle { color: var(--text-muted); font-size: var(--fs-200); margin-block-start: var(--space-2); }
.login-form { display: flex; flex-direction: column; gap: var(--space-4); text-align: start; }
.login-error { color: var(--danger-text); font-size: var(--fs-200); text-align: center; }
.login-alt-link {
    font-size: var(--fs-100);
    color: var(--text-muted);
    text-decoration: underline;
    text-underline-offset: 3px;
    min-height: var(--tap-target);
    align-self: center;
}
.login-alt-link:hover { color: var(--text-default); }

/* Input with a leading icon. The icon is taken out of flow and overlaid on the field. Its
   size AND its position are written on `[data-lucide]`, so the `<i>` placeholder sits in
   exactly the box the `<svg>` will: an in-flow placeholder would push the input sideways
   for the moment before Lucide lands. `.studio-lesson-search` is the same control. */
.input-affix, .studio-lesson-search { position: relative; display: flex; align-items: center; }
.input-affix [data-lucide], .studio-lesson-search > [data-lucide] {
    position: absolute;
    inset-inline-start: var(--space-3);
    inline-size: 18px; block-size: 18px;
    pointer-events: none;
}
.input-affix svg, .studio-lesson-search > svg { color: var(--text-muted); }
.input-affix .field { padding-inline-start: calc(var(--space-3) * 2 + 18px); }

/* ─── Stat tiles ─────────────────────────────────────────────────────────── */
.stats-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
    gap: var(--space-4);
    margin-block-end: var(--space-5);
}
.stat-card { padding: var(--space-5); position: relative; overflow: hidden; }
.stat-header {
    display: flex; align-items: center; justify-content: space-between;
    gap: var(--space-3);
    margin-block-end: var(--space-3);
}
.stat-label { font-size: var(--fs-200); color: var(--text-default); font-weight: var(--fw-medium); }
/* Neutral by default. The semantic variants below still exist for the places
   where the colour IS the message (a missing-scopes warning in Settings), but
   the stat tiles no longer use them: `.stat-icon.warning` on "People reached"
   painted a counter in the colour this design system uses for "something needs
   your attention" everywhere else, which made the token mean nothing. */
.stat-icon {
    width: 36px; height: 36px;
    border-radius: var(--radius-xs);
    display: flex; align-items: center; justify-content: center;
    flex-shrink: 0;
    background: var(--surface-glass-hover);
    color: var(--text-muted);
}
.stat-icon [data-lucide] { inline-size: 18px; block-size: 18px; }
.stat-icon.accent { background: var(--accent-soft); color: var(--accent-text); }
.stat-icon.success { background: var(--success-soft); color: var(--success-text); }
.stat-icon.danger { background: var(--danger-soft); color: var(--danger-text); }
.stat-icon.warning { background: var(--warning-soft); color: var(--warning-text); }
.stat-value {
    font-size: var(--fs-600);
    font-weight: var(--fw-bold);
    color: var(--text-strong);
    line-height: 1.15;
    font-variant-numeric: tabular-nums;
    /* Numbers are Latin even in the Arabic UI, but `direction: ltr` here also flipped
       `text-align: start` to mean LEFT — putting the card's biggest number on the far side
       from its own label. Isolate the Latin run without redirecting the box: `plaintext`
       keeps a trailing "%" or "/" attached while the block stays on the reading edge. */
    unicode-bidi: plaintext;
    text-align: start;
}
.stat-sub { font-size: var(--fs-100); color: var(--text-muted); margin-block-start: var(--space-2); }

/* ─── Briefing (Overview) ────────────────────────────────────────────────── */
.briefing { margin-block-end: var(--space-5); }
.briefing-head {
    display: flex; align-items: baseline; gap: var(--space-3); flex-wrap: wrap;
    margin-block-end: var(--space-3);
}
.briefing-head h2 { font-size: var(--fs-400); }
.briefing-date { font-size: var(--fs-100); color: var(--text-muted); }
.briefing-list { display: grid; gap: var(--space-3); grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); }
.alert-card {
    display: flex; gap: var(--space-3); align-items: flex-start;
    padding: var(--space-4);
    border-radius: var(--radius-md);
    border: 1px solid var(--border-subtle);
    /* A tint, not glass: nothing is blurred behind it, so a translucent glass token read matte. */
    background: var(--surface-glass);
    border-inline-start: 3px solid var(--text-muted);
}
.alert-card .alert-icon {
    width: 34px; height: 34px; flex-shrink: 0;
    border-radius: var(--radius-xs);
    display: flex; align-items: center; justify-content: center;
}
.alert-card .alert-icon [data-lucide] { inline-size: var(--icon-md); block-size: var(--icon-md); }
.alert-card h3 { font-size: var(--fs-200); }
.alert-card p { font-size: var(--fs-100); color: var(--text-muted); margin-block: var(--space-1) var(--space-2); }
.alert-danger { border-inline-start-color: var(--danger); }
.alert-danger .alert-icon { background: var(--danger-soft); color: var(--danger-text); }
.alert-warning { border-inline-start-color: var(--warning); }
.alert-warning .alert-icon { background: var(--warning-soft); color: var(--warning-text); }
.alert-info { border-inline-start-color: var(--info); }
.alert-info .alert-icon { background: var(--info-soft); color: var(--info-text); }
.alert-success { border-inline-start-color: var(--success); }
.alert-success .alert-icon { background: var(--success-soft); color: var(--success-text); }
.alert-link {
    font-size: var(--fs-100);
    font-weight: var(--fw-semibold);
    color: var(--accent-text);
    display: inline-flex; align-items: center; gap: var(--space-1);
    min-height: var(--tap-target);
}
.alert-link:hover { text-decoration: underline; }
.alert-link [data-lucide] { inline-size: var(--icon-sm); block-size: var(--icon-sm); }

/* ─── Charts ─────────────────────────────────────────────────────────────── */
.chart-grid { display: grid; grid-template-columns: 2fr 1fr; gap: var(--space-4); margin-block-end: var(--space-5); }
.chart-grid--even { grid-template-columns: 1fr 1fr; }
.chart-card { padding: var(--space-5); min-width: 0; }
.chart-card-header {
    display: flex; align-items: center; justify-content: space-between;
    gap: var(--space-3);
    margin-block-end: var(--space-4);
}
.chart-card-title { font-size: var(--fs-400); font-weight: var(--fw-semibold); color: var(--text-strong); }
/* `.chart-wrapper` reserved 260px for a Chart.js canvas. The SVG charts are a
   54px strip or a 14px bar plus a two-row legend, so the placeholder matches
   that instead — a skeleton taller than its content jumps the page when data
   arrives. */
.chart-skel { position: relative; height: 92px; }

.platform-bar {
    block-size: 10px;
    display: flex;
    margin-block-end: var(--space-5);
}
.platform-bar-ig { background: var(--brand-instagram); }
.platform-bar-fb { background: var(--brand-facebook); }
.platform-legend { display: flex; flex-direction: column; gap: var(--space-2); margin-block-end: var(--space-4); }
.platform-legend-row { display: flex; justify-content: space-between; gap: var(--space-3); font-size: var(--fs-200); }
.platform-totals { display: flex; justify-content: space-around; text-align: center; gap: var(--space-4); }
.platform-total-value { font-size: var(--fs-500); font-weight: var(--fw-bold); color: var(--text-strong); font-variant-numeric: tabular-nums; }
.platform-total-label { font-size: var(--fs-100); color: var(--text-muted); }
.platform-totals-divider { inline-size: 1px; background: var(--border-subtle); }

/* ─── Campaign cards ─────────────────────────────────────────────────────── */
.card-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(330px, 1fr)); gap: var(--space-4); }
.campaign-card { padding: var(--space-5); display: flex; flex-direction: column; gap: var(--space-3); }
.campaign-card.is-paused { opacity: 0.68; }
.campaign-card-head { display: flex; justify-content: space-between; align-items: flex-start; gap: var(--space-3); }
.keyword-list { display: flex; flex-wrap: wrap; gap: var(--space-2); min-width: 0; }
/* `.campaign-state` — the "Active"/"Paused" caption that sat immediately next
   to the toggle controlling it — is gone. The switch already shows the state
   and announces it natively, and `.campaign-card.is-paused` dims the whole
   card; a third copy of one bit of information is not information. */
.template-preview {
    font-size: var(--fs-200);
    color: var(--text-default);
    padding: var(--space-3);
    max-height: 7.5em;
    overflow: hidden;
    position: relative;
}
.campaign-meta-line { font-size: var(--fs-100); color: var(--text-muted); }
.campaign-meta-line [data-lucide] { inline-size: var(--icon-sm); block-size: var(--icon-sm); flex-shrink: 0; }
.campaign-stats { display: flex; gap: var(--space-4); flex-wrap: wrap; }
.campaign-stat { display: flex; align-items: center; gap: var(--space-2); font-size: var(--fs-100); color: var(--text-default); }
.campaign-stat .dot { width: 7px; height: 7px; border-radius: 50%; flex-shrink: 0; }
.campaign-stat .dot.ok { background: var(--success); }
.campaign-stat .dot.bad { background: var(--danger); }
.card-actions { display: flex; gap: var(--space-2); flex-wrap: wrap; margin-block-start: auto; }

/* Keyword hazard inspector — the point-of-decision warning */
.match-inspector {
    margin-block-start: var(--space-3);
    padding: var(--space-3);
    border-radius: var(--radius-sm);
    border: 1px solid var(--border-subtle);
    background: var(--surface-sunken);
    font-size: var(--fs-100);
    line-height: var(--lh-normal);
}
.match-inspector h4 {
    font-size: var(--fs-100);
    display: flex; align-items: center; gap: var(--space-2);
    margin-block-end: var(--space-2);
}
.match-inspector h4 [data-lucide] { inline-size: var(--icon-sm); block-size: var(--icon-sm); }
.match-inspector p { color: var(--text-muted); }
.match-inspector.is-risky { border-color: var(--border-warning); background: var(--warning-soft); }
.match-inspector.is-risky h4 { color: var(--warning-text); }
.match-inspector.is-risky p { color: var(--text-default); }
.match-inspector.is-safe h4 { color: var(--success-text); }
.match-risk-list { list-style: none; display: flex; flex-direction: column; gap: var(--space-2); margin-block-start: var(--space-2); }
.match-risk-list li { color: var(--text-default); }
.match-example { color: var(--warning-text); font-weight: var(--fw-semibold); }

/* ─── Post picker ────────────────────────────────────────────────────────── */
.post-picker {
    margin-block-start: var(--space-2);
    max-height: 240px; overflow-y: auto;
    padding: var(--space-2);
}
.post-picker-item {
    display: flex; gap: var(--space-3); width: 100%;
    padding: var(--space-2);
    border-radius: var(--radius-xs);
    text-align: start;
    min-height: var(--tap-target);
    align-items: center;
    transition: background var(--motion-fast) var(--ease-standard);
}
.post-picker-item:hover { background: var(--surface-glass-hover); }
.post-picker-item img, .post-picker-thumb {
    width: 40px; height: 40px;
    object-fit: cover; flex-shrink: 0;
}
.post-picker-thumb [data-lucide] { inline-size: var(--icon-md); block-size: var(--icon-md); }
.post-picker-body { overflow: hidden; display: flex; flex-direction: column; min-width: 0; }
.post-picker-id { font-size: var(--fs-100); color: var(--accent-text); font-weight: var(--fw-semibold); }
.post-picker-caption { font-size: var(--fs-100); color: var(--text-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.post-picker-note { font-size: var(--fs-100); }
.post-picker-note.is-error { color: var(--danger-text); }

/* ─── Post cards ─────────────────────────────────────────────────────────── */
.post-card { padding: var(--space-5); display: flex; flex-direction: column; gap: var(--space-3); }
.post-card-head { display: flex; justify-content: space-between; align-items: flex-start; gap: var(--space-2); flex-wrap: wrap; }
.post-card-badges { display: flex; gap: var(--space-2); align-items: center; flex-wrap: wrap; }
.post-media-frame {
    width: 100%; height: 170px;
    background: var(--surface-sunken);
    display: flex; align-items: center; justify-content: center;
}
.post-media-frame img, .post-media-frame video { width: 100%; height: 100%; object-fit: cover; }
.post-card-meta { font-size: var(--fs-100); color: var(--text-muted); display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; }
.post-card-meta [data-lucide] { inline-size: var(--icon-sm); block-size: var(--icon-sm); flex-shrink: 0; }
/* A sentence rather than a fact: keep the icon beside the first line instead
   of letting the wrap push the text under it. */
.post-card-meta--note { flex-wrap: nowrap; align-items: flex-start; }
.post-card-meta--note [data-lucide] { margin-block-start: 3px; }
.post-card-meta--note > span { min-width: 0; }
.post-card-error {
    background: var(--danger-soft);
    border: 1px solid var(--border-danger);
    border-radius: var(--radius-xs);
    padding: var(--space-2) var(--space-3);
    font-size: var(--fs-100);
    color: var(--danger-text);
    overflow-wrap: anywhere;
}
.post-card-id { font-size: var(--fs-100); color: var(--text-muted); overflow-wrap: anywhere; }

.publish-error-panel {
    padding: var(--space-4);
    margin-block-end: var(--space-5);
    border: 1px solid var(--border-danger);
    background: var(--danger-soft);
    border-radius: var(--radius-md);
}
.publish-error-head { display: flex; align-items: center; gap: var(--space-3); margin-block-end: var(--space-2); }
.publish-error-head [data-lucide] { inline-size: 18px; block-size: 18px; }
.publish-error-head svg { color: var(--danger-text); }
.publish-error-head h3 { font-size: var(--fs-300); flex: 1; }
.publish-error-message { font-size: var(--fs-200); color: var(--text-strong); overflow-wrap: anywhere; }
.publish-error-hint { font-size: var(--fs-100); color: var(--text-default); margin-block-start: var(--space-2); }

/* ─── Cover / thumbnail step ─────────────────────────────────────────────── */
.cover-step {
    padding: var(--space-4);
    margin-block-end: var(--space-4);
}
.cover-step-head { display: flex; align-items: center; gap: var(--space-2); margin-block-end: var(--space-2); }
.cover-step-head [data-lucide] { inline-size: var(--icon-md); block-size: var(--icon-md); }
.cover-step-head svg { color: var(--accent-text); }
.cover-step-head h4 { font-size: var(--fs-200); }
.cover-step-why { font-size: var(--fs-100); color: var(--text-muted); margin-block-end: var(--space-4); }
.cover-compare { display: flex; gap: var(--space-3); margin-block-start: var(--space-4); }
.cover-tile { flex: 0 0 96px; }
.cover-tile-frame {
    width: 96px; height: 96px;
    background: #000;
    display: flex; align-items: center; justify-content: center;
    color: var(--text-muted);
}
.cover-tile-frame [data-lucide] { inline-size: var(--icon-lg); block-size: var(--icon-lg); }
.cover-tile-label { font-size: var(--fs-100); color: var(--text-muted); margin-block-start: var(--space-2); text-align: center; }
.cover-tile.is-chosen .cover-tile-frame { border-color: var(--success); box-shadow: 0 0 0 2px var(--success-soft); }
.cover-note { font-size: var(--fs-100); align-self: center; color: var(--text-default); flex: 1; }

/* Upload progress + preview */
.upload-progress {
    display: none;
    align-items: center; gap: var(--space-2);
    margin-block-start: var(--space-2);
    font-size: var(--fs-100); color: var(--text-default);
}
.upload-progress.is-active { display: flex; }
.media-preview {
    margin-block-start: var(--space-3);
    width: 100%; height: 170px;
    background: var(--surface-sunken);
    align-items: center; justify-content: center;
    display: none;
}
.media-preview.is-visible { display: flex; }
.media-preview img, .media-preview video { width: 100%; height: 100%; object-fit: cover; }

/* Schedule honesty block */
.schedule-truth {
    margin-block-start: var(--space-2);
    color: var(--text-default);
}
.schedule-truth [data-lucide] { inline-size: var(--icon-md); block-size: var(--icon-md); flex-shrink: 0; margin-block-start: 2px; }
.schedule-truth svg { color: var(--info-text); }
.schedule-actual { display: block; margin-block-start: var(--space-2); font-weight: var(--fw-semibold); color: var(--text-strong); }
.schedule-actual.is-warning { color: var(--warning-text); }

/* ─── TikTok ─────────────────────────────────────────────────────────────────
   The composer panel and the card's "it is in your inbox" step. Same quiet
   treatment as .schedule-truth: information, not an alarm. */
.tiktok-panel,
.tiktok-next {
    display: flex; flex-direction: column; gap: var(--space-2);
    padding: var(--space-3);
    margin-block: var(--space-2);
    border-radius: var(--radius-sm);
    background: var(--brand-tiktok-soft);
    border: 1px solid var(--brand-tiktok-soft);
    font-size: var(--fs-100);
    color: var(--text-default);
}
.tiktok-panel.hidden { display: none; }
.tiktok-panel .form-hint { margin: 0; }
.tiktok-account,
.tiktok-next-title { display: flex; align-items: center; gap: var(--space-2); margin: 0; color: var(--text-strong); }
.tiktok-next-body { margin: 0; }
.tiktok-account [data-lucide],
.tiktok-next-title [data-lucide] { inline-size: var(--icon-md); block-size: var(--icon-md); flex-shrink: 0; }
.tiktok-account { flex-wrap: wrap; }
.tiktok-avatar { width: 24px; height: 24px; border-radius: 50%; object-fit: cover; flex-shrink: 0; }

/* Direct Post: the controls TikTok's Content Sharing Guidelines require, in
   the same quiet panel. */
.tiktok-direct-title { margin: 0; font-size: var(--fs-200); font-weight: var(--fw-semibold); color: var(--text-strong); }
.tiktok-direct { display: flex; flex-direction: column; gap: var(--space-3); }
.tiktok-direct .form-label { margin-block-end: var(--space-1); }
.tiktok-subhead {
    margin: var(--space-2) 0 0;
    font-size: var(--fs-100); font-weight: var(--fw-semibold);
    color: var(--text-strong);
}
.tiktok-loading { display: flex; align-items: center; gap: var(--space-2); margin: 0; }
.tiktok-note {
    margin: 0;
    color: var(--text-default);
}
.tiktok-note [data-lucide] { inline-size: var(--icon-md); block-size: var(--icon-md); flex-shrink: 0; margin-block-start: 2px; }
.tiktok-note svg { color: var(--info-text); }
.tiktok-disclosure {
    display: flex; flex-direction: column; gap: var(--space-3);
    padding-inline-start: var(--space-4);
    border-inline-start: 2px solid var(--border-subtle);
}
.tiktok-disclosure .form-hint { margin: 0; }
.tiktok-disclosure .form-hint [data-lucide] { inline-size: var(--icon-sm); block-size: var(--icon-sm); vertical-align: -2px; }
.tiktok-label-preview { margin: 0; font-weight: var(--fw-semibold); color: var(--text-strong); }
/* Consent + "By posting, you agree to …", directly above the submit button. */
.tiktok-consent { display: flex; flex-direction: column; gap: var(--space-2); margin-block: var(--space-4) var(--space-2); }
.tiktok-declaration { margin: 0; font-size: var(--fs-100); color: var(--text-default); line-height: var(--lh-normal); }
.tiktok-declaration a { color: var(--accent-text); text-decoration: underline; text-underline-offset: 2px; }
.tiktok-declaration a:hover { text-decoration-thickness: 2px; }
.tiktok-avatar-lg { width: 44px; height: 44px; border-radius: 50%; object-fit: cover; flex-shrink: 0; }
/* Per-post delivery choice, until TikTok approves the app. */
.tiktok-delivery { border: 0; margin: 0 0 var(--space-3); padding: 0; display: grid; gap: var(--space-2); }
.tiktok-delivery legend { margin-block-end: var(--space-2); }
.tiktok-delivery-option {
    display: flex; gap: var(--space-2); align-items: flex-start; cursor: pointer;
    padding: var(--space-2) var(--space-3); border-radius: var(--radius-sm);
    border: 1px solid var(--brand-tiktok-soft);
}
.tiktok-delivery-option input { margin-block-start: 3px; flex-shrink: 0; }
.tiktok-delivery-option .form-hint { margin: 0; }
.tiktok-manual { display: grid; gap: var(--space-2); }
.tiktok-manual .form-hint { margin: 0; }
.tiktok-register { display: grid; gap: var(--space-3); margin: 0; padding: 0; list-style: none; }
/* Label on its own line, then the value with its copy button beside it — the
   values are long URLs, and a label sharing their line pushed each one onto a
   different wrap. */
.tiktok-register li {
    display: grid; grid-template-columns: minmax(0, 1fr) auto;
    align-items: center; column-gap: var(--space-2); row-gap: var(--space-1);
}
.tiktok-register li .form-label { grid-column: 1 / -1; margin: 0; }
.tiktok-register code { overflow-wrap: anywhere; font-size: var(--fs-100); }
.tiktok-app-summary {
    display: flex; align-items: center; gap: var(--space-2);
    min-block-size: var(--tap-target);
    cursor: pointer; list-style: none;
}
.tiktok-app-summary::-webkit-details-marker { display: none; }
.tiktok-app-summary [data-lucide] { inline-size: var(--icon-md); block-size: var(--icon-md); }
.tiktok-app-summary svg { transition: transform var(--motion-fast) var(--ease-standard); }
details[open] > .tiktok-app-summary svg { transform: rotate(180deg); }

/* ─── Carousel slides ────────────────────────────────────────────────────────
   The composer's multi-image picker (the carousel itself, and the optional 9:16
   set for TikTok) and the post card's strip. Slide order is reading order: the
   grid flows from the inline start, so slide 1 sits on the right in Arabic,
   and the move buttons' arrows — mirrored under RTL — point where the slide
   will go. Thumbnails sit on `--surface-solid`: an image frame is opaque, per
   DESIGN.md, or the photo reads washed out through the glass. */

/* A label with a count at the far end: the slide picker and the TikTok title. */
.field-head {
    display: flex; align-items: baseline; justify-content: space-between;
    gap: var(--space-3);
    margin-block-end: var(--space-2);
}
.field-head .form-label { margin: 0; }
.field-count {
    font-size: var(--fs-100);
    font-weight: var(--fw-semibold);
    color: var(--text-muted);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}
.field-count.is-warning { color: var(--warning-text); }

.slide-picker { display: flex; flex-direction: column; gap: var(--space-2); }
.slide-picker .field-head { margin: 0; }
.slide-picker .form-hint { margin: 0; }
.slide-rule { margin: 0; font-size: var(--fs-100); color: var(--warning-text); }
.slide-progress { margin: 0; font-size: var(--fs-100); color: var(--text-default); }
.slide-rule:empty, .slide-progress:empty { display: none; }

.slide-strip {
    list-style: none; margin: 0; padding: 0;
    display: grid;
    /* 140px is three 44px buttons and their two gaps: every tile keeps a full
       tap target for earlier, later and remove, at any modal width. */
    grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
    gap: var(--space-3);
}
.slide-strip:empty { display: none; }
.slide-tile { display: flex; flex-direction: column; gap: var(--space-1); min-inline-size: 0; }
.slide-thumb {
    position: relative;
    aspect-ratio: 1;
}
.slide-thumb > [data-lucide] { inline-size: var(--icon-xl); block-size: var(--icon-xl); }
.slide-num {
    position: absolute;
    inset-block-start: var(--space-1);
    inset-inline-start: var(--space-1);
    box-shadow: var(--elev-1);
}
/* In-flight and failed: a solid chip along the bottom edge, never text laid
   straight over a photo, whose colours nobody can promise contrast against. */
.slide-state {
    position: absolute;
    inset-inline: var(--space-1);
    inset-block-end: var(--space-1);
    display: flex; align-items: center; justify-content: center; gap: var(--space-1);
    padding: var(--space-1) var(--space-2);
    border-radius: var(--radius-xs);
    background: var(--surface-solid);
    color: var(--text-default);
    font-size: var(--fs-100);
    box-shadow: var(--elev-1);
}
.slide-state [data-lucide] { inline-size: var(--icon-sm); block-size: var(--icon-sm); flex-shrink: 0; }
.slide-tile.is-working .slide-thumb img { opacity: 0.6; }
.slide-tile.is-failed .slide-thumb { border-color: var(--border-danger); }
.slide-state--failed { color: var(--danger-text); }
/* The failed chip is the retry itself, at a full tap target. */
.slide-retry {
    min-block-size: var(--tap-target);
    color: var(--danger-text);
    font-weight: var(--fw-semibold);
    cursor: pointer;
    transition: background var(--motion-fast) var(--ease-standard);
}
.slide-retry:hover { background: color-mix(in srgb, var(--danger) 12%, var(--surface-solid)); }
.slide-actions { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--space-1); }
.slide-actions .icon-btn { inline-size: 100%; }

/* TikTok's own 9:16 set, collapsed under the carousel until asked for. */
.slide-alt > summary { min-block-size: var(--tap-target); margin: 0; }
.slide-alt > summary .field-count { margin-inline-start: auto; }
.slide-alt[open] > summary { margin-block-end: var(--space-2); }
.slide-alt > .form-hint { margin-block: 0 var(--space-3); }

/* On a post card: the first four slides and "+N", at one cell size whatever
   the count, so two cards side by side compare at a glance. */
.post-slides {
    list-style: none; margin: 0; padding: 0;
    display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: var(--space-2);
}
.post-slide {
    aspect-ratio: 1;
    background: var(--surface-solid);
}
.post-slide-more {
    display: flex; align-items: center; justify-content: center;
    background: var(--surface-glass-hover);
    color: var(--text-strong);
    font-size: var(--fs-200);
    font-weight: var(--fw-semibold);
    font-variant-numeric: tabular-nums;
}
/* The photo kit's one-at-a-time downloads, numbered in slide order. */
.slide-downloads { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: var(--space-2); }

/* ─── Inbox ──────────────────────────────────────────────────────────────── */
.inbox-layout {
    display: grid;
    grid-template-columns: minmax(280px, 340px) 1fr;
    gap: var(--space-4);
    height: calc(100dvh - var(--header-height) - var(--space-7));
    min-height: 460px;
    overflow: hidden;
}
.inbox-sidebar { display: flex; flex-direction: column; height: 100%; overflow: hidden; }
.inbox-search {
    padding: var(--space-3);
    border-block-end: 1px solid var(--border-subtle);
}
.threads-list {
    flex: 1; overflow-y: auto;
    padding: var(--space-2);
    display: flex; flex-direction: column; gap: var(--space-1);
}
.thread-item {
    width: 100%;
    display: block;
    text-align: start;
    padding: var(--space-3);
    min-height: var(--tap-target);
    border-radius: var(--radius-md);
    border: 1px solid transparent;
    transition: background var(--motion-fast) var(--ease-standard);
    /* A busy account has hundreds of these in a scroller. `auto` skips style,
       layout and paint for the rows that are off-screen, and the `auto`
       keyword on contain-intrinsic-size makes the browser remember each row's
       real height once it has been rendered once, so the scrollbar settles
       instead of jumping. Rows stay focusable and findable — this is not
       `hidden`. Deliberately NOT applied to .chat-messages, whose append logic
       reads scrollHeight/scrollTop to decide whether to stick to the bottom;
       estimated heights would make that decision wrong. */
    content-visibility: auto;
    contain-intrinsic-size: auto 84px;
}
.thread-item:hover { background: var(--surface-glass-hover); }
.thread-item.active { background: var(--accent-soft); border-color: color-mix(in srgb, var(--accent) 30%, transparent); }
.thread-top { display: flex; justify-content: space-between; align-items: baseline; gap: var(--space-2); }
.thread-name { font-weight: var(--fw-semibold); font-size: var(--fs-200); color: var(--text-strong); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.thread-time { font-size: var(--fs-100); color: var(--text-muted); flex-shrink: 0; font-variant-numeric: tabular-nums; }
.thread-preview {
    font-size: var(--fs-100);
    color: var(--text-default);
    margin-block: var(--space-1);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    unicode-bidi: plaintext;
}
.thread-badges { display: flex; gap: var(--space-2); flex-wrap: wrap; }
.empty-threads { font-size: var(--fs-200); }

.chat-pane { display: flex; flex-direction: column; height: 100%; overflow: hidden; }
.chat-empty { flex: 1; }
.chat-empty [data-lucide] { inline-size: 46px; block-size: 46px; }
.chat-empty svg { color: var(--text-muted); }
.chat-empty h3 { font-size: var(--fs-400); }
.chat-empty p { font-size: var(--fs-200); color: var(--text-muted); max-width: 40ch; }

.chat-header {
    padding: var(--space-3) var(--space-4);
    border-block-end: 1px solid var(--border-subtle);
    display: flex; align-items: center; gap: var(--space-3);
}
.chat-header h3 { font-size: var(--fs-300); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.chat-header-info { flex: 1; min-width: 0; }
.chat-sub { font-size: var(--fs-100); color: var(--text-muted); }
.chat-header-actions { display: flex; align-items: center; gap: var(--space-2); flex-shrink: 0; }
.chat-back { display: none; }

.chat-messages {
    flex: 1; overflow-y: auto;
    padding: var(--space-4);
    display: flex; flex-direction: column; gap: var(--space-3);
    scroll-behavior: smooth;
}
@media (prefers-reduced-motion: reduce) { .chat-messages { scroll-behavior: auto; } }
.message-row { display: flex; width: 100%; }
/* flex-start / flex-end are writing-mode aware: inbound sits at the reading
   edge and outbound opposite it, in both directions, with no override. */
.row-inbound { justify-content: flex-start; }
.row-outbound { justify-content: flex-end; }
.message-bubble {
    max-width: min(68ch, 78%);
    padding: var(--space-3) var(--space-4);
    border-radius: var(--radius-md);
    font-size: var(--fs-200);
    line-height: var(--lh-loose);
    display: flex; flex-direction: column; gap: var(--space-2);
    unicode-bidi: plaintext;
    overflow-wrap: anywhere;
}
.bubble-inbound {
    background: var(--surface-sunken);
    border: 1px solid var(--border-subtle);
    color: var(--text-strong);
    border-start-start-radius: var(--radius-xs);
}
.bubble-outbound {
    background: var(--gradient-brand);
    color: var(--text-on-accent);
    border-start-end-radius: var(--radius-xs);
}
/* `opacity: 0.7` measured 3.02:1 on the outbound gradient. An explicit mix is contrast you
   can check. */
.message-time { font-size: var(--fs-100); color: color-mix(in srgb, currentColor 78%, transparent); align-self: flex-end; font-variant-numeric: tabular-nums; }
/* A reply that has left the composer but not yet the server. Dimmed, because
   the one thing it must not do is look like a message that was delivered.
   It is removed on both outcomes: replaced by the canonical row on success,
   and taken away with the draft restored on failure. */
.message-bubble.is-pending { opacity: 0.55; }

.chat-composer {
    padding: var(--space-3) var(--space-4);
    border-block-start: 1px solid var(--border-subtle);
}
.chat-composer form { display: flex; gap: var(--space-2); align-items: flex-end; }
.chat-composer textarea {
    flex: 1;
    background: var(--surface-sunken);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-sm);
    padding: var(--space-3);
    color: var(--text-strong);
    font-size: var(--fs-200);
    line-height: var(--lh-normal);
    resize: none;
    outline: 2px solid transparent; /* not `none`: still renders in forced-colors mode */
    min-height: var(--tap-target);
    max-height: 140px;
    unicode-bidi: plaintext;
}
.chat-composer textarea:focus { border-color: var(--accent); box-shadow: 0 0 0 var(--ring) var(--accent-ring); }

/* Structured message content */
.msg-quick-replies { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.qr-pill {
    padding: var(--space-1) var(--space-3);
    border-radius: var(--radius-pill);
    background: var(--surface-glass);
    border: 1px solid var(--border-subtle);
    font-size: var(--fs-100);
    color: var(--text-default);
}
.msg-carousel { display: flex; gap: var(--space-3); overflow-x: auto; padding-block-end: var(--space-2); width: 100%; }
.carousel-card {
    flex: 0 0 190px;
    border-radius: var(--radius-sm);
    overflow: hidden;
    display: flex; flex-direction: column;
    /* Holds an image; opaque so the media is not tinted by what is behind it. */
    background: var(--surface-solid);
    border: 1px solid var(--border-subtle);
}
.carousel-card-img { width: 100%; height: 100px; object-fit: cover; }
.carousel-card-body { padding: var(--space-3); display: flex; flex-direction: column; gap: var(--space-1); flex: 1; }
.carousel-card-title { font-size: var(--fs-200); font-weight: var(--fw-semibold); color: var(--text-strong); }
.carousel-card-desc { font-size: var(--fs-100); color: var(--text-muted); flex: 1; }
/* NOT a control, despite the name — `inbox.js` renders it as a <span>. It is a
   PICTURE of the quick-reply button Meta drew in the customer's DM, inside a
   record of a message that has already been sent. It has no hover, press or
   disabled state on purpose: every one of those would be the interface claiming
   something can be clicked when nothing is listening. Same for
   `.sandbox-card-btn` below. If either is ever promoted to a real <button>, the
   states belong here at that point and not before. */
.carousel-btn {
    padding: var(--space-1);
    border-radius: var(--radius-xs);
    background: var(--surface-glass-hover);
    border: 1px solid var(--border-subtle);
    font-size: var(--fs-100);
    text-align: center;
    color: var(--accent-text);
}

/* ─── AI settings ────────────────────────────────────────────────────────── */
.ai-grid {
    display: grid;
    grid-template-columns: 1.15fr 1fr;
    gap: var(--space-4);
    align-items: start;
}
.ai-panel { padding: var(--space-5); }
.ai-sandbox { padding: var(--space-5); display: flex; flex-direction: column; min-height: 520px; }
.panel-head { display: flex; align-items: center; gap: var(--space-3); margin-block-end: var(--space-4); }
.panel-head [data-lucide] { inline-size: var(--icon-lg); block-size: var(--icon-lg); }
.panel-head svg { color: var(--accent-text); }
.panel-head h3 { font-size: var(--fs-400); }
.panel-desc { font-size: var(--fs-200); color: var(--text-muted); margin-block-end: var(--space-4); }
.field-desc { display: block; font-size: var(--fs-100); color: var(--text-muted); margin-block-end: var(--space-2); }

.sandbox-shell {
    flex: 1;
    display: flex; flex-direction: column;
    overflow: hidden;
    min-height: 320px;
}
.sandbox-messages { flex: 1; overflow-y: auto; padding: var(--space-4); display: flex; flex-direction: column; gap: var(--space-3); }
.sandbox-welcome { margin-block-start: var(--space-5); }
.bot-avatar {
    width: 44px; height: 44px;
    background: var(--accent-soft);
    border-radius: 50%;
    display: flex; align-items: center; justify-content: center;
    color: var(--accent-text);
}
.bot-avatar-sm {
    width: 26px; height: 26px; flex-shrink: 0;
    background: var(--accent-soft);
    border-radius: 50%;
    display: flex; align-items: center; justify-content: center;
    color: var(--accent-text);
    margin-inline-end: var(--space-2);
}
.sandbox-welcome h4 { font-size: var(--fs-300); }
.sandbox-welcome p { font-size: var(--fs-100); color: var(--text-muted); max-width: 34ch; }
.sandbox-msg { display: flex; width: 100%; }
.sandbox-msg.msg-user { justify-content: flex-end; }
.sandbox-msg.msg-bot, .sandbox-msg.msg-error { justify-content: flex-start; align-items: flex-start; }
.sandbox-bubble {
    max-width: 84%;
    padding: var(--space-3);
    border-radius: var(--radius-sm);
    font-size: var(--fs-200);
    line-height: var(--lh-loose);
    unicode-bidi: plaintext;
    overflow-wrap: anywhere;
}
.bubble-user { background: var(--gradient-brand); color: var(--text-on-accent); border-start-end-radius: var(--radius-xs); }
.bubble-bot {
    /* A tint, not glass: a bubble sits IN the thread, and blurring the messages
       behind each bubble is both wrong and expensive at scroll. */
    background: var(--surface-glass);
    border: 1px solid var(--border-subtle);
    color: var(--text-strong);
    border-start-start-radius: var(--radius-xs);
    display: flex; flex-direction: column; gap: var(--space-2);
}
.bubble-error { background: var(--danger-soft); border: 1px solid var(--border-danger); color: var(--danger-text); }
.sandbox-composer { padding: var(--space-3); border-block-start: 1px solid var(--border-subtle); }
.sandbox-composer form { display: flex; gap: var(--space-2); }
.typing-dots { display: flex; align-items: center; gap: var(--space-1); }
.typing-dots .dot { width: 6px; height: 6px; background: var(--text-muted); border-radius: 50%; opacity: 0.35; animation: typingPulse 1s infinite alternate; }
.typing-dots .dot:nth-child(2) { animation-delay: 0.2s; }
.typing-dots .dot:nth-child(3) { animation-delay: 0.4s; }
@keyframes typingPulse { from { opacity: 0.35; transform: none; } to { opacity: 1; transform: translateY(-2px); } }
@media (prefers-reduced-motion: reduce) { .typing-dots .dot { animation: none; opacity: 0.7; } }
.sandbox-carousel { display: flex; gap: var(--space-2); overflow-x: auto; width: 100%; }
.sandbox-card { flex: 0 0 160px; border-radius: var(--radius-xs); overflow: hidden; display: flex; flex-direction: column; background: var(--surface-sunken); border: 1px solid var(--border-subtle); }
.sandbox-card-img { width: 100%; height: 72px; object-fit: cover; }
.sandbox-card-body { padding: var(--space-2); display: flex; flex-direction: column; gap: var(--space-1); flex: 1; }
.sandbox-card-title { font-size: var(--fs-100); font-weight: var(--fw-semibold); color: var(--text-strong); }
.sandbox-card-subtitle { font-size: var(--fs-100); color: var(--text-muted); flex: 1; }
/* A <span>, like `.carousel-btn` — see the note there. Stateless on purpose. */
.sandbox-card-btn { padding: var(--space-1); border-radius: var(--radius-xs); background: var(--surface-glass-hover); border: 1px solid var(--border-subtle); font-size: var(--fs-100); text-align: center; color: var(--accent-text); }

/* ─── Filters ────────────────────────────────────────────────────────────── */
.filter-bar { display: flex; gap: var(--space-2); align-items: center; flex-wrap: wrap; }
.filter-bar .field, .filter-bar .select { width: auto; min-width: 150px; }
.filter-bar .field[type='search'] { min-width: 210px; }

.pagination { display: flex; align-items: center; justify-content: center; gap: var(--space-2); margin-block-start: var(--space-4); }
.page-btn {
    padding: var(--space-2) var(--space-4);
    min-height: var(--tap-target);
    border-radius: var(--radius-sm);
    font-size: var(--fs-200);
    background: var(--surface-glass);
    border: 1px solid var(--border-subtle);
    color: var(--text-default);
}
.page-btn:hover:not(:disabled) { border-color: var(--border-strong); color: var(--text-strong); }
.page-btn:disabled { opacity: 0.35; cursor: not-allowed; }
.page-position { font-size: var(--fs-200); color: var(--text-default); }

/* ─── Activity cards (the mobile form of the log table) ──────────────────── */
.log-cards { display: none; flex-direction: column; gap: var(--space-3); }
.log-card {
    padding: var(--space-4);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    /* A tint, not glass: nothing is blurred behind it, so a translucent glass token read matte. */
    background: var(--surface-glass);
    display: flex; flex-direction: column; gap: var(--space-2);
}
.log-card-head { display: flex; justify-content: space-between; align-items: center; gap: var(--space-2); }

/* ─── Settings ───────────────────────────────────────────────────────────── */
.settings-card { padding: var(--space-5); }
.settings-block { padding-block-start: var(--space-5); margin-block-start: var(--space-5); border-block-start: 1px solid var(--border-subtle); }
.token-status { display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap; margin-block-end: var(--space-4); }
.token-info { font-size: var(--fs-200); color: var(--text-default); display: flex; gap: var(--space-2); flex-wrap: wrap; }
.token-info strong { color: var(--text-strong); font-weight: var(--fw-medium); }
.scope-list { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.warning-card {
    border-inline-start: 4px solid var(--warning);
    background: var(--warning-soft);
    padding: var(--space-5);
    margin-block-end: var(--space-5);
    border-radius: var(--radius-md);
}
.warning-card-title { font-size: var(--fs-300); color: var(--warning-text); margin-block-end: var(--space-2); }
.warning-card-list { font-size: var(--fs-100); color: var(--text-default); padding-inline-start: var(--space-5); }
.warning-card-list strong { color: var(--text-strong); }

/* ─── Tenants / users ────────────────────────────────────────────────────── */
.tenants-table { min-width: 940px; }
.tenants-table .col-health { width: 150px; }
.tenant-name-cell { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; }
.tenant-name { font-weight: var(--fw-semibold); color: var(--text-strong); }
.tenant-page-ids { display: flex; gap: var(--space-3); flex-wrap: wrap; margin-block-start: var(--space-1); font-size: var(--fs-100); color: var(--text-muted); }
.tenant-row-inactive td { opacity: var(--opacity-off); }
.count-ok { color: var(--success-text); font-weight: var(--fw-semibold); }
.count-bad { color: var(--danger-text); font-weight: var(--fw-semibold); margin-inline-start: var(--space-2); }
.count-bad.count-zero { color: var(--text-muted); font-weight: var(--fw-regular); }
.dm-meter { display: flex; flex-direction: column; gap: var(--space-1); min-width: 100px; }
.dm-meter-text { font-size: var(--fs-100); color: var(--text-default); white-space: nowrap; font-variant-numeric: tabular-nums; }
.dm-meter-track { display: block; block-size: 6px; }
.dm-meter-fill { display: block; block-size: 100%; border-radius: var(--radius-pill); }
.dm-meter-fresh { background: var(--success); }
.dm-meter-warn { background: var(--warning); }
.dm-meter-stale { background: var(--danger); }
.user-tenants { display: flex; flex-wrap: wrap; gap: var(--space-2); max-width: 320px; }
.role-chip { display: inline-block; padding: var(--space-1) var(--space-3); border-radius: var(--radius-pill); font-size: var(--fs-100); font-weight: var(--fw-semibold); background: var(--surface-glass-hover); color: var(--text-default); }
.role-chip.role-admin { background: var(--accent-soft); color: var(--accent-text); }

/* ─── In-tenant roles ────────────────────────────────────────────────────────
   Three tiers that now decide what a request may do (src/services/tenant.ts).
   The chip is used on the Users screen, on the tenant detail screen, and in the
   top bar — the one header use is styled here rather than with the chrome so
   the whole vocabulary reads in one place.

   Owner is DELIBERATELY the plain neutral chip. It is the default every
   membership already holds, so colouring it would put a coloured badge on every
   row in the product and say nothing; the two restricted tiers are the ones
   worth a second look, and only those two carry a tint. */
.tenant-role-chip {
    display: inline-flex; align-items: center; gap: var(--space-1);
    padding: var(--space-1) var(--space-3);
    border-radius: var(--radius-pill);
    font-size: var(--fs-100);
    font-weight: var(--fw-semibold);
    background: var(--surface-glass-hover);
    color: var(--text-default);
    white-space: nowrap;
}
.tenant-role-chip [data-lucide] { inline-size: var(--icon-xs); block-size: var(--icon-xs); }
.tenant-role-operator { background: var(--info-soft); color: var(--info-text); }
.tenant-role-viewer { background: var(--warning-soft); color: var(--warning-text); }

/* The header badge. Present only when the role restricts you — see
   App.renderRoleBadge() — so it is never the decorative always-on indicator
   DESIGN.md rules out. */
.tenant-role { display: flex; align-items: center; }

.role-legend { display: flex; flex-direction: column; gap: var(--space-2); margin-block-start: var(--space-3); }
.role-legend-row { display: flex; align-items: flex-start; gap: var(--space-3); }
.role-legend-row dt { flex: 0 0 auto; }
.role-legend-row dd { font-size: var(--fs-100); color: var(--text-muted); }

/* A tenant switched off holds its due posts rather than publishing them, and
   this is where the operator is standing when they switch it off. */
.tenant-held-note {
    display: flex; align-items: center; gap: var(--space-1);
    margin-block-start: var(--space-1);
    font-size: var(--fs-100);
    color: var(--warning-text);
}
.tenant-held-note [data-lucide] { inline-size: var(--icon-xs); block-size: var(--icon-xs); flex: 0 0 auto; }

/* ─── First-run setup ────────────────────────────────────────────────────── */
.app.setup-mode .sidebar-nav { display: none; }
.setup-screen { padding: var(--space-6); max-width: 680px; margin-inline: auto; text-align: center; }
.setup-icon {
    width: 56px; height: 56px;
    margin-inline: auto;
    margin-block-end: var(--space-4);
    display: flex; align-items: center; justify-content: center;
    border-radius: var(--radius-md);
    background: var(--accent-soft); color: var(--accent-text);
}
.setup-icon [data-lucide] { inline-size: 26px; block-size: 26px; }
.setup-screen h2 { font-size: var(--fs-500); margin-block-end: var(--space-3); }
.setup-screen p { font-size: var(--fs-200); color: var(--text-default); }
.setup-steps {
    text-align: start;
    margin-block: var(--space-5);
    margin-inline: auto;
    padding-inline-start: var(--space-5);
    display: flex; flex-direction: column; gap: var(--space-3);
    font-size: var(--fs-200); color: var(--text-default);
    max-width: 52ch;
}
.setup-steps code { color: var(--text-strong); overflow-wrap: anywhere; }
.setup-note { margin-block-start: var(--space-4); font-size: var(--fs-200); color: var(--text-muted); }

/* Two stages, because two of these steps happen somewhere this product cannot
   reach. There is no OAuth connect flow — that needs Advanced Access from Meta
   — so the page access token and the webhook subscription are obtained by hand
   in Meta's console, and a setup screen that does not say so leaves the
   operator waiting for a button that is never going to appear. */
.setup-stage {
    margin-block: var(--space-5);
    padding: var(--space-4);
    text-align: start;
}
.setup-stage-title {
    display: flex; align-items: center; gap: var(--space-2);
    font-size: var(--fs-300);
    font-weight: var(--fw-semibold);
    color: var(--text-strong);
}
.setup-stage-title [data-lucide] { inline-size: var(--icon-md); block-size: var(--icon-md); flex: 0 0 auto; }
.setup-stage-note { margin-block-start: var(--space-2); font-size: var(--fs-100); color: var(--text-muted); }
.setup-stage .setup-steps { margin-block: var(--space-4) 0; max-width: none; }
.setup-stage .setup-steps li { list-style: decimal; }
.setup-detail { display: block; margin-block-start: var(--space-1); font-size: var(--fs-100); color: var(--text-muted); }
.setup-value {
    display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap;
    margin-block-start: var(--space-2);
}
.setup-value code {
    padding: var(--space-1) var(--space-2);
    border-radius: var(--radius-xs);
    background: var(--surface-sunken);
    overflow-wrap: anywhere;
}

/* ─── Bulk import modal ──────────────────────────────────────────────────── */
.bulk-scroll { max-height: 56vh; overflow-y: auto; padding-block: var(--space-3); display: flex; flex-direction: column; gap: var(--space-4); }
.bulk-intro { color: var(--text-default); font-size: var(--fs-200); }
.bulk-row {
    padding: var(--space-4);
    display: flex; flex-direction: column; gap: var(--space-3);
    /* A tint, not glass: nothing is blurred behind it, so a translucent glass token read matte. */
    background: var(--surface-glass);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    /* A Udemy CSV of thirty courses builds thirty of these, each with a text
       input, a tall textarea and another input — the single heaviest DOM this
       dashboard ever constructs, inside a 56vh scroller. Skipping the
       off-screen ones is the difference between the modal opening and the
       modal arriving. The fields stay focusable, and executeBulkImport() reads
       them by id, which content-visibility does not affect. */
    content-visibility: auto;
    contain-intrinsic-size: auto 420px;
}
.bulk-row.is-mapped { border-color: color-mix(in srgb, var(--accent) 30%, transparent); }
.bulk-row-head { display: flex; justify-content: space-between; align-items: flex-start; gap: var(--space-3); }
.bulk-name { font-size: var(--fs-200); font-weight: var(--fw-semibold); color: var(--text-strong); display: block; cursor: pointer; }
.bulk-coupon { font-size: var(--fs-100); color: var(--text-muted); }
.bulk-import-input { font-size: var(--fs-100); }
.bulk-import-textarea { min-height: 96px; font-size: var(--fs-100); }
.import-progress { text-align: center; padding: var(--space-6) var(--space-4); }
.import-progress .spinner { margin-inline: auto; margin-block-end: var(--space-4); }
.import-progress p { color: var(--text-default); font-size: var(--fs-200); margin-block-start: var(--space-2); }

/* ─── Administration UI ──────────────────────────────────────────────────────
   Operations, tenant detail, users, jobs, audit and erasure. No new values:
   every number below is a token from tokens.css, and every colour is a
   semantic pair (`--*-soft` background with `--*-text` foreground) so the light
   theme follows without a second rule.

   Direction is logical throughout — `padding-inline`, `border-inline-start`,
   `margin-block-end` — because these screens read right-to-left first. There
   is no physical `left`/`right` in this section. */

/* The strip above the page container. It is the ordinary operator's answer to
   "are webhooks still arriving?", so it sits above the fold on Settings rather
   than inside it. Padding is block-start only: the page container below
   supplies its own top padding, and doubling them would push the page down. */
.page-prelude { padding: var(--space-5) var(--space-6) 0; }

.health-strip { padding: var(--space-5); }
.health-strip-head {
    display: flex; align-items: center; justify-content: space-between;
    gap: var(--space-3); flex-wrap: wrap;
    margin-block-end: var(--space-4);
}
.health-strip-title { font-size: var(--fs-400); font-weight: var(--fw-semibold); color: var(--text-strong); }
/* An alarm is a border, not a fill: the tiles inside carry their own severity
   colour and a red panel behind red pills reads as noise. */
.health-strip-alarm { border-color: var(--border-danger); }
.health-strip-lede {
    font-size: var(--fs-200);
    color: var(--text-strong);
    background: var(--danger-soft);
    border-inline-start: 3px solid var(--danger);
    border-radius: var(--radius-sm);
    padding: var(--space-3) var(--space-4);
    margin-block-end: var(--space-4);
}
.health-strip-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
    gap: var(--space-4);
}
.health-tile { display: flex; flex-direction: column; align-items: flex-start; gap: var(--space-2); min-width: 0; }
.health-tile-label { font-size: var(--fs-100); color: var(--text-muted); }
.health-tile-note { font-size: var(--fs-100); color: var(--text-muted); }
.health-strip-facts { margin-block-start: var(--space-4); }

/* Meta's own explanation of a bad token. It was on the wire the whole time and
   being thrown away, which left a red pill and no diagnosis. */
.health-diagnosis {
    display: flex; align-items: flex-start; gap: var(--space-2);
    margin-block: var(--space-3);
    padding: var(--space-3);
    border-radius: var(--radius-sm);
    background: var(--danger-soft);
    border: 1px solid var(--border-danger);
    font-size: var(--fs-100);
    color: var(--text-strong);
    overflow-wrap: anywhere;
}
.health-diagnosis [data-lucide] { inline-size: var(--icon-md); block-size: var(--icon-md); flex-shrink: 0; margin-block-start: 2px; }
.health-diagnosis svg { color: var(--danger-text); }

/* ─── Definition pairs (the detail cards' shared shape) ─────────────────── */
.ops-facts { display: grid; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); gap: var(--space-3) var(--space-4); }
.detail-field { display: flex; flex-direction: column; gap: var(--space-1); min-width: 0; }
.detail-field dt { font-size: var(--fs-100); color: var(--text-muted); }
.detail-field dd { font-size: var(--fs-200); color: var(--text-default); display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; }
.detail-field dd strong { color: var(--text-strong); }

/* ─── Operations ────────────────────────────────────────────────────────── */
.ops-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(340px, 1fr)); gap: var(--space-4); }
.ops-card { padding: var(--space-5); display: flex; flex-direction: column; gap: var(--space-4); min-width: 0; }
.ops-card-alarm { border-color: var(--border-danger); }
.ops-card-off { opacity: var(--opacity-off); }
.ops-card-head { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--space-3); flex-wrap: wrap; }
.ops-card-title {
    font-size: var(--fs-400); font-weight: var(--fw-semibold); color: var(--text-strong);
    display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap;
    min-width: 0;
}
.ops-card-actions { display: flex; gap: var(--space-2); flex-wrap: wrap; }

/* The lead. Token status and webhook age are the two facts the page exists to
   report, so they get their own band above everything else rather than being
   two more cells in a list. */
.ops-lead {
    display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
    gap: var(--space-4);
    padding: var(--space-4);
    border-radius: var(--radius-md);
    background: var(--surface-sunken);
}
.ops-lead-item { display: flex; flex-direction: column; align-items: flex-start; gap: var(--space-2); min-width: 0; }
.ops-lead-label { font-size: var(--fs-100); color: var(--text-muted); }
.ops-lead-note { font-size: var(--fs-100); color: var(--text-muted); }

.queue-triple { display: inline-flex; gap: var(--space-3); flex-wrap: wrap; font-variant-numeric: tabular-nums; }
.icon-success { color: var(--success-text); }

/* Short-keyword hazards: each row is a keyword plus the way out. */
.hazard-list { display: flex; flex-direction: column; gap: var(--space-3); }
.hazard-item {
    display: flex; align-items: center; justify-content: space-between;
    gap: var(--space-4); flex-wrap: wrap;
    padding: var(--space-3) var(--space-4);
    border: 1px solid var(--border-subtle);
    border-inline-start: 3px solid var(--warning);
    border-radius: var(--radius-md);
    /* A tint, not glass: nothing is blurred behind it, so a translucent glass token read matte. */
    background: var(--surface-glass);
}
.hazard-main { display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap; min-width: 0; }
.hazard-keyword { font-size: var(--fs-300); color: var(--text-strong); font-weight: var(--fw-semibold); }

/* ─── Tenant detail ─────────────────────────────────────────────────────── */
.detail-name {
    font-size: var(--fs-500); color: var(--text-strong);
    display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap;
    min-width: 0;
}

/* ─── Users ─────────────────────────────────────────────────────────────── */
.user-card-list { display: grid; grid-template-columns: repeat(auto-fit, minmax(360px, 1fr)); gap: var(--space-4); }
.user-card { padding: var(--space-5); display: flex; flex-direction: column; gap: var(--space-4); min-width: 0; }
.user-card-off { opacity: var(--opacity-off); }
.user-card-head { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--space-3); flex-wrap: wrap; }
.user-card-email { font-size: var(--fs-300); color: var(--text-strong); overflow-wrap: anywhere; min-width: 0; }
.user-card-tags { display: flex; gap: var(--space-2); flex-wrap: wrap; }
.user-card-controls { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: var(--space-4); align-items: end; }
/* The last control in a card must not add a trailing margin the flex gap has
   already accounted for. */
.user-card-controls .form-group { margin-block-end: 0; }
.user-active-row { min-height: var(--tap-target); }
.user-memberships { padding-block-start: var(--space-4); border-block-start: 1px solid var(--border-subtle); }
.membership-list { display: flex; flex-direction: column; gap: var(--space-2); }
.membership-item { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; }
/* The tenant name takes the slack so the role picker and the revoke button keep
   a predictable size as names get longer. */
.membership-name { flex: 1 1 auto; min-width: 0; overflow-wrap: anywhere; }
.membership-role { flex: 0 0 auto; width: auto; min-width: 8.5rem; }
.select-sm { min-height: var(--tap-target); padding-block: var(--space-1); font-size: var(--fs-100); }

/* Who can reach one tenant. The data was already in the tenant-detail payload
   and nothing rendered it; now that the membership role decides something, it
   is the answer to "who can rotate this client's page token". */
.member-list { display: flex; flex-direction: column; gap: var(--space-2); }
.member-item {
    display: flex; align-items: center; justify-content: space-between;
    gap: var(--space-3); flex-wrap: wrap;
    padding: var(--space-2) var(--space-3);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    background: var(--surface-glass);
}
.member-item-off { opacity: var(--opacity-off); }
.member-email { font-size: var(--fs-200); color: var(--text-strong); overflow-wrap: anywhere; min-width: 0; }
.member-tags { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; }
.user-card-actions { display: flex; gap: var(--space-2); flex-wrap: wrap; }

/* ─── Jobs ──────────────────────────────────────────────────────────────── */
/* Cards are the phone form of the table; CSS picks one, so there is one data
   path and no resize listener — the same arrangement the activity log uses. */
.jobs-cards, .audit-cards { display: none; flex-direction: column; gap: var(--space-3); }
.jobs-table .data-table, .audit-table .data-table { min-width: 720px; }
.job-kind { font-weight: var(--fw-semibold); color: var(--text-strong); display: block; overflow-wrap: anywhere; }
.job-tenant, .job-claimed { display: block; font-size: var(--fs-100); color: var(--text-muted); margin-block-start: var(--space-1); }
.job-error {
    font-size: var(--fs-100);
    color: var(--danger-text);
    background: var(--danger-soft);
    border-radius: var(--radius-sm);
    padding: var(--space-3);
    overflow-wrap: anywhere;
}

/* ─── Audit ─────────────────────────────────────────────────────────────── */
.audit-detail { max-width: 320px; font-size: var(--fs-100); color: var(--text-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.audit-detail-block { font-size: var(--fs-100); color: var(--text-muted); overflow-wrap: anywhere; }
.audit-target-type { font-size: var(--fs-100); color: var(--text-muted); margin-inline-end: var(--space-1); }

/* ─── Erasure ───────────────────────────────────────────────────────────── */
.erasure-subtitle { font-size: var(--fs-300); color: var(--text-strong); margin-block-end: var(--space-3); }
.erasure-sample {
    display: flex; flex-direction: column; gap: var(--space-2);
    max-height: 220px; overflow-y: auto;
    padding: var(--space-3);
    font-size: var(--fs-100);
    color: var(--text-default);
}
.erasure-sample li { overflow-wrap: anywhere; }

/* ─── Sidebar group label ───────────────────────────────────────────────── */
.nav-group-label {
    display: block;
    padding: var(--space-4) var(--space-4) var(--space-2);
    font-size: var(--fs-100);
    font-weight: var(--fw-semibold);
    color: var(--text-muted);
}

/* ═══════════════════════════════════════════════════════════════════════════
   Responsive.  Every screen has to work at 375px.
   ═══════════════════════════════════════════════════════════════════════════ */
@media (max-width: 1100px) {
    .chart-grid, .chart-grid--even { grid-template-columns: 1fr; }
    .ai-grid { grid-template-columns: 1fr; }
}

@media (max-width: 860px) {
    /* Inbox: one pane at a time. A 320px + 1fr grid left the chat pane ~39px
       wide at 375px, which made "check my DMs on my phone" impossible. It goes
       single-pane, and the form grids single-column, at the same width the sidebar
       becomes a drawer: between 861 and 900px the desktop sidebar used to sit open
       beside a one-pane inbox. */
    .inbox-layout {
        grid-template-columns: 1fr;
        grid-template-rows: 1fr;
        height: calc(100dvh - var(--header-height) - var(--space-6));
    }
    .inbox-layout > .inbox-sidebar,
    .inbox-layout > .chat-pane { grid-column: 1; grid-row: 1; min-width: 0; }
    .inbox-layout[data-pane='threads'] > .chat-pane { display: none; }
    .inbox-layout[data-pane='chat'] > .inbox-sidebar { display: none; }
    .chat-back { display: inline-flex; }
    .message-bubble { max-width: 88%; }
    .form-grid { grid-template-columns: 1fr; }

    .sidebar {
        transform: translateX(-100%);
        box-shadow: var(--elev-3);
    }
    html[dir='rtl'] .sidebar { transform: translateX(100%); }
    .sidebar.open { transform: none; }
    /* Just below the drawer, above page content. Created and toggled by app.js —
       the drawer had no scrim, no Escape-to-close and no click-outside-to-close, unlike
       every other overlay in the app (the modal already has all three). */
    .sidebar-backdrop {
        position: fixed; inset: 0; z-index: calc(var(--z-drawer) - 10);
        background: rgb(0 0 0 / 40%);
        opacity: 0; pointer-events: none;
        transition: opacity var(--motion-fast) var(--ease-standard);
    }
    .sidebar-backdrop.open { opacity: 1; pointer-events: auto; }
    .main-content { margin-inline-start: 0; }
    .menu-button { display: inline-flex; }
    .page-container { padding: var(--space-4); }
    .page-header { padding-inline: var(--space-4); }
    .header-start { min-width: 0; }
    .header-start > div { min-width: 0; }
    .page-title, .page-subtitle { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
}

@media (max-width: 768px) {
    .modal-overlay { padding: var(--space-2); align-items: flex-start; }
    .modal-content { padding: var(--space-4); max-height: calc(100dvh - var(--space-4)); }
    .modal-actions .btn { flex: 1; min-width: 130px; }
    .page-toolbar { flex-direction: column; align-items: stretch; }
    .toolbar-actions .btn { flex: 1; }
    .segmented { width: 100%; }
    .segmented .btn { flex: 1; }
    .card-grid { grid-template-columns: 1fr; }
    .table-card { padding: var(--space-4); }
    .filter-bar .field, .filter-bar .select, .filter-bar .field[type='search'] { width: 100%; min-width: 0; }
    .filter-bar { flex-direction: column; align-items: stretch; }
    /* The filter column and the count/export row must not share a flex line,
       or the inputs only get about half the card's width. */
    .table-header { flex-direction: column; align-items: stretch; }
    .table-header > .row { justify-content: space-between; }
    .bulk-scroll { max-height: 52dvh; }
    .cover-compare { flex-direction: column; }
    .cover-tile { flex: 0 0 auto; }
    .cover-tile-frame { width: 100%; height: 150px; }

    /* The activity log stops being a six-column table and becomes cards. */
    .log-table { display: none; }
    .log-cards { display: flex; }

    .tenants-table { min-width: 640px; }
    .tenants-table th:nth-child(5), .tenants-table td:nth-child(5),
    .tenants-table th:nth-child(6), .tenants-table td:nth-child(6),
    .tenants-table th:nth-child(7), .tenants-table td:nth-child(7),
    .tenants-table th:nth-child(8), .tenants-table td:nth-child(8) { display: none; }
    .row-actions { justify-content: flex-start; }
    .user-tenants { max-width: none; }
}

/* Below ~560px the header has to give the page title room. With the status
   badge gone there is far more of it, so the language toggle — two words, and
   duplicated in Settings — can now stay; only the tenant name gets shortened. */
@media (max-width: 560px) {
    .chip-select select { max-width: 110px; }
    .lang-toggle { padding-inline: var(--space-2); }
    .stats-grid { grid-template-columns: 1fr 1fr; }
    .stat-card { padding: var(--space-4); }
    .stat-value { font-size: var(--fs-500); }
    .briefing-list { grid-template-columns: 1fr; }
}

@media (max-width: 400px) {
    .stats-grid { grid-template-columns: 1fr; }
    .page-container { padding: var(--space-3); }
}

@media (prefers-reduced-motion: reduce) {
    /* Slowed, not stopped: a frozen spinner reads as a hung request. */
    .spinner { animation-duration: 2s; }
}

/* ─── Administration UI, narrow ──────────────────────────────────────────────
   Kept as its own block rather than folded into the media queries above, so
   the two sets of rules can be read (and merged) independently.

   Rule for these screens: a table either becomes cards or scrolls on purpose.
   Nothing overflows the page. */
@media (max-width: 1100px) {
    /* One card per row before the cards themselves get squeezed: an ops card
       holds two health pills side by side and a six-cell fact grid. */
    .ops-grid, .user-card-list { grid-template-columns: 1fr; }
}

@media (max-width: 768px) {
    .page-prelude { padding: var(--space-4) var(--space-4) 0; }
    .health-strip, .ops-card, .user-card { padding: var(--space-4); }

    /* Jobs and audit stop being wide tables and become cards. */
    .jobs-table, .audit-table { display: none; }
    .jobs-cards, .audit-cards { display: flex; }

    /* A stat row of three at 375px is three unreadable columns. */
    .ops-lead { grid-template-columns: 1fr; }
    .ops-facts, .health-strip-grid, .user-card-controls { grid-template-columns: 1fr; }

    /* Full-width actions, so a 44px target is not also a 44px-wide target. */
    .ops-card-actions .btn,
    .user-card-actions .btn { flex: 1 1 auto; }

    .hazard-item { align-items: stretch; }
    .hazard-item .btn { width: 100%; }

    /* A membership row is a name, a role picker and a revoke button. Below this
       the three cannot share a line without the picker shrinking past its own
       longest option, so the picker takes the full width on its own line. */
    .membership-name { flex: 1 1 100%; }
    .membership-role { flex: 1 1 auto; width: 100%; min-width: 0; }

    .member-item { align-items: flex-start; }
    .setup-stage { padding: var(--space-3); }
    .setup-stage .setup-steps { padding-inline-start: var(--space-4); }

    /* The tenants table gains two row actions (inspect, re-check), so it needs
       more room before it scrolls; the count/refresh row and the filters must
       not share a flex line. */
    .tenants-table { min-width: 720px; }
}

@media (max-width: 560px) {
    .detail-name { font-size: var(--fs-400); }
    .health-strip-title { font-size: var(--fs-300); }
    /* A definition value that is a meter needs the whole column. */
    .detail-field dd { align-items: flex-start; }
}

@media (max-width: 400px) {
    .page-prelude { padding: var(--space-3) var(--space-3) 0; }
    .ops-card-actions, .user-card-actions { flex-direction: column; align-items: stretch; }
    /* At 375px the top bar holds a title, a role badge and the language toggle.
       The badge keeps its colour and its icon and drops the word — it is a
       pointer to an explanation, not the explanation. */
    .tenant-role-chip { padding-inline: var(--space-2); }
    .tenant-role .tenant-role-chip span:not(.sr-only) { display: none; }
    .role-legend-row { flex-direction: column; gap: var(--space-1); }
}

html[dir='rtl'] .select { background-position: left var(--space-3) center; }

/* Naming only the group left the UA's default fade on the old/new pseudo-elements, so the
   marker ghosted: a fading bar at the old position and a rising one at the new. */
::view-transition-old(nav-active),
::view-transition-new(nav-active) { animation: none; }

/* The stylesheet never named (root), so it ran at the browser's 250ms default while (page)
   ran at --motion-base — chrome and content settled 50ms apart on every navigation. */
::view-transition-group(root),
::view-transition-old(root),
::view-transition-new(root) { animation-duration: var(--motion-base); animation-timing-function: var(--ease-standard); }

/* The page-lifecycle pass promoted these from <span> to real headings so the screens have an
   outline a screen reader can navigate. The base h1..h5 rule sets bold and tight leading, and
   neither class pinned either — so without this the promotion would have been visible.
   (`.table-title` now pins its own weight: semibold, the card-title treatment.) */
.page-toolbar-count { font-weight: var(--fw-regular); }
.table-title, .page-toolbar-count, .chart-card-title { line-height: var(--lh-normal); }

/* A clipped error cell is readable by a screen reader via its title; this lets a sighted
   keyboard user see it too. */
.cell-error:focus-visible { white-space: normal; overflow: visible; }

/* A long Meta error must not squeeze the toast's new close button out of reach. */
.toast .modal-close { flex-shrink: 0; }

/* ─── RTL icon mirroring ──────────────────────────────────────────────────────
   Only icons whose MEANING depends on reading direction flip.

   Material Design 3 is the one published design system with an actually
   enumerated rule for this, and it is explicit in both directions: directional
   arrows and send buttons mirror; clocks, circular refresh icons and media
   transport controls must NOT.
   https://m3.material.io/foundations/layout/bidirectionality-rtl

   So `rotate-cw` and `refresh-cw` are deliberately absent below — they are the
   most-used icons in this dashboard (14 uses between them) and mirroring either
   would make a clockwise arrow read counter-clockwise, which is just wrong.
   Same for `clock`, `calendar-clock`, `file-clock`, `check`, `search`,
   `download` and `upload`: a magnifying glass and a vertical arrow carry no
   reading direction. Do not add them here.

   Targeting `[data-lucide]` rather than `.lucide-<name>` is deliberate:
   lucide@0.577 copies the attribute onto the `<svg>` it generates AND it is
   present on the `<i>` beforehand, so the flip is already correct during the
   pre-hydration placeholder phase rather than snapping after createIcons().

   This replaces three hand-written per-component rules (`.alert-link`,
   `.chat-back`, `.page-btn`) which covered exactly arrow-left/right and
   chevron-left/right. They were correct, but they had to be *remembered* — the
   next directional icon added anywhere would have silently pointed the wrong
   way. `scripts/check-rtl-icons.mjs` now fails CI if an in-use directional icon
   is missing from this list, or if a must-not-mirror icon is added to it. */
html[dir='rtl'] [data-lucide='arrow-left'],
html[dir='rtl'] [data-lucide='arrow-right'],
html[dir='rtl'] [data-lucide='arrow-left-to-line'],
html[dir='rtl'] [data-lucide='arrow-right-to-line'],
html[dir='rtl'] [data-lucide='move-left'],
html[dir='rtl'] [data-lucide='move-right'],
html[dir='rtl'] [data-lucide='chevron-left'],
html[dir='rtl'] [data-lucide='chevron-right'],
html[dir='rtl'] [data-lucide='chevrons-left'],
html[dir='rtl'] [data-lucide='chevrons-right'],
html[dir='rtl'] [data-lucide='circle-chevron-left'],
html[dir='rtl'] [data-lucide='circle-chevron-right'],
html[dir='rtl'] [data-lucide='square-chevron-left'],
html[dir='rtl'] [data-lucide='square-chevron-right'],
html[dir='rtl'] [data-lucide='corner-down-left'],
html[dir='rtl'] [data-lucide='corner-down-right'],
html[dir='rtl'] [data-lucide='corner-up-left'],
html[dir='rtl'] [data-lucide='corner-up-right'],
html[dir='rtl'] [data-lucide='send'],
html[dir='rtl'] [data-lucide='send-horizontal'],
html[dir='rtl'] [data-lucide='reply'],
html[dir='rtl'] [data-lucide='reply-all'],
html[dir='rtl'] [data-lucide='forward'],
html[dir='rtl'] [data-lucide='log-in'],
html[dir='rtl'] [data-lucide='log-out'],
html[dir='rtl'] [data-lucide='external-link'],
html[dir='rtl'] [data-lucide='indent-increase'],
html[dir='rtl'] [data-lucide='indent-decrease'] {
    transform: scaleX(-1);
}

/* ─── Micro-label tracking, Latin only ──────────────────────────────────────────────────────
   The brief asks for generous letter-spacing on small uppercase labels. That cannot be
   applied globally here, and the reason is not stylistic: **Arabic is a joining script**, and
   letter-spacing pulls the joined letters apart into disconnected glyphs. It also asks for
   uppercase, which Arabic does not have. The header of this file records that an earlier
   version of this dashboard shipped `letter-spacing: 0.5px; text-transform: uppercase` on
   table headers, and that removing it was a fix.

   So the tracking is applied in exactly the two places where the content is guaranteed Latin:

     1. When the UI language is English — `html[lang='en']`. An Arabic UI never gets it.
     2. On monospace id/token cells, which are Latin regardless of UI language.

   The uppercase transform is deliberately NOT reintroduced anywhere. Tracking alone gives
   the crisp, settled feel the brief is after; uppercasing Arabic-adjacent UI gains nothing
   and loses the letterforms. */
html[lang='en'] .text-meta,
html[lang='en'] .stat-label,
html[lang='en'] .form-label,
html[lang='en'] .page-toolbar-count {
    letter-spacing: 0.02em;
}

/* Always Latin by nature: ids, tokens, page ids, connection fragments. */
.field-mono,
.cell-id,
.post-card-id,
.job-kind {
    letter-spacing: 0.02em;
}

/* ─── Charts ────────────────────────────────────────────────────────────────────────────────
   These replaced Chart.js canvases. Deliberately almost no chrome: no gridlines, no axis
   rules, no boxed legend. A day strip and a proportion bar are legible without any of it, and
   every one of those was a thing Chart.js drew by default and this design had to fight.

   `dir="ltr"` is set on the strip in JS, not here: the cells are reordered in the builder so
   the most recent day sits at the reading edge, and the SVG itself must then not be flipped
   again by the document direction. */

.chart-strip { padding-block: var(--space-2); }
.chart-strip svg { display: block; overflow: visible; }

.chart-foot {
    margin-block-start: var(--space-3);
    font-size: var(--fs-100);
    color: var(--text-muted);
    line-height: var(--lh-normal);
}

.chart-split { padding-block: var(--space-3) 0; }
.chart-split svg { display: block; border-radius: var(--radius-pill); }

.chart-split-legend {
    list-style: none;
    margin: var(--space-4) 0 0;
    padding: 0;
    display: grid;
    gap: var(--space-2);
}
.chart-split-legend li {
    display: grid;
    grid-template-columns: auto 1fr auto auto;
    align-items: center;
    gap: var(--space-2) var(--space-3);
    font-size: var(--fs-200);
}
.chart-dot {
    inline-size: 9px; block-size: 9px;
    border-radius: var(--radius-pill);
    /* A dot, not a swatch: the colour is a key to the bar above, not a sample. */
    flex-shrink: 0;
}
.chart-split-label { color: var(--text-default); }
/* Tabular figures so the two rows' numbers line up under each other. */
.chart-split-value { color: var(--text-strong); font-weight: var(--fw-semibold); font-variant-numeric: tabular-nums; }
.chart-split-pct { color: var(--text-muted); font-variant-numeric: tabular-nums; }

.chart-spark { padding-block: var(--space-2); }
.chart-spark svg { display: block; }

/* Says there is nothing, rather than drawing empty axes around no data. */
.chart-empty {
    margin: 0;
    font-size: var(--fs-200);
}

/* The daily line (Charts.lineChart). A grid: the y labels in the first column — the
   inline START, so in Arabic they sit on the right beside the oldest day — the plot in the
   second, and the date row under the plot only. The labels are HTML because glyphs inside
   a stretched viewBox would stretch with it. */
.chart-line {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    column-gap: var(--space-2);
    padding-block: var(--space-2) 0;
}
.chart-line-y {
    grid-column: 1; grid-row: 1;
    display: flex; flex-direction: column; justify-content: space-between;
    block-size: 160px;
    font-size: var(--fs-100); line-height: 16px;
    color: var(--text-muted);
    font-variant-numeric: tabular-nums;
    text-align: end;
    unicode-bidi: plaintext;
}
.chart-line-svg { grid-column: 2; grid-row: 1; display: block; block-size: 160px; overflow: visible; }
.chart-line-x {
    grid-column: 2; grid-row: 2;
    display: flex;
    margin-block-start: var(--space-2);
    font-size: var(--fs-100);
    color: var(--text-muted);
}
/* A date is wider than its day's slot, and text that overflows its box always spills toward
   the END side — outward, for the last date. As flex boxes the labels overflow by alignment
   instead: centred on their day, and the two edge dates anchored so they spill inward. */
.chart-line-x > span { flex: 1 1 0%; min-inline-size: 0; display: flex; justify-content: center; overflow: visible; white-space: nowrap; }
.chart-line-x > span.is-first { justify-content: flex-start; }
.chart-line-x > span.is-last { justify-content: flex-end; }
@media (max-width: 560px) {
    .chart-line-y, .chart-line-svg { block-size: 132px; }
    .chart-line-x > span.is-minor { visibility: hidden; }
}

/* ─── Shared patterns (UX audit VS15) ───────────────────────────────────────
   Declarations several components repeated byte for byte, now stated once.
   CSS-only: each component keeps its class and its own rule for everything
   that differs. The near-misses the refactor left (22px vs 24px, --radius-sm vs
   --radius-md) were decided in the design pass of 2026-09-27, and each recipe now
   lives in its pattern (DESIGN.md §4). The next step is a class in the markup
   (`.callout`, `.well`…), not more selectors here.
   Why here and not with the primitives at the top: moving a declaration past
   an earlier rule of the same specificity that sets the same property can flip
   which one wins. Every declaration below can reach this stretch of the file
   without crossing one; `node scripts/css-declmap.mjs` checks that (the design
   pass's one flagged pair, .empty-state × .loader, is explained in
   scripts/design-pass.tags.json). */

/* ─── Pattern: callout — shared by .schedule-truth, .tiktok-note, .studio-hint, .studio-callout, .help-callout ─── */
/* One recipe: 12/16 padding, a 12 gap, an inner panel's radius, UI-size text, and the tone's
   one border (--border-<tone>, tokens.css). A variant changes the tone, never the shape. */
.schedule-truth, .tiktok-note, .studio-hint, .studio-callout, .help-callout {
    display: flex; align-items: flex-start; gap: var(--space-3);
    padding: var(--space-3) var(--space-4);
    border-radius: var(--radius-md);
    background: var(--info-soft);
    border: 1px solid var(--border-info);
    font-size: var(--fs-200);
}

/* ─── Pattern: step mark — shared by .slide-num, .slide-card-num, .studio-step-num, .setup-step-mark, .studio-stage-mark ─── */
/* One size everywhere: 24px, its label at --fs-100 semibold. Growth's `.growth-action-num` is
   the same mark and restates these values in its own rule. */
.slide-num, .slide-card-num, .studio-step-num, .setup-step-mark, .studio-stage-mark {
    display: inline-flex; align-items: center; justify-content: center;
    block-size: 24px;
    border-radius: var(--radius-pill);
    font-size: var(--fs-100); font-weight: var(--fw-semibold);
}
/* A circle: the stepper, the setup checklist, the writing stages. */
.studio-step-num, .setup-step-mark, .studio-stage-mark { flex-shrink: 0; inline-size: 24px; }
/* Outlined: the stepper, the setup checklist, the writing stages. */
.studio-step-num, .setup-step-mark, .studio-stage-mark { border: 1px solid var(--border-strong); }
/* Filled: a slide's number, on its tile and on its form — a pill, so "10" still fits. */
.slide-num, .slide-card-num {
    min-inline-size: 24px; padding-inline: var(--space-2);
    background: var(--surface-solid); color: var(--text-strong);
    font-variant-numeric: tabular-nums;
}

/* ─── Pattern: media frame — shared by .post-picker-thumb, .cover-tile-frame, .slide-thumb, .post-slide, .moment-thumb, .draft-cover, .shot-thumb, .shot-hero-frame, .slide-alt-thumb, .post-media-frame, .media-preview, .rail-tab, .monteur-media, and the images in .phone-stage and .rail-tab ─── */
/* Every thumbnail and preview at --radius-sm. The phone's own screen (.phone-stage) is a
   device, not a frame, and keeps the device's radius. */
.post-picker-thumb, .post-picker-item img, .cover-tile-frame, .slide-thumb, .post-slide, .moment-thumb,
.draft-cover, .shot-thumb, .shot-hero-frame, .slide-alt-thumb, .post-media-frame, .media-preview,
.rail-tab, .monteur-media { border-radius: var(--radius-sm); }
/* Centres its placeholder icon on the opaque ground: opaque, or an image on it reads washed out. */
.post-picker-thumb, .slide-thumb, .moment-thumb, .draft-cover, .shot-thumb, .shot-hero-frame, .slide-alt-thumb {
    display: flex; align-items: center; justify-content: center;
    background: var(--surface-solid); color: var(--text-muted);
}
/* Clips to its hairline. */
.cover-tile-frame, .slide-thumb, .post-slide, .draft-cover, .slide-alt-thumb, .post-media-frame, .media-preview {
    overflow: hidden; border: 1px solid var(--border-subtle);
}
/* The image fills it. */
.moment-thumb img, .draft-cover img, .shot-thumb img, .phone-stage img, .rail-tab img { inline-size: 100%; block-size: 100%; object-fit: cover; }
.cover-tile-frame img, .slide-thumb img, .post-slide img { width: 100%; height: 100%; object-fit: cover; }

/* ─── Pattern: well — shared by .template-preview, .post-picker, .cover-step, .sandbox-shell, .erasure-sample, .studio-lessons, .studio-summary, .studio-preview-card, .shot-field, .help-code pre ─── */
/* An inner panel: --radius-md, like the tint panels and the list rows. */
.template-preview, .post-picker, .cover-step, .sandbox-shell, .erasure-sample,
.studio-lessons, .studio-summary, .studio-preview-card, .shot-field, .help-code pre {
    background: var(--surface-sunken); border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
}

/* ─── Pattern: tint panel — shared by .setup-stage, .plan-item, .slide-card, .studio-more, .help-empty, .help-toc, .help-qa-item ─── */
.setup-stage, .plan-item, .slide-card, .studio-more, .help-empty, .help-toc, .help-qa-item {
    border: 1px solid var(--border-subtle); border-radius: var(--radius-md);
    background: var(--surface-glass);
}

/* ─── Pattern: meter — shared by .platform-bar, .dm-meter-track, .studio-meter, .studio-indeterminate ─── */
/* Heights: a progress meter is 6px (.dm-meter-track, .studio-meter and Growth's SVG .growth-bar);
   the indeterminate sweep is 4px; the labelled data bar (.platform-bar) is 10px. Each states its
   own height, since the three differ by role. */
.platform-bar, .dm-meter-track, .studio-meter, .studio-indeterminate {
    border-radius: var(--radius-pill); background: var(--surface-glass-hover); overflow: hidden;
}

/* ─── Pattern: empty state — shared by .empty-state, .chat-empty, .sandbox-welcome, .table-empty-cell, .post-picker-note, .empty-threads, .chart-empty ─── */
/* One padding for every empty state. `.data-table .table-empty-cell`: the empty row of a table
   is a <td>, and `.data-table td` (0,1,1) outranks a bare class. */
.empty-state, .chat-empty, .sandbox-welcome, .table-empty-cell, .data-table .table-empty-cell,
.post-picker-note, .empty-threads, .chart-empty { padding: var(--space-6) var(--space-4); }
.empty-state, .chat-empty, .sandbox-welcome {
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    gap: var(--space-3);
    text-align: center;
}
/* The one-line form. */
.table-empty-cell, .post-picker-note, .empty-threads, .chart-empty { text-align: center; color: var(--text-muted); }

/* ═══════════════════════════════════════════════════════════════════════════
   Carousel Studio (#/studio).  Home: status, a new carousel, the plan, the
   drafts. Editor: previews, one form per slide, captions, campaign, schedule.
   Settings: brand, voice, CTAs, schedule, library, workers. Logical
   properties throughout; images sit on --surface-solid, as elsewhere.
   ═══════════════════════════════════════════════════════════════════════════ */
.studio-tabs { inline-size: max-content; max-inline-size: 100%; }

/* The worker pill's "off": a real state (not seen for 90s), so grey, not red. */
.health-off { background: var(--surface-glass-hover); color: var(--text-default); }
.status-pill.scheduled { background: var(--info-soft); color: var(--info-text); }

/* The status is a strip, not a dashboard: it gets loud only when something needs doing. */
.studio-status { padding: var(--space-4); margin-block-end: var(--space-4); }
.studio-status-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
    gap: var(--space-3) var(--space-5);
}
.studio-status-item { display: flex; flex-direction: column; gap: var(--space-1); min-inline-size: 0; }
.studio-status-item .text-meta { margin: 0; }
.studio-status-head {
    display: flex; align-items: center; flex-wrap: wrap; gap: var(--space-2);
    margin: 0; font-size: var(--fs-200); color: var(--text-strong);
}
.studio-status-head [data-lucide] { inline-size: var(--icon-md); block-size: var(--icon-md); flex-shrink: 0; }
.studio-status-count { font-size: var(--fs-300); font-variant-numeric: tabular-nums; }
.studio-status-failed { display: flex; align-items: center; flex-wrap: wrap; gap: var(--space-2); font-size: var(--fs-200); color: var(--danger-text); }
.studio-status-failed [data-lucide] { inline-size: var(--icon-md); block-size: var(--icon-md); flex-shrink: 0; }
.studio-hint {
    margin: 0;
    color: var(--text-default);
}
.studio-hint [data-lucide] { inline-size: var(--icon-md); block-size: var(--icon-md); flex-shrink: 0; margin-block-start: 2px; }
.studio-hint svg { color: var(--info-text); }
.studio-hint a { color: var(--accent-text); text-decoration: underline; text-underline-offset: 2px; }

.studio-section-head {
    display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap;
    gap: var(--space-3); margin-block-end: var(--space-4);
}
.studio-section-head .section-title,
.studio-section-head .studio-panel-title { margin: 0; }
.studio-panel-title { font-size: var(--fs-400); font-weight: var(--fw-semibold); color: var(--text-strong); margin-block-end: var(--space-3); }
.studio-subhead { margin: var(--space-3) 0 var(--space-2); font-size: var(--fs-200); font-weight: var(--fw-semibold); color: var(--text-strong); }
.studio-empty-note { margin: 0; font-size: var(--fs-200); color: var(--text-muted); }
.studio-progress { display: flex; align-items: center; gap: var(--space-2); margin: var(--space-2) 0; font-size: var(--fs-200); color: var(--text-default); }
.studio-progress.hidden { display: none; }

/* ─── New carousel ──────────────────────────────────────────────────────── */
.studio-new { margin-block-end: var(--space-4); }
.studio-new-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); column-gap: var(--space-4); }
.studio-plan-launch { display: flex; align-items: stretch; flex-wrap: wrap; gap: var(--space-2); }
.studio-plan-count { inline-size: auto; min-inline-size: 0; }
.studio-color {
    inline-size: var(--tap-target); block-size: var(--tap-target); flex-shrink: 0;
    padding: var(--space-1);
    border: 1px solid var(--border-subtle); border-radius: var(--radius-sm);
    background: var(--surface-sunken); cursor: pointer;
}
.studio-color:disabled { opacity: var(--opacity-disabled); cursor: not-allowed; }

/* The picker scrolls inside itself: 34 lessons must not push Generate off the page. */
.studio-lessons {
    display: flex; flex-direction: column; gap: var(--space-3);
    max-block-size: 14rem; overflow-y: auto; overscroll-behavior: contain;
    padding: var(--space-3);
}
.lesson-group { display: flex; flex-direction: column; gap: var(--space-2); }
.lesson-group-title { margin: 0; font-size: var(--fs-100); font-weight: var(--fw-semibold); color: var(--text-muted); }
.lesson-chips { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: var(--space-2); }
.lesson-chip-item { display: inline-flex; align-items: stretch; gap: var(--space-1); max-inline-size: 100%; }
.lesson-chip {
    display: inline-flex; align-items: center; gap: var(--space-2);
    min-block-size: var(--tap-target); min-inline-size: 0; max-inline-size: 100%;
    padding-inline: var(--space-3);
    border-radius: var(--radius-pill);
    border: 1px solid var(--border-subtle);
    background: var(--surface-glass);
    color: var(--text-default);
    font-size: var(--fs-100);
    cursor: pointer;
    transition: background var(--motion-fast) var(--ease-standard), border-color var(--motion-fast) var(--ease-standard);
}
.lesson-chip [data-lucide] { inline-size: var(--icon-sm); block-size: var(--icon-sm); flex-shrink: 0; }
.lesson-chip:hover:not(:disabled) { border-color: var(--border-strong); background: var(--surface-glass-hover); }
.lesson-chip.is-picked { background: var(--accent-soft); color: var(--accent-text); border-color: color-mix(in srgb, var(--accent) 45%, transparent); }
.lesson-chip:disabled { cursor: not-allowed; }
.lesson-chip:disabled:not(.is-picked) { opacity: 0.55; }
.lesson-chip.is-unindexed { border-style: dashed; }
.lesson-chip-no { font-weight: var(--fw-semibold); font-variant-numeric: tabular-nums; }
.lesson-chip-title { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-inline-size: 0; }
/* One info button per lesson, but quiet: the lesson is the thing, not its button. */
.lesson-chip-info { border-radius: var(--radius-pill); border-color: transparent; background: transparent; flex-shrink: 0; }

.studio-points { margin: 0; padding-inline-start: var(--space-5); display: grid; gap: var(--space-2); font-size: var(--fs-200); }
.moment-grid {
    list-style: none; margin: 0; padding: 0;
    display: grid; grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: var(--space-3);
}
.moment-tile { display: flex; flex-direction: column; gap: var(--space-1); min-inline-size: 0; font-size: var(--fs-100); }
.moment-pick {
    display: flex; flex-direction: column; gap: var(--space-1); align-items: stretch;
    inline-size: 100%; padding: var(--space-2);
    border: 1px solid var(--border-subtle); border-radius: var(--radius-sm);
    background: var(--surface-glass); color: var(--text-default);
    text-align: start; cursor: pointer;
}
.moment-pick:hover { border-color: var(--border-strong); background: var(--surface-glass-hover); }
/* Selected is a ring INSIDE the box; focus keeps the global ring outside it, and a focused
   selected tile shows both. */
.moment-pick.is-current { border-color: var(--accent-text); box-shadow: inset 0 0 0 2px var(--accent-text); }
.moment-pick.is-current:focus-visible { box-shadow: inset 0 0 0 2px var(--accent-text), 0 0 0 var(--ring) var(--accent-ring); }
.moment-thumb {
    aspect-ratio: 16 / 9; overflow: hidden;
}
.moment-thumb [data-lucide] { inline-size: var(--icon-lg); block-size: var(--icon-lg); }
.moment-meta { color: var(--text-muted); font-variant-numeric: tabular-nums; }
.moment-desc { color: var(--text-default); line-height: var(--lh-normal); }
.moment-tile .badge, .moment-pick .badge { align-self: flex-start; }

/* ─── Plan my week ──────────────────────────────────────────────────────── */
.studio-plan { margin-block-end: var(--space-4); }
.plan-list { list-style: none; margin: 0 0 var(--space-4); padding: 0; display: grid; gap: var(--space-3); }
.plan-item {
    display: flex; flex-direction: column; gap: var(--space-2);
    padding: var(--space-4);
}
.plan-item.is-dropped { opacity: var(--opacity-off); border-style: dashed; }
.plan-item-head { display: flex; align-items: baseline; gap: var(--space-2); }
.plan-item-num { font-weight: var(--fw-semibold); color: var(--text-muted); font-variant-numeric: tabular-nums; }
.plan-item-title { font-size: var(--fs-200); color: var(--text-strong); }
.plan-item-meta { display: flex; flex-wrap: wrap; gap: var(--space-2); margin: 0; }
.plan-item-why { margin: 0; font-size: var(--fs-200); color: var(--text-default); line-height: var(--lh-normal); }
.plan-item-foot { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: var(--space-2); }
.plan-run { display: inline-flex; align-items: center; gap: var(--space-2); font-size: var(--fs-100); }
.plan-run [data-lucide] { inline-size: var(--icon-sm); block-size: var(--icon-sm); flex-shrink: 0; }
.plan-run a { color: var(--accent-text); text-decoration: underline; text-underline-offset: 2px; }

/* ─── Drafts grid ───────────────────────────────────────────────────────── */
.studio-drafts .section-title { margin-block-end: var(--space-3); }
.studio-draft-grid { list-style: none; margin: 0; padding: 0; }
.draft-card {
    display: grid; grid-template-columns: 112px minmax(0, 1fr); gap: var(--space-3);
    padding: var(--space-4); align-items: start;
}
.draft-cover {
    aspect-ratio: 4 / 5;
}
.draft-cover [data-lucide] { inline-size: var(--icon-xl); block-size: var(--icon-xl); }
.draft-card-body { display: flex; flex-direction: column; gap: var(--space-2); min-inline-size: 0; }
.draft-card-head { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--space-2); }
.draft-title { margin: 0; font-size: var(--fs-200); line-height: var(--lh-normal); min-inline-size: 0; overflow-wrap: anywhere; }
.draft-title a { color: var(--text-strong); text-decoration: none; }
.draft-title a:hover { text-decoration: underline; text-underline-offset: 2px; }
.draft-card-tags { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); margin: 0; }

/* ─── Editor ────────────────────────────────────────────────────────────── */
.studio-editor-head { margin-block-end: var(--space-4); display: flex; flex-direction: column; gap: var(--space-2); }
.studio-editor-titlebar { display: flex; align-items: flex-start; justify-content: space-between; flex-wrap: wrap; gap: var(--space-3); }
.studio-editor-title { margin: 0; font-size: var(--fs-400); font-weight: var(--fw-semibold); color: var(--text-strong); overflow-wrap: anywhere; }
.studio-danger { display: flex; justify-content: flex-end; margin-block-start: var(--space-5); }

/* Previews: a strip that scrolls sideways and snaps per slide — a swipe on a
   phone, a trackpad scroll or the arrow keys (it is focusable) elsewhere. */
.preview-scroller {
    overflow-x: auto; overscroll-behavior-inline: contain;
    scroll-snap-type: inline mandatory;
    padding-block-end: var(--space-2);
    border-radius: var(--radius-sm);
}
.preview-scroller:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.preview-strip { list-style: none; margin: 0; padding: 0; display: flex; gap: var(--space-3); }
.preview-slide {
    position: relative; flex: 0 0 auto;
    inline-size: min(72%, 260px);
    aspect-ratio: 4 / 5;
    overflow: hidden;
    scroll-snap-align: start;
    border-radius: var(--radius-sm);
    background: var(--surface-solid); border: 1px solid var(--border-subtle);
}
.preview-strip--tt .preview-slide { inline-size: min(56%, 190px); aspect-ratio: 9 / 16; }
.preview-slide img { inline-size: 100%; block-size: 100%; object-fit: cover; display: block; }
.preview-slide .skel-block { border-radius: 0; }

.slide-cards { list-style: none; margin: var(--space-3) 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-4); }
/* 16 on every side: the last field's own 12px margin completes the bottom. */
.slide-card {
    padding: var(--space-4) var(--space-4) var(--space-1);
    scroll-margin-block: calc(var(--header-height) + var(--space-4));
}
.slide-card:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.slide-card.has-problems { border-color: var(--border-danger); }
.slide-card-head { display: flex; align-items: center; flex-wrap: wrap; gap: var(--space-2); margin-block-end: var(--space-3); }
.slide-card-name { margin: 0; font-size: var(--fs-200); color: var(--text-strong); flex: 1 1 auto; min-inline-size: 0; }
.slide-card-actions { display: flex; align-items: center; flex-wrap: wrap; gap: var(--space-1); }
.studio-field { margin-block-end: var(--space-3); }
.studio-field-num { max-inline-size: 120px; }
.studio-field-short { max-inline-size: 120px; }
.studio-field-time { max-inline-size: 160px; }
.studio-accent-row .field-row { max-inline-size: 260px; }

.studio-items { margin: 0 0 var(--space-3); min-inline-size: 0; }
.studio-items:focus-visible { outline: 2px solid var(--accent); outline-offset: 4px; border-radius: var(--radius-sm); }
.studio-items-head { inline-size: 100%; }
.studio-item-list { list-style: none; margin: 0 0 var(--space-2); padding: 0; display: flex; flex-direction: column; gap: var(--space-2); }
.studio-item { display: flex; align-items: flex-start; gap: var(--space-2); }
.studio-item > .field { flex: 1; min-inline-size: 0; }
.studio-item-num {
    flex-shrink: 0; min-inline-size: 22px; padding-block-start: var(--space-3);
    color: var(--text-muted); font-size: var(--fs-100); font-weight: var(--fw-semibold); font-variant-numeric: tabular-nums;
}
.studio-item-fields { flex: 1; min-inline-size: 0; display: grid; gap: 0 var(--space-3); }
.studio-item > .icon-btn { margin-block-start: var(--space-5); }
.studio-compare-row,
.studio-compare-labels { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 0 var(--space-3); }

.shot-field {
    display: flex; align-items: flex-start; gap: var(--space-3);
    padding: var(--space-2);
}
.shot-field.is-invalid { border-color: color-mix(in srgb, var(--danger) 55%, transparent); }
.shot-thumb {
    flex: 0 0 auto; inline-size: 96px; aspect-ratio: 16 / 9;
    overflow: hidden;
}
.shot-thumb [data-lucide] { inline-size: var(--icon-lg); block-size: var(--icon-lg); }
.shot-info { display: flex; flex-direction: column; gap: var(--space-2); min-inline-size: 0; font-size: var(--fs-200); }

.problem-list {
    margin: var(--space-2) 0 0; padding-inline-start: var(--space-5);
    display: grid; gap: var(--space-1);
    font-size: var(--fs-100); color: var(--danger-text); line-height: var(--lh-normal);
}
.problem-list li { overflow-wrap: anywhere; }
.slide-card > .problem-list { margin-block-end: var(--space-3); }
.studio-problems {
    padding: var(--space-3); margin-block-end: var(--space-3);
    border-radius: var(--radius-sm);
    background: var(--danger-soft);
    border: 1px solid var(--border-danger);
}
.studio-problems:focus-visible { outline: 2px solid var(--danger); outline-offset: 2px; }
.studio-problems-title { display: flex; align-items: center; gap: var(--space-2); margin: 0; font-weight: var(--fw-semibold); color: var(--danger-text); }
.studio-problems-title [data-lucide] { inline-size: var(--icon-md); block-size: var(--icon-md); flex-shrink: 0; }
.studio-problems .problem-list li { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: var(--space-2); }
.studio-problems .problem-list .btn { color: var(--text-strong); }

.studio-area-main .form-hint [data-lucide] { inline-size: var(--icon-sm); block-size: var(--icon-sm); vertical-align: -2px; }
.studio-line { font-weight: var(--fw-semibold); color: var(--text-strong); }

/* Save sits under the forms it saves and follows the operator down them.
   `.studio-savebar` itself is defined once, under "Editor: the action bar". */
.studio-savebar-row { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: var(--space-3); }
.studio-savebar-row .text-meta { margin: 0; }
.studio-add-slide { display: flex; align-items: stretch; flex-wrap: wrap; gap: var(--space-2); margin-block-start: var(--space-3); }
.studio-add-slide .select { inline-size: auto; min-inline-size: 180px; }
.studio-custom-time { margin-block-start: var(--space-2); }

/* ─── Settings ──────────────────────────────────────────────────────────── */
.studio-settings { display: flex; flex-direction: column; gap: var(--space-4); margin-block-end: var(--space-4); }
.studio-settings .section-title { margin-block-end: var(--space-3); }
.studio-settings-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); column-gap: var(--space-4); }
.studio-hex { font-family: var(--font-mono); font-size: var(--fs-100); color: var(--text-default); }
.studio-swatches { list-style: none; margin: 0 0 var(--space-2); padding: 0; display: flex; flex-wrap: wrap; gap: var(--space-3); }
.studio-swatch { display: inline-flex; align-items: center; gap: var(--space-2); }
.studio-preview-card {
    display: flex; flex-direction: column;
    padding: var(--space-4); margin-block-end: var(--space-4);
}
.studio-preview-card .studio-subhead:first-child { margin-block-start: 0; }
.studio-preview-line { margin: 0; font-size: var(--fs-200); color: var(--text-strong); }
.studio-preview-dm { white-space: pre-wrap; font-size: var(--fs-200); color: var(--text-default); line-height: var(--lh-loose); overflow-wrap: anywhere; }

.studio-worker-list { list-style: none; margin: var(--space-3) 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-2); }
.studio-worker {
    display: flex; align-items: center; flex-wrap: wrap; gap: var(--space-3);
    padding: var(--space-3);
    border: 1px solid var(--border-subtle); border-radius: var(--radius-sm);
}
.studio-worker.is-revoked { opacity: var(--opacity-off); }
.studio-worker-main { display: flex; flex-direction: column; gap: var(--space-1); flex: 1 1 200px; min-inline-size: 0; }
.studio-worker-main .text-meta { margin: 0; }
.studio-worker-form { margin-block-start: var(--space-3); }
.studio-worker-form .field { min-inline-size: 0; }
/* The one-time token: loud, because it is the only chance to copy it. */
.studio-token {
    display: flex; flex-direction: column; gap: var(--space-2); align-items: flex-start;
    padding: var(--space-4); margin-block: var(--space-3);
    border-radius: var(--radius-md);
    background: var(--warning-soft);
    border: 1px solid var(--border-warning);
}
.studio-token .field-row { inline-size: 100%; }
.studio-token-title { display: flex; align-items: center; gap: var(--space-2); margin: 0; color: var(--text-strong); }
.studio-token-title [data-lucide] { inline-size: var(--icon-md); block-size: var(--icon-md); flex-shrink: 0; }
.studio-token-warning { margin: 0; font-size: var(--fs-200); color: var(--warning-text); font-weight: var(--fw-semibold); }
.studio-token .form-hint { margin: 0; }

/* ─── The flow: stepper, callouts, meters (Studio UX v2) ────────────────── */
.studio-toolbar { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: var(--space-3); }
.studio-stepper {
    list-style: none; margin: 0; padding: 0;
    display: flex; align-items: center; flex-wrap: wrap; gap: var(--space-2);
    counter-reset: none;
}
.studio-step {
    display: inline-flex; align-items: center; gap: var(--space-2);
    font-size: var(--fs-100); color: var(--text-muted);
}
/* The line between steps: logical, so it sits on the reading-direction side. */
.studio-step + .studio-step::before {
    content: ''; inline-size: 20px; block-size: 1px; background: var(--border-strong);
    margin-inline-end: var(--space-1);
}
.studio-step-num { font-variant-numeric: tabular-nums; }
.studio-step-num [data-lucide] { inline-size: var(--icon-sm); block-size: var(--icon-sm); }
.studio-step.is-current { color: var(--text-strong); font-weight: var(--fw-semibold); }
.studio-step.is-current .studio-step-num { background: var(--gradient-brand); border-color: transparent; color: var(--text-on-accent); }
.studio-step.is-done .studio-step-num { background: var(--success-soft); border-color: transparent; color: var(--success-text); }

.studio-callout { color: var(--text-default); }
.studio-callout > [data-lucide] { inline-size: 18px; block-size: 18px; flex-shrink: 0; margin-block-start: 2px; }
.studio-callout > svg { color: var(--info-text); }
.studio-callout.is-warning { background: var(--warning-soft); border-color: var(--border-warning); }
.studio-callout.is-warning > svg { color: var(--warning-text); }
.studio-callout-body { display: flex; flex-direction: column; gap: var(--space-2); min-inline-size: 0; flex: 1; }
.studio-callout-body p { margin: 0; line-height: var(--lh-normal); }
.studio-callout-title { font-weight: var(--fw-semibold); color: var(--text-strong); }
.studio-callout-row { flex-direction: row; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: var(--space-2) var(--space-3); }
.studio-howto summary { cursor: pointer; color: var(--accent-text); font-weight: var(--fw-semibold); min-block-size: var(--tap-target); display: flex; align-items: center; }
.studio-howto ol { margin: 0; padding-inline-start: var(--space-5); display: grid; gap: var(--space-1); }
.studio-notices { display: flex; flex-direction: column; gap: var(--space-3); }
.studio-notices:not(:empty) { margin-block-end: var(--space-4); }
.studio-status-item.is-wide { grid-column: 1 / -1; }
.studio-status-item .row { margin-block-start: var(--space-1); }

.studio-meter {
    appearance: none; -webkit-appearance: none;
    inline-size: 100%; block-size: 6px;
    border: 0;
}
.studio-meter::-webkit-progress-bar { background: var(--surface-glass-hover); border-radius: var(--radius-pill); }
.studio-meter::-webkit-progress-value { background: var(--accent); border-radius: var(--radius-pill); transition: inline-size var(--motion-base) var(--ease-standard); }
.studio-meter::-moz-progress-bar { background: var(--accent); border-radius: var(--radius-pill); }

/* Indeterminate: the request is one call, so this says "working", never "63%". */
.studio-indeterminate { position: relative; block-size: 4px; }
.studio-indeterminate span {
    position: absolute; inset-block: 0; inset-inline-start: 0; inline-size: 35%;
    border-radius: inherit; background: var(--accent);
    animation: studio-sweep 1.6s linear infinite; /* linear: an infinite sweep hitches at every loop boundary on an easing curve */
}
@keyframes studio-sweep { from { transform: translateX(-100%); } to { transform: translateX(300%); } }
html[dir='rtl'] .studio-indeterminate span { animation-name: studio-sweep-rtl; }
@keyframes studio-sweep-rtl { from { transform: translateX(100%); } to { transform: translateX(-300%); } }

/* ─── Step 1: the form ──────────────────────────────────────────────────── */
.studio-new-head { align-items: flex-start; }
.studio-new-heading { display: flex; flex-direction: column; gap: var(--space-1); min-inline-size: 0; }
.studio-lede { margin: 0; }
/* `.studio-seg` is still emitted by the pages; its pressed look now lives on
   `.segmented` itself (see the Segmented control section). */
.studio-source { inline-size: max-content; max-inline-size: 100%; margin-block-end: var(--space-3); }
.studio-source-body { margin-block-end: var(--space-2); }
/* Laid out by the `.input-affix` rule it shares; only its glyph is a size smaller. */
.studio-lesson-search { margin-block: var(--space-2); }
.studio-lesson-search > [data-lucide] { inline-size: var(--icon-md); block-size: var(--icon-md); }
.studio-lesson-search .field { padding-inline-start: calc(var(--space-3) + 24px); }
.studio-no-match { display: flex; align-items: center; flex-wrap: wrap; gap: var(--space-2); margin-block-start: var(--space-2); }
.studio-picked-list { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: var(--space-2); }
.picked-chip {
    display: inline-flex; align-items: center; gap: var(--space-2);
    min-block-size: var(--tap-target); max-inline-size: 100%;
    padding-inline: var(--space-3);
    border-radius: var(--radius-pill);
    background: var(--accent-soft); color: var(--accent-text);
    border: 1px solid color-mix(in srgb, var(--accent) 45%, transparent);
    font-size: var(--fs-100); cursor: pointer;
}
.picked-chip [data-lucide] { inline-size: var(--icon-sm); block-size: var(--icon-sm); flex-shrink: 0; }
.picked-chip:hover { background: color-mix(in srgb, var(--accent) 24%, transparent); }
.studio-picked-summary {
    margin: var(--space-2) 0 0; font-size: var(--fs-100); color: var(--text-default); line-height: var(--lh-normal);
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.studio-index-note { display: flex; flex-direction: column; gap: var(--space-2); margin-block-start: var(--space-3); }
.studio-index-note-text { display: flex; align-items: center; flex-wrap: wrap; gap: var(--space-2); margin: 0; font-size: var(--fs-200); }
.studio-index-note .btn { align-self: flex-start; }

.studio-setup {
    display: flex; flex-direction: column; gap: var(--space-2);
    padding: var(--space-4); border-radius: var(--radius-md);
    border: 1px dashed var(--border-strong); background: var(--surface-sunken);
}
.studio-setup-title { margin: 0; font-size: var(--fs-300); font-weight: var(--fw-semibold); color: var(--text-strong); }
.studio-setup > .form-hint { margin: 0; }
/* Not `.setup-steps`: that is the first-run screen's numbered list, and this
   unnumbered one used to clobber it from further down the file. */
.studio-setup-steps { list-style: none; margin: var(--space-2) 0 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-3); }
.setup-step { display: flex; align-items: flex-start; gap: var(--space-3); }
.setup-step-mark [data-lucide] { inline-size: var(--icon-sm); block-size: var(--icon-sm); }
.setup-step.is-done .setup-step-mark { background: var(--success-soft); color: var(--success-text); border-color: transparent; }
.setup-step-body { display: flex; flex-direction: column; gap: var(--space-2); min-inline-size: 0; flex: 1; }
.setup-step-body .btn { align-self: flex-start; }
.setup-step-body .form-hint { margin: 0; }
.setup-step-title { margin: 0; font-weight: var(--fw-semibold); color: var(--text-strong); }
.setup-step.is-done .setup-step-title { color: var(--text-default); }
.studio-path { font-family: var(--font-mono); font-size: var(--fs-100); overflow-wrap: anywhere; }
.studio-setup-alt { display: flex; align-items: center; flex-wrap: wrap; gap: var(--space-2); margin: var(--space-2) 0 0; font-size: var(--fs-200); }

.studio-more {
    margin-block: var(--space-3);
}
.studio-more-toggle {
    display: flex; align-items: center; flex-wrap: wrap; gap: var(--space-1) var(--space-3);
    min-block-size: var(--tap-target); padding: var(--space-2) var(--space-4);
    cursor: pointer; list-style: none; border-radius: var(--radius-md);
}
.studio-more-toggle::-webkit-details-marker { display: none; }
.studio-more-toggle:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.studio-more-label { display: inline-flex; align-items: center; gap: var(--space-2); font-weight: var(--fw-semibold); color: var(--text-strong); }
.studio-more-label [data-lucide] { inline-size: var(--icon-md); block-size: var(--icon-md); }
.studio-more-summary { font-size: var(--fs-100); color: var(--text-muted); }
.studio-more-body { padding: var(--space-2) var(--space-4) var(--space-3); border-block-start: 1px solid var(--border-subtle); }
.swatch-row { list-style: none; margin: 0 0 var(--space-2); padding: 0; display: flex; flex-wrap: wrap; gap: var(--space-2); }
.swatch-btn {
    display: inline-flex; align-items: center; justify-content: center;
    inline-size: var(--tap-target); block-size: var(--tap-target);
    border-radius: var(--radius-pill); border: 2px solid transparent;
    background: var(--surface-glass); color: var(--text-default); cursor: pointer; padding: 0;
}
.swatch-btn [data-lucide] { inline-size: var(--icon-md); block-size: var(--icon-md); }
.swatch-btn[aria-pressed='true'] { border-color: var(--accent-text); box-shadow: inset 0 0 0 2px var(--accent-text); }
.swatch-btn[aria-pressed='true']:focus-visible { box-shadow: inset 0 0 0 2px var(--accent-text), 0 0 0 var(--ring) var(--accent-ring); }
.swatch-btn:disabled { opacity: var(--opacity-disabled); cursor: not-allowed; }
.swatch-btn:focus-visible, .picked-chip:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.swatch-dot { inline-size: 26px; block-size: 26px; border-radius: var(--radius-pill); border: 1px solid var(--border-strong); display: inline-block; }
.studio-accent-custom { max-inline-size: 260px; }
.studio-accent-choice { margin-block-end: var(--space-2); }
.studio-new-actions { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: var(--space-2) var(--space-4); margin-block-start: var(--space-3); }
.studio-new-actions .form-hint { margin: 0; }
.studio-generate { min-inline-size: 220px; min-block-size: 48px; font-size: var(--fs-300); }

/* ─── Step 2: writing ───────────────────────────────────────────────────── */
.studio-progress-card { display: flex; flex-direction: column; gap: var(--space-3); }
.studio-progress-card .section-title { margin: 0; }
.studio-gen-summary { margin: 0; }
.studio-stages { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-2); }
.studio-stage { display: flex; align-items: center; gap: var(--space-3); font-size: var(--fs-200); color: var(--text-muted); min-block-size: 28px; }
.studio-stage-mark [data-lucide] { inline-size: var(--icon-sm); block-size: var(--icon-sm); }
.studio-stage.is-done { color: var(--text-default); }
.studio-stage.is-done .studio-stage-mark { background: var(--success-soft); color: var(--success-text); border-color: transparent; }
.studio-stage.is-current { color: var(--text-strong); font-weight: var(--fw-semibold); }
.studio-stage.is-current .studio-stage-mark { border-color: transparent; }
.studio-gen-time { display: flex; align-items: center; gap: var(--space-2); margin: 0; font-size: var(--fs-200); color: var(--text-default); }
.studio-gen-clock, .studio-render-clock { font-variant-numeric: tabular-nums; font-weight: var(--fw-semibold); }

/* ─── Drafts ────────────────────────────────────────────────────────────── */
.studio-drafts-empty { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: var(--space-5); }
.studio-empty-copy { display: flex; flex-direction: column; gap: var(--space-2); max-inline-size: 46ch; }
.studio-empty-copy .form-hint { margin: 0; }
.studio-empty-title { margin: 0; font-size: var(--fs-300); font-weight: var(--fw-semibold); color: var(--text-strong); }
.draft-example { margin: 0; display: flex; flex-direction: column; gap: var(--space-2); inline-size: min(100%, 380px); }
.draft-example figcaption { margin: 0; }
.draft-card--example { opacity: 0.85; pointer-events: none; }
.draft-cover--example {
    align-items: flex-end; justify-content: flex-start; padding: var(--space-2);
    background: linear-gradient(160deg, #1b1b22, #0b0b10); border-color: transparent;
}
.draft-example-title { color: #f5f5f7; font-size: var(--fs-100); font-weight: var(--fw-semibold); line-height: var(--lh-normal); }
.draft-card-stage { display: flex; align-items: center; gap: var(--space-2); margin: 0; font-size: var(--fs-100); color: var(--text-default); }

/* ─── Editor: layout ────────────────────────────────────────────────────── */
.studio-editor { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--space-4); align-items: start; }
.studio-area-previews,
.studio-area-main { display: flex; flex-direction: column; gap: var(--space-4); min-inline-size: 0; }
.studio-editor-titlebar .row { align-items: center; }

/* ─── Editor: the phone ─────────────────────────────────────────────────── */
.phone {
    margin-inline: auto; inline-size: min(100%, 340px);
    padding: var(--space-2);
    border-radius: var(--radius-2xl);
    background: var(--surface-solid);
    border: 1px solid var(--border-strong);
    box-shadow: var(--elev-2);
}
.phone--tt { inline-size: min(100%, 280px); }
.phone-bar {
    display: flex; align-items: center; gap: var(--space-2);
    padding: var(--space-1) var(--space-2) var(--space-2);
    font-size: var(--fs-100); font-weight: var(--fw-semibold); color: var(--text-strong);
}
.phone-avatar { inline-size: 24px; block-size: 24px; border-radius: var(--radius-pill); background: var(--gradient-brand); flex-shrink: 0; }
.phone-handle { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.phone-stage {
    position: relative; overflow: hidden;
    aspect-ratio: 4 / 5;
    border-radius: var(--radius-xl);
    background: var(--surface-sunken);
    cursor: pointer; touch-action: pan-y; user-select: none; -webkit-user-select: none;
}
.phone--tt .phone-stage { aspect-ratio: 9 / 16; border-radius: 22px; }
.phone--ig .phone-stage { border-radius: var(--radius-sm); }
.phone-stage img { display: block; -webkit-user-drag: none; }
.phone-stage:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.phone-stage.is-stale img { opacity: 0.7; filter: saturate(0.85); }
.phone-badge {
    position: absolute; inset-block-start: var(--space-2); inset-inline-start: var(--space-2);
    padding: var(--space-1) var(--space-3); border-radius: var(--radius-pill);
    background: var(--surface-overlay); color: var(--text-strong);
    backdrop-filter: blur(var(--blur-glass)) saturate(180%); -webkit-backdrop-filter: blur(var(--blur-glass)) saturate(180%);
    font-size: var(--fs-100); font-weight: var(--fw-semibold); box-shadow: var(--elev-1);
}
.phone-wire {
    position: absolute; inset: 0;
    display: flex; flex-direction: column; justify-content: center; gap: var(--space-2);
    padding: var(--space-5);
}
.phone-wire .skel-block { position: absolute; inset: 0; }
.phone-wire-kind, .phone-wire-title { position: relative; }
.phone-wire-kind { font-size: var(--fs-100); color: var(--text-muted); font-weight: var(--fw-semibold); }
.phone-wire-title { font-size: var(--fs-400); font-weight: var(--fw-semibold); color: var(--text-strong); line-height: 1.4; overflow-wrap: anywhere; }
.phone-nav { display: flex; align-items: center; justify-content: space-between; gap: var(--space-2); padding-block-start: var(--space-2); }
.phone-count { font-size: var(--fs-200); color: var(--text-default); font-variant-numeric: tabular-nums; }
.phone-caption {
    margin: var(--space-1) var(--space-2) var(--space-1); font-size: var(--fs-100); color: var(--text-default); line-height: var(--lh-normal);
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.studio-render-state { margin-block-start: var(--space-3); display: flex; flex-direction: column; gap: var(--space-2); }
.studio-render-state .text-meta, .studio-render-state .form-hint { margin: 0; }
.studio-render-line { display: flex; align-items: center; flex-wrap: wrap; gap: var(--space-2); font-size: var(--fs-200); color: var(--text-default); }
.studio-render-line.is-warning { color: var(--warning-text); }

.slide-rail {
    list-style: none; margin: var(--space-3) 0 0; padding: 3px 3px var(--space-2);
    display: flex; gap: var(--space-2);
    overflow-x: auto; overscroll-behavior-inline: contain; scroll-snap-type: inline proximity;
}
.rail-item { flex: 0 0 auto; scroll-snap-align: start; }
.rail-tab {
    position: relative; display: block; overflow: hidden;
    inline-size: 56px; aspect-ratio: 4 / 5; padding: 0;
    border: 2px solid transparent;
    background: var(--surface-solid); cursor: pointer;
    /* Brought into view clear of the sticky action bar, not under it. */
    scroll-margin-block: var(--space-3) 112px; scroll-margin-inline: var(--space-3);
}
.slide-rail--tt .rail-tab { inline-size: 46px; aspect-ratio: 9 / 16; }
.rail-tab img { display: block; }
.rail-tab .skel-block { border-radius: 0; }
/* The thumbnail covers the tab's whole padding box, so an inset shadow on the tab itself would
   be painted under the image: the inner ring is an overlay. The tab's own box-shadow is left to
   the global focus ring. */
.rail-tab.is-selected { border-color: var(--accent); }
.rail-tab.is-selected::after {
    content: ''; position: absolute; inset: 0;
    border-radius: inherit; box-shadow: inset 0 0 0 2px var(--accent-text);
    pointer-events: none;
}
.rail-tab.has-problems:not(.is-selected) { border-color: color-mix(in srgb, var(--danger) 60%, transparent); }
.rail-tab:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.studio-head-meta { flex-wrap: wrap; row-gap: var(--space-2); }
.rail-tab .slide-num { inset-block-end: 3px; inset-inline-start: 3px; }
.rail-wire { display: flex; align-items: center; justify-content: center; block-size: 100%; padding: 2px; font-size: var(--fs-100); line-height: 1.2; color: var(--text-muted); text-align: center; overflow-wrap: anywhere; }
.rail-flag {
    position: absolute; inset-block-start: 4px; inset-inline-end: 4px;
    inline-size: 10px; block-size: 10px; border-radius: var(--radius-pill);
    background: var(--danger); box-shadow: 0 0 0 2px var(--surface-solid);
}

/* ─── Editor: the slide form ────────────────────────────────────────────── */
.slide-kind-row { display: grid; grid-template-columns: minmax(0, 240px); gap: var(--space-1); }
.slide-kind-row .form-label { margin: 0; }
.slide-kind-row .form-hint { margin: 0; }
.studio-problems .problem-text { display: flex; flex-direction: column; gap: 2px; min-inline-size: 0; }
.problem-where { color: var(--text-strong); font-size: var(--fs-100); }
.problem-raw { color: var(--danger-text); font-size: var(--fs-100); }

/* ─── Editor: the action bar ────────────────────────────────────────────── */
.studio-savebar {
    position: sticky; inset-block-end: max(var(--space-3), env(safe-area-inset-bottom)); z-index: 6;
    margin-block-start: var(--space-4);
    padding: var(--space-3) var(--space-4);
    background: var(--surface-overlay);
    backdrop-filter: blur(var(--blur-glass)) saturate(180%); -webkit-backdrop-filter: blur(var(--blur-glass)) saturate(180%);
    box-shadow: var(--elev-2);
}
.studio-dirty { display: inline-flex; align-items: center; flex-wrap: wrap; gap: var(--space-2); margin: 0; font-size: var(--fs-200); color: var(--text-default); }
.studio-dirty-dot { inline-size: 8px; block-size: 8px; border-radius: var(--radius-pill); background: var(--success); flex-shrink: 0; }
.studio-dirty.is-dirty { color: var(--text-strong); font-weight: var(--fw-semibold); }
.studio-dirty.is-dirty .studio-dirty-dot { background: var(--warning); }
.studio-savebar-actions { display: flex; align-items: center; flex-wrap: wrap; gap: var(--space-2); }
.studio-kbd {
    font-family: var(--font-mono); font-size: var(--fs-100); color: var(--text-muted);
    padding: 2px var(--space-2); border: 1px solid var(--border-strong); border-radius: var(--radius-xs);
}
@media (hover: none) { .studio-kbd { display: none; } }

/* ─── Editor: schedule ──────────────────────────────────────────────────── */
.studio-summary {
    margin-block: var(--space-2) var(--space-4);
    padding: var(--space-4);
}
.studio-summary-title { margin: 0 0 var(--space-2); font-size: var(--fs-200); font-weight: var(--fw-semibold); color: var(--text-strong); }
.studio-summary-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-2); }
.studio-summary-list li { display: flex; align-items: flex-start; gap: var(--space-2); font-size: var(--fs-200); color: var(--text-default); line-height: var(--lh-normal); }
.studio-summary-list [data-lucide] { inline-size: var(--icon-md); block-size: var(--icon-md); flex-shrink: 0; margin-block-start: 2px; }
.studio-summary-list svg { color: var(--text-muted); }

/* ─── Screenshot picker ─────────────────────────────────────────────────── */
.modal-content:has(.shot-picker) { max-width: min(980px, calc(100vw - var(--space-5))); }
.shot-picker-controls { display: flex; align-items: flex-end; flex-wrap: wrap; gap: 0 var(--space-5); }
.shot-picker-controls .form-group:first-child { flex: 1 1 260px; }
.shot-picker { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--space-4); }
.shot-hero { display: flex; flex-direction: column; gap: var(--space-1); }
.shot-hero-frame {
    overflow: hidden;
    aspect-ratio: 16 / 9;
}
.shot-hero-frame img { inline-size: 100%; block-size: 100%; object-fit: contain; }
.shot-hero-meta { font-size: var(--fs-100); color: var(--text-muted); font-variant-numeric: tabular-nums; }
.shot-hero-desc { font-size: var(--fs-200); color: var(--text-strong); line-height: var(--lh-normal); }
.shot-picker-grid .text-meta { margin: 0 0 var(--space-2); }
.moment-pick:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
@media (min-width: 860px) {
    .shot-picker { grid-template-columns: minmax(0, 5fr) minmax(0, 6fr); align-items: start; }
    .shot-hero { position: sticky; inset-block-start: 0; }
    .shot-picker-grid { max-block-size: 60dvh; overflow-y: auto; padding: 2px; }
}

/* ─── Settings ──────────────────────────────────────────────────────────── */
.studio-section-lede { margin: calc(-1 * var(--space-2)) 0 var(--space-4); }
.studio-item.has-problems { flex-wrap: wrap; }
.studio-item > .problem-list { flex-basis: 100%; margin-block-start: 0; }
.brand-sample { display: flex; align-items: center; flex-wrap: wrap; gap: var(--space-4); margin-block: var(--space-2) var(--space-1); }
.brand-sample-slide {
    display: flex; flex-direction: column; justify-content: flex-end; gap: var(--space-1);
    inline-size: 150px; aspect-ratio: 4 / 5; padding: var(--space-3);
    border-radius: var(--radius-sm); border: 1px solid var(--border-strong);
    background: #0b0b10; color: #f5f5f7;
}
.brand-sample-kicker { font-size: var(--fs-100); font-weight: var(--fw-semibold); }
.brand-sample-title { font-size: var(--fs-200); font-weight: var(--fw-semibold); line-height: 1.3; }
.brand-sample-sign { font-size: var(--fs-100); letter-spacing: 0.04em; }
.brand-sample-dots { display: flex; flex-wrap: wrap; gap: var(--space-2); }

/* ─── Wide screens: the preview stays in view while its slide is edited ─── */
@media (min-width: 1100px) {
    .studio-editor { grid-template-columns: minmax(320px, 400px) minmax(0, 1fr); }
    .phone { inline-size: min(100%, 280px); }
    .phone--tt { inline-size: min(100%, 230px); }
    /* Room for the action bar under it, so the phone's own controls are never covered. */
    .studio-area-previews {
        position: sticky; inset-block-start: calc(var(--header-height) + var(--space-3));
        max-block-size: calc(100dvh - var(--header-height) - 104px);
        overflow-y: auto; overscroll-behavior: contain;
        scrollbar-width: thin;
    }
}

/* ─── Phones: content stacks, the preview comes first, the bar sits on the edge ─── */
@media (max-width: 560px) {
    .studio-compare-row, .studio-compare-labels { grid-template-columns: minmax(0, 1fr); }
    .draft-card { grid-template-columns: 88px minmax(0, 1fr); }
    .studio-new-actions > .btn, .studio-plan-launch { inline-size: 100%; }
    .studio-plan-launch > * { flex: 1 1 auto; }
    .studio-source { inline-size: 100%; }
    .studio-source .btn { flex: 1; }
    .slide-card-actions { inline-size: 100%; }
    .shot-field { flex-direction: column; }
    .shot-thumb { inline-size: 100%; }
    .studio-tabs { inline-size: 100%; }
    .studio-step-label { display: none; }
    .studio-step.is-current .studio-step-label { display: inline; }
    .studio-lessons { max-block-size: 18rem; }
    .phone { inline-size: min(100%, 320px); }
    .phone--tt { inline-size: min(100%, 250px); }
    .studio-savebar {
        inset-block-end: 0;
        border-end-start-radius: 0; border-end-end-radius: 0;
        padding-block-end: max(var(--space-3), env(safe-area-inset-bottom));
    }
    .studio-savebar-row { flex-direction: column; align-items: stretch; }
    .studio-savebar-actions { justify-content: flex-end; }
    .studio-savebar-actions .btn-primary, #studio-go-schedule { flex: 1; }
    .studio-generate { inline-size: 100%; }
    .studio-drafts-empty { flex-direction: column; align-items: stretch; }
}

@media (prefers-reduced-motion: reduce) {
    .studio-indeterminate span { animation: none; inline-size: 100%; opacity: 0.5; }
    .studio-meter::-webkit-progress-value { transition: none; }
}

/* ─── Help Center (#/help) ──────────────────────────────────────────────────
   Mobile first: one column, and the route decides which half shows, the list
   (#/help) or the article (#/help/<slug>). From 1100px the list sits beside the
   article, and because the grid follows the document's direction it is on the
   right in Arabic and on the left in English with no extra rule. Logical
   properties only, like the rest of this file. */
.help-layout {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: var(--space-5);
    align-items: start;
}
.help-layout[data-view='article'] .help-nav { display: none; }
.help-layout[data-view='index'] .help-main { display: none; }
.help-nav { display: flex; flex-direction: column; gap: var(--space-4); min-inline-size: 0; }
.help-main { min-inline-size: 0; }
.help-list-host:focus { outline: none; }

.help-group-title {
    font-size: var(--fs-100);
    font-weight: var(--fw-semibold);
    color: var(--text-muted);
    margin-block: var(--space-4) var(--space-2);
}
.help-group:first-child .help-group-title { margin-block-start: 0; }
.help-list, .help-results, .help-cards, .help-related-list {
    list-style: none; margin: 0; padding: 0;
    display: grid; gap: var(--space-1);
}
.help-item, .help-result {
    display: flex; align-items: flex-start; gap: var(--space-3);
    padding: var(--space-3);
    min-block-size: var(--tap-target);
    border-radius: var(--radius-md);
    color: var(--text-default);
    text-decoration: none;
    transition: background var(--motion-fast) var(--ease-standard);
}
.help-item:hover, .help-result:hover { background: var(--surface-glass-hover); }
.help-item[aria-current='page'] { background: var(--accent-soft); }
.help-item [data-lucide] { inline-size: 18px; block-size: 18px; color: var(--text-muted); margin-block-start: 2px; }
.help-item[aria-current='page'] [data-lucide] { color: var(--accent-text); }
.help-item-text { display: flex; flex-direction: column; gap: 2px; min-inline-size: 0; }
.help-item-title { font-size: var(--fs-200); font-weight: var(--fw-medium); color: var(--text-strong); }
.help-item-summary { font-size: var(--fs-100); color: var(--text-muted); line-height: var(--lh-normal); }

.help-results-count { font-size: var(--fs-100); color: var(--text-muted); margin: 0 0 var(--space-2); }
.help-result { flex-direction: column; gap: 2px; }
.help-result-title { font-size: var(--fs-200); font-weight: var(--fw-semibold); color: var(--text-strong); }
.help-result-section { font-size: var(--fs-100); color: var(--accent-text); }
.help-result-snippet { font-size: var(--fs-100); color: var(--text-default); line-height: var(--lh-normal); }
mark.help-hit {
    background: var(--accent-soft);
    color: var(--text-strong);
    border-radius: 3px;
    padding-inline: 1px;
}
.help-empty {
    display: flex; flex-direction: column; gap: var(--space-2);
    padding: var(--space-4);
}
.help-empty-title { margin: 0; font-weight: var(--fw-semibold); color: var(--text-strong); }
.help-notice {
    margin: 0 0 var(--space-3);
    padding: var(--space-3) var(--space-4);
    border-radius: var(--radius-md);
    background: var(--warning-soft);
    color: var(--text-default);
    font-size: var(--fs-200);
}

/* The reading pane: one readable measure, whatever the column's width. */
.help-article, .help-welcome {
    padding: var(--space-4);
    max-inline-size: calc(var(--measure) + 2 * var(--space-6));
}
.help-back {
    display: inline-flex; align-items: center; gap: var(--space-2);
    min-block-size: var(--tap-target);
    margin-block-end: var(--space-2);
    font-size: var(--fs-200);
    color: var(--accent-text);
    text-decoration: none;
}
.help-back [data-lucide] { inline-size: var(--icon-md); block-size: var(--icon-md); }
.help-kicker { margin: 0; font-size: var(--fs-100); font-weight: var(--fw-semibold); color: var(--text-muted); }
.help-article-title {
    font-size: var(--fs-500);
    line-height: var(--lh-tight);
    color: var(--text-strong);
    margin-block: var(--space-1) var(--space-2);
    scroll-margin-block-start: calc(var(--header-height) + var(--space-4));
}
.help-lede { margin: 0; font-size: var(--fs-300); line-height: var(--lh-normal); color: var(--text-muted); }
.help-subhead { font-size: var(--fs-300); font-weight: var(--fw-semibold); color: var(--text-strong); margin-block: var(--space-5) var(--space-3); }

.help-toc {
    margin-block: var(--space-5) 0;
    padding: var(--space-3) var(--space-4);
}
.help-toc-title { font-size: var(--fs-100); font-weight: var(--fw-semibold); color: var(--text-muted); margin: 0 0 var(--space-2); }
.help-toc-list { margin: 0; padding-inline-start: var(--space-5); display: grid; gap: var(--space-1); font-size: var(--fs-200); }
.help-toc-list a, .help-inline-link { color: var(--accent-text); text-underline-offset: 2px; }
.help-toc-list a { text-decoration: none; }
.help-toc-list a:hover { text-decoration: underline; }

.help-section { margin-block-start: var(--space-6); scroll-margin-block-start: calc(var(--header-height) + var(--space-4)); }
.help-section-title {
    display: flex; align-items: center; gap: var(--space-2);
    font-size: var(--fs-400);
    color: var(--text-strong);
    margin: 0 0 var(--space-4);
    scroll-margin-block-start: calc(var(--header-height) + var(--space-4));
}
.help-anchor {
    display: inline-flex; align-items: center; justify-content: center;
    inline-size: 28px; block-size: 28px; flex-shrink: 0;
    border-radius: var(--radius-xs);
    color: var(--text-muted);
    transition: opacity var(--motion-fast) var(--ease-standard);
}
.help-anchor [data-lucide] { inline-size: var(--icon-md); block-size: var(--icon-md); }
.help-anchor:hover { color: var(--accent-text); background: var(--surface-glass-hover); }
@media (hover: hover) {
    .help-anchor { opacity: 0; }
    .help-section-title:hover .help-anchor, .help-anchor:focus-visible { opacity: 1; }
}

.help-section p, .help-ul, .help-ol {
    font-size: var(--fs-300);
    line-height: var(--lh-loose);
    color: var(--text-default);
    margin: 0 0 var(--space-3);
}
.help-ul, .help-ol { padding-inline-start: var(--space-5); display: grid; gap: var(--space-2); }
.help-article strong { font-weight: var(--fw-semibold); color: var(--text-strong); }
.help-article code, .help-welcome code {
    padding: 1px 5px;
    border-radius: var(--radius-xs);
    background: var(--surface-sunken);
    border: 1px solid var(--border-subtle);
    overflow-wrap: anywhere;
}
.help-code { margin: var(--space-3) 0 var(--space-4); }
.help-code figcaption { font-size: var(--fs-100); color: var(--text-muted); margin-block-end: var(--space-1); }
/* `dir="ltr"` is on the <pre> itself: a command reads left to right in either
   language, and it scrolls inside its own box rather than widening the page. */
.help-code pre {
    margin: 0;
    padding: var(--space-3);
    max-inline-size: 100%;
    overflow-x: auto;
    font-size: var(--fs-100);
    line-height: var(--lh-normal);
    color: var(--text-strong);
}
.help-code pre code { padding: 0; border: 0; background: none; overflow-wrap: normal; }

.help-callout { margin: var(--space-3) 0 var(--space-4); }
.help-callout [data-lucide] { inline-size: 18px; block-size: 18px; margin-block-start: 3px; color: var(--info-text); }
.help-callout p { margin: 0; font-size: var(--fs-200); line-height: var(--lh-normal); }
.help-callout--tip { background: var(--success-soft); border-color: var(--border-success); }
.help-callout--tip [data-lucide] { color: var(--success-text); }
.help-callout--warn { background: var(--warning-soft); border-color: var(--border-warning); }
.help-callout--warn [data-lucide] { color: var(--warning-text); }

.help-dl { margin: 0 0 var(--space-4); display: grid; }
.help-dl-row {
    display: grid; gap: var(--space-1);
    padding-block: var(--space-3);
    border-block-end: 1px solid var(--border-subtle);
}
.help-dl-row:last-child { border-block-end: 0; }
.help-dl dt { font-size: var(--fs-200); font-weight: var(--fw-semibold); color: var(--text-strong); }
.help-dl dd { margin: 0; font-size: var(--fs-200); line-height: var(--lh-normal); color: var(--text-default); }

.help-qa { display: grid; gap: var(--space-2); margin: 0 0 var(--space-4); }
.help-qa-item > summary {
    cursor: pointer;
    padding: var(--space-3) var(--space-4);
    min-block-size: var(--tap-target);
    font-size: var(--fs-200);
    font-weight: var(--fw-medium);
    color: var(--text-strong);
    line-height: var(--lh-normal);
}
.help-qa-item[open] > summary { border-block-end: 1px solid var(--border-subtle); }
.help-qa-answer { padding: var(--space-3) var(--space-4) var(--space-1); }
.help-qa-answer p, .help-qa-answer .help-ul { font-size: var(--fs-200); line-height: var(--lh-normal); }

.help-related { margin-block-start: var(--space-6); }
.help-related-list { grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: var(--space-2); }
.help-related-link, .help-card {
    display: flex; align-items: center; gap: var(--space-2);
    padding: var(--space-3);
    min-block-size: var(--tap-target);
    border-radius: var(--radius-md);
    border: 1px solid var(--border-subtle);
    background: var(--surface-glass);
    color: var(--text-strong);
    font-size: var(--fs-200);
    text-decoration: none;
    transition: background var(--motion-fast) var(--ease-standard), border-color var(--motion-fast) var(--ease-standard);
}
.help-related-link:hover, .help-card:hover { background: var(--surface-glass-hover); border-color: var(--border-strong); }
.help-related-link [data-lucide], .help-card [data-lucide] { inline-size: 18px; block-size: 18px; color: var(--accent-text); }

.help-cards { grid-template-columns: repeat(auto-fill, minmax(210px, 1fr)); gap: var(--space-3); }
.help-card { flex-direction: column; align-items: flex-start; gap: var(--space-1); padding: var(--space-4); }
.help-card-title { font-weight: var(--fw-semibold); }
.help-card-summary { font-size: var(--fs-100); color: var(--text-muted); line-height: var(--lh-normal); }

.help-feedback-thanks { margin: 0; inline-size: 100%; font-size: var(--fs-200); color: var(--text-muted); }
.help-feedback-thanks:empty { display: none; }

.help-next {
    display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center;
    column-gap: var(--space-3);
    margin-block-start: var(--space-5);
    padding: var(--space-4);
    color: var(--text-strong);
    text-decoration: none;
}
.help-next:hover { border-color: var(--border-strong); }
.help-next-label { font-size: var(--fs-100); color: var(--text-muted); }
.help-next-title { grid-row: 2; font-weight: var(--fw-semibold); }
.help-next [data-lucide] { grid-row: 1 / span 2; grid-column: 2; inline-size: 18px; block-size: 18px; color: var(--accent-text); }

/* The contextual "?" links other screens carry (UI.helpLink). Small, quiet, and
   still a 24px target. */
.help-link {
    display: inline-flex; align-items: center; gap: var(--space-1);
    min-block-size: 24px;
    font-size: var(--fs-100);
    color: var(--accent-text);
    text-decoration: none;
    border-radius: var(--radius-xs);
}
.help-link:hover span { text-decoration: underline; text-underline-offset: 2px; }
.help-link [data-lucide] { inline-size: var(--icon-sm); block-size: var(--icon-sm); }
.help-link--icon {
    justify-content: center;
    inline-size: 28px; block-size: 28px;
    border-radius: var(--radius-pill);
    color: var(--text-muted);
}
.help-link--icon:hover { color: var(--accent-text); background: var(--surface-glass-hover); }
.help-link--icon [data-lucide] { inline-size: var(--icon-md); block-size: var(--icon-md); }
/* The composer's line under the carousel hint: snug to the hint it follows. */
.slide-help { margin-block: var(--space-1) 0; }

@media (min-width: 560px) {
    .help-article, .help-welcome { padding: var(--space-5); }
    .help-dl-row { grid-template-columns: minmax(9rem, 13rem) minmax(0, 1fr); gap: var(--space-4); }
}

@media (min-width: 1100px) {
    .help-layout { grid-template-columns: minmax(240px, 280px) minmax(0, 1fr); }
    .help-layout[data-view='article'] .help-nav { display: flex; }
    .help-layout[data-view='index'] .help-main { display: block; }
    .help-nav {
        position: sticky;
        inset-block-start: calc(var(--header-height) + var(--space-4));
        max-block-size: calc(100dvh - var(--header-height) - var(--space-7));
        overflow-y: auto;
        padding: 2px;
    }
    .help-item-summary, .help-back { display: none; }
    .help-article, .help-welcome { padding: var(--space-6); }
}

@media (prefers-reduced-motion: reduce) {
    .help-item, .help-result, .help-anchor, .help-related-link, .help-card { transition: none; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   Growth & SEO hub (#/growth).  Performance: the numbers, the coach, best times,
   by type, the posts. SEO & reach: keywords, hashtag sets, competitors.
   Built from the inventory — stat cards, chart cards, the table and its mobile
   cards, the Studio's callouts, stages and segmented tabs — with only what is
   new here: the heat map, the proportion bars and the chip lists.
   ═══════════════════════════════════════════════════════════════════════════ */
.growth-page { display: flex; flex-direction: column; gap: var(--space-5); }
.growth-page > * { min-inline-size: 0; }
.growth-page .stats-grid, .growth-page .chart-grid { margin-block-end: 0; }
#growth-summary, #growth-details { display: flex; flex-direction: column; gap: var(--space-4); }
#growth-banner { display: flex; flex-direction: column; gap: var(--space-3); }
#growth-banner:empty { display: none; }

/* ─── Toolbar ───────────────────────────────────────────────────────────── */
.growth-toolbar { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--space-3); }
.growth-tabs, .growth-range, .growth-metric { inline-size: max-content; max-inline-size: 100%; }
.growth-toolbar-end { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3); }
.growth-sync { display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap; }
.growth-sync-note { flex-basis: 100%; margin: 0; }

/* ─── The permission banner ─────────────────────────────────────────────── */
.growth-banner .studio-callout-title { font-size: var(--fs-300); }
.growth-scopes { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--space-2); }
.growth-scopes li { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--space-1) var(--space-3); }
.growth-scopes code {
    font-family: var(--font-mono); font-size: var(--fs-100);
    padding: 2px var(--space-2); border-radius: var(--radius-xs);
    background: var(--surface-glass-hover); color: var(--text-strong);
}
.growth-fix-lead { font-weight: var(--fw-semibold); color: var(--text-strong); }
.growth-fix { margin: 0; padding-inline-start: var(--space-5); display: grid; gap: var(--space-1); }
.growth-tiktok-note, .growth-status-failed { margin: 0; }

/* ─── KPI cards ─────────────────────────────────────────────────────────── */
.growth-kpis { grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); }
.growth-kpi { display: flex; flex-direction: column; }
.growth-kpi.is-missing .stat-value { color: var(--text-muted); }
.growth-delta { display: flex; flex-wrap: wrap; gap: var(--space-1); align-items: baseline; }
.growth-delta.text-success, .growth-delta.text-danger { font-weight: var(--fw-semibold); }
.growth-tip { margin-block-start: auto; padding-block-start: var(--space-2); }
.growth-tip > summary {
    display: inline-flex; align-items: center; gap: var(--space-1);
    min-block-size: 32px; cursor: pointer; list-style: none;
    font-size: var(--fs-100); color: var(--text-muted);
    border-radius: var(--radius-xs);
}
.growth-tip > summary::-webkit-details-marker { display: none; }
.growth-tip > summary:hover { color: var(--accent-text); }
.growth-tip > summary [data-lucide] { inline-size: var(--icon-sm); block-size: var(--icon-sm); }
.growth-tip > p { margin: 0; font-size: var(--fs-100); line-height: var(--lh-normal); color: var(--text-default); }

/* ─── Trend ─────────────────────────────────────────────────────────────── */
.growth-trend-head { flex-wrap: wrap; }
.growth-trend-summary { margin: 0 0 var(--space-2); }

/* ─── Sections with a heading, a line and an action ─────────────────────── */
.growth-section-head { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--space-3); margin-block-end: var(--space-4); }
.growth-section-heading { display: flex; flex-direction: column; gap: var(--space-1); min-inline-size: 0; }
.growth-section-heading .section-title { margin: 0; display: flex; align-items: center; gap: var(--space-2); }
.growth-section-heading .section-title [data-lucide] { inline-size: 18px; block-size: 18px; }
.growth-section-heading .section-title svg { color: var(--accent-text); }
.growth-section-heading .text-meta { margin: 0; max-inline-size: var(--measure); }

/* ─── Proportion bars (SVG: data as attributes, colour from tokens) ─────── */
.growth-bar { display: block; inline-size: 100%; min-inline-size: 48px; block-size: 6px; }
.growth-bar-track { fill: var(--surface-glass-hover); }
.growth-bar-fill { fill: var(--accent); }
.growth-bar.is-alt .growth-bar-fill { fill: var(--info); }

/* ─── Posts ─────────────────────────────────────────────────────────────── */
.growth-filters { align-items: flex-end; }
.growth-filter { display: flex; flex-direction: column; gap: var(--space-1); }
.growth-filter .form-label { margin: 0; font-size: var(--fs-100); }
.growth-sort-select { display: none; }
.growth-posts-count { margin: 0 0 var(--space-3); }
/* A containing block for the scrollers. `.sr-only` is position:absolute, and without a
   positioned ancestor the hidden caption and sort labels escape the `overflow-x: auto`
   box and widen the whole page — by 280px in Arabic, where the table overflows leftward. */
.growth-posts-table, .growth-heat-wrap { position: relative; }
.growth-posts-table .data-table { min-inline-size: 980px; }
.growth-post-cell { min-inline-size: 260px; max-inline-size: 360px; }
.growth-post { display: flex; align-items: center; gap: var(--space-3); min-inline-size: 0; }
.growth-thumb {
    inline-size: 48px; block-size: 48px; flex-shrink: 0;
    border-radius: var(--radius-sm); overflow: hidden;
    background: var(--surface-solid); border: 1px solid var(--border-subtle);
    display: flex; align-items: center; justify-content: center; color: var(--text-muted);
}
.growth-thumb img { inline-size: 100%; block-size: 100%; object-fit: cover; display: block; }
.growth-thumb [data-lucide] { inline-size: 18px; block-size: 18px; }
.growth-post-text { display: flex; flex-direction: column; gap: var(--space-1); min-inline-size: 0; }
.growth-post-caption {
    font-size: var(--fs-200); color: var(--text-strong);
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.growth-post-meta { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); }
.growth-post-note {
    display: flex; align-items: flex-start; gap: var(--space-1);
    font-size: var(--fs-100); color: var(--text-muted);
}
.growth-post-note [data-lucide] { flex: none; inline-size: var(--icon-sm); block-size: var(--icon-sm); margin-block-start: 2px; }
.growth-post-note svg { color: var(--accent-text); }
.growth-post-meta .platform-tag { margin: 0; }
.growth-permalink {
    display: inline-flex; align-items: center; gap: var(--space-1);
    min-block-size: 32px; font-size: var(--fs-100); color: var(--accent-text);
}
.growth-permalink:hover { text-decoration: underline; }
.growth-permalink [data-lucide] { inline-size: var(--icon-sm); block-size: var(--icon-sm); }
.th-sort {
    display: inline-flex; align-items: center; gap: var(--space-1);
    min-block-size: 32px; padding: 0; background: none; border: 0;
    font: inherit; color: inherit; cursor: pointer; white-space: nowrap;
}
.th-sort:hover, .th-sort.is-active { color: var(--text-strong); }
.th-sort [data-lucide] { inline-size: var(--icon-sm); block-size: var(--icon-sm); }
.growth-er { display: inline-flex; align-items: center; gap: var(--space-2); inline-size: 100%; justify-content: flex-end; }
.growth-er .growth-bar { inline-size: 64px; flex-shrink: 0; }
.growth-er-value { font-variant-numeric: tabular-nums; unicode-bidi: plaintext; min-inline-size: 3.5em; text-align: end; }
.growth-er.is-missing { color: var(--text-muted); }
.growth-post-cards { list-style: none; margin: 0; padding: 0; }
.growth-card-metrics { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--space-2); margin: 0; }
.growth-card-metrics div { display: flex; flex-direction: column; gap: 2px; }
.growth-card-metrics dt { font-size: var(--fs-100); color: var(--text-muted); }
.growth-card-metrics dd { margin: 0; font-weight: var(--fw-semibold); color: var(--text-strong); font-variant-numeric: tabular-nums; }
.growth-card-foot { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); flex-wrap: wrap; }
.growth-card-er { display: flex; align-items: center; gap: var(--space-2); flex: 1; justify-content: flex-end; min-inline-size: 180px; }
.growth-card-er .growth-er { inline-size: auto; }

/* ─── Best times: a 7×24 table. Columns flow from the inline start, so in Arabic
   midnight is on the right and the day runs leftward, like the text around it. ── */
.growth-best-top { margin: 0 0 var(--space-3); font-size: var(--fs-200); display: flex; flex-wrap: wrap; gap: var(--space-1) var(--space-2); }
.growth-best-slot { font-weight: var(--fw-semibold); color: var(--text-strong); }
.growth-heat-wrap { overflow-x: auto; }
.growth-heat { inline-size: 100%; border-collapse: separate; border-spacing: 2px; table-layout: fixed; min-inline-size: 280px; }
/* Hour labels come every third column, so at the 12px floor they still clear each other
   (~14px wide, three ~9px columns apart at the table's 280px minimum). `table-layout:
   fixed` takes the day column's width from this row, so its 3.2em is now the same 3.2em
   the day names below were sized for. */
.growth-heat thead th, .growth-heat thead td { font-size: var(--fs-100); font-weight: var(--fw-medium); color: var(--text-muted); text-align: center; padding: 0 0 2px; font-variant-numeric: tabular-nums; }
.growth-heat tbody th { font-size: var(--fs-100); font-weight: var(--fw-medium); color: var(--text-default); text-align: start; padding-inline-end: var(--space-2); white-space: nowrap; inline-size: 3.2em; }
.growth-heat thead td:first-child { inline-size: 3.2em; }
.heat { block-size: 18px; border-radius: 3px; background: var(--surface-glass); }
td.heat { padding: 0; }
.heat-1 { background: color-mix(in srgb, var(--accent) 22%, transparent); }
.heat-2 { background: color-mix(in srgb, var(--accent) 45%, transparent); }
.heat-3 { background: color-mix(in srgb, var(--accent) 70%, transparent); }
.heat-4 { background: var(--accent); }
.growth-heat-legend { display: flex; align-items: center; gap: var(--space-1); margin-block-start: var(--space-3); font-size: var(--fs-100); color: var(--text-muted); }
.growth-heat-legend .heat { display: inline-block; inline-size: 16px; block-size: 12px; }
.growth-zone { margin: var(--space-2) 0 0; }

/* ─── By type ───────────────────────────────────────────────────────────── */
.growth-verdict { margin: 0 0 var(--space-3); font-size: var(--fs-200); color: var(--text-strong); font-weight: var(--fw-medium); }
.growth-type-list { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--space-4); }
.growth-type { display: grid; gap: var(--space-2); }
.growth-type-head { margin: 0; display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-3); }
.growth-type-metric { display: grid; grid-template-columns: minmax(8rem, auto) minmax(0, 1fr) auto; align-items: center; gap: var(--space-3); font-size: var(--fs-100); }
.growth-type-label { color: var(--text-muted); }
.growth-type-value { font-variant-numeric: tabular-nums; color: var(--text-strong); font-weight: var(--fw-semibold); unicode-bidi: plaintext; min-inline-size: 3.5em; text-align: end; }
.growth-type-metric > .text-meta { grid-column: 2 / -1; }

/* ─── The coach ─────────────────────────────────────────────────────────── */
.growth-coach { display: flex; flex-direction: column; gap: var(--space-3); }
.growth-coach-lede { margin: 0; max-inline-size: var(--measure); color: var(--text-default); }
.growth-coach-cta { align-items: center; }
.growth-coach-summary { margin: 0; font-size: var(--fs-300); line-height: var(--lh-loose); color: var(--text-strong); max-inline-size: var(--measure); }
.growth-coach-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: var(--space-4); }
.growth-coach-col { padding: var(--space-4); border-radius: var(--radius-md); border: 1px solid var(--border-subtle); background: var(--surface-glass); }
.growth-coach-sub { display: flex; align-items: center; gap: var(--space-2); font-size: var(--fs-400); font-weight: var(--fw-semibold); margin: 0 0 var(--space-2); color: var(--text-strong); }
.growth-coach-sub [data-lucide] { inline-size: var(--icon-md); block-size: var(--icon-md); }
.growth-coach-col.is-wins .growth-coach-sub svg { color: var(--success-text); }
.growth-coach-col.is-problems .growth-coach-sub svg { color: var(--warning-text); }
.growth-coach-list { margin: 0; padding-inline-start: var(--space-5); display: grid; gap: var(--space-2); line-height: var(--lh-normal); }
.growth-actions { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--space-3); }
.growth-action { padding: var(--space-4); border-radius: var(--radius-md); border: 1px solid var(--border-subtle); background: var(--surface-glass); display: grid; gap: var(--space-2); }
.growth-action-head { display: flex; align-items: flex-start; gap: var(--space-3); }
.growth-action-num {
    inline-size: 24px; block-size: 24px; flex-shrink: 0; border-radius: var(--radius-pill);
    display: inline-flex; align-items: center; justify-content: center;
    background: var(--accent-soft); color: var(--accent-text); font-weight: var(--fw-semibold); font-size: var(--fs-100);
}
.growth-action-title { font-size: var(--fs-400); font-weight: var(--fw-semibold); margin: 0; color: var(--text-strong); line-height: var(--lh-tight); padding-block-start: 2px; }
.growth-action-chips { margin: 0; display: flex; flex-wrap: wrap; gap: var(--space-2); }
.growth-action-line { margin: 0; line-height: var(--lh-normal); }
.growth-experiments { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--space-3); grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); }
.growth-experiment { padding: var(--space-4); border-radius: var(--radius-md); border: 1px dashed var(--border-strong); }
.growth-experiment dl { margin: 0; display: grid; gap: var(--space-2); }
.growth-experiment dt { font-size: var(--fs-100); color: var(--text-muted); font-weight: var(--fw-semibold); }
.growth-experiment dd { margin: 0; line-height: var(--lh-normal); }
.growth-coach-foot { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); flex-wrap: wrap; }
.growth-coach-foot .form-hint { margin: 0; }
.growth-coach-progress { max-inline-size: 520px; }

/* ─── SEO tools ─────────────────────────────────────────────────────────── */
.growth-seo-card { display: flex; flex-direction: column; gap: var(--space-3); }
.growth-seo-card .form-hint { margin: 0; }
.growth-chips { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: var(--space-2); }
.growth-chip { font-size: var(--fs-200); padding-inline-end: var(--space-1); gap: var(--space-2); }
.growth-chip-x {
    display: inline-flex; align-items: center; justify-content: center;
    inline-size: 28px; block-size: 28px; border-radius: var(--radius-pill);
    border: 0; background: transparent; color: var(--text-muted); cursor: pointer;
}
.growth-chip-x:hover { background: var(--surface-glass-hover); color: var(--danger-text); }
.growth-chip-x [data-lucide] { inline-size: var(--icon-sm); block-size: var(--icon-sm); }
.growth-inline-form { display: flex; flex-direction: column; gap: var(--space-2); max-inline-size: 560px; }
.growth-inline-form .form-label { margin: 0; }
.growth-inline-row { display: flex; gap: var(--space-2); align-items: stretch; }
.growth-inline-row .field { flex: 1; min-inline-size: 0; }
.growth-form-error { margin: 0; }
.growth-suggest { display: flex; flex-direction: column; gap: var(--space-3); padding: var(--space-4); border-radius: var(--radius-md); border: 1px solid var(--border-subtle); background: var(--surface-glass); }
.growth-ai-label { margin: 0; display: flex; align-items: flex-start; gap: var(--space-2); font-size: var(--fs-100); color: var(--text-default); font-weight: var(--fw-medium); }
.growth-ai-label [data-lucide] { inline-size: var(--icon-sm); block-size: var(--icon-sm); flex-shrink: 0; margin-block-start: 3px; }
.growth-ai-label svg { color: var(--accent-text); }
.growth-suggest-list { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--space-2); }
.growth-idea { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); padding-block: var(--space-1); border-block-end: 1px solid var(--border-subtle); }
.growth-idea:last-child { border-block-end: 0; }
.growth-idea-text { display: flex; flex-direction: column; gap: 2px; min-inline-size: 0; }
.growth-idea-term { font-weight: var(--fw-semibold); color: var(--text-strong); }
.growth-suggest-tags { margin: 0; display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); }
.growth-suggest-tags .form-label { margin: 0; }
.growth-suggest-busy { margin: 0; display: flex; align-items: center; gap: var(--space-2); font-size: var(--fs-200); color: var(--text-default); }
.growth-sets { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--space-3); }
.growth-set { padding: var(--space-4); border-radius: var(--radius-md); border: 1px solid var(--border-subtle); background: var(--surface-glass); display: grid; gap: var(--space-2); }
.growth-set-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--space-2) var(--space-3); }
.growth-set-name { font-size: var(--fs-300); margin: 0; color: var(--text-strong); }
.growth-set-tags { margin: 0; display: flex; flex-wrap: wrap; gap: var(--space-2); }
.growth-set-actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); }
.growth-set-form { display: grid; gap: var(--space-3); max-inline-size: 560px; }
.growth-set-form .form-group { margin: 0; }
.growth-competitor-grid { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--space-3); grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); }
.growth-competitor { padding: var(--space-4); border-radius: var(--radius-md); border: 1px solid var(--border-subtle); background: var(--surface-glass); display: grid; gap: var(--space-3); align-content: start; }
.growth-competitor-head { margin: 0; font-weight: var(--fw-semibold); }
.growth-competitor-stats { margin: 0; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-2) var(--space-3); }
.growth-competitor-stats div { display: flex; flex-direction: column; gap: 2px; }
.growth-competitor-stats dt { font-size: var(--fs-100); color: var(--text-muted); }
.growth-competitor-stats dd { margin: 0; font-weight: var(--fw-semibold); color: var(--text-strong); font-variant-numeric: tabular-nums; unicode-bidi: plaintext; text-align: start; }
.growth-competitor-recent { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--space-2); font-size: var(--fs-100); }
.growth-competitor-recent li { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); }
.growth-competitor-numbers { color: var(--text-default); font-variant-numeric: tabular-nums; }

/* ─── Who sees you, and the lines that explain a number ────────────────── */
.growth-summary-pair { grid-template-columns: minmax(0, 2fr) minmax(0, 1fr); }
.growth-audience { display: flex; flex-direction: column; gap: var(--space-2); }
.growth-audience .chart-card-header { margin-block-end: var(--space-2); }
.growth-audience-lead { margin: var(--space-2) 0 0; font-size: var(--fs-300); font-weight: var(--fw-semibold); color: var(--text-strong); line-height: var(--lh-tight); }
.growth-audience .form-hint { margin: 0; }
.growth-best-basis, .growth-platform-views { margin: 0 0 var(--space-3); }
@media (max-width: 1100px) { .growth-summary-pair { grid-template-columns: 1fr; } }

/* ─── Composer: the Instagram reach fields (alt text, collabs, trial reels) ── */
.reach-fields { display: grid; gap: var(--space-3); padding: var(--space-4); border-radius: var(--radius-md); border: 1px solid var(--border-subtle); background: var(--surface-glass); }
.reach-fields > .form-group { margin: 0; }
.reach-title { margin: 0; font-size: var(--fs-200); font-weight: var(--fw-semibold); color: var(--text-strong); display: flex; align-items: center; gap: var(--space-2); }
.reach-title [data-lucide] { inline-size: var(--icon-md); block-size: var(--icon-md); }
.reach-title svg { color: var(--accent-text); }
.reach-fields .form-hint { margin: 0; }
.reach-learn { margin: 0; }
/* A fieldset for the radios' shared name, without the native box the UA draws around one. */
.reach-trial-options {
    display: grid; gap: var(--space-2); min-inline-size: 0;
    margin: 0; padding: 0; padding-inline-start: var(--space-4);
    border: 0; border-inline-start: 2px solid var(--border-subtle);
}
.reach-trial-options > legend { padding: 0; margin-block-end: var(--space-1); }
.reach-radio { display: flex; align-items: center; gap: var(--space-2); min-block-size: var(--tap-target); font-size: var(--fs-200); }
.slide-alts { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--space-2); }
.slide-alt-row { display: grid; grid-template-columns: 44px minmax(0, 1fr); gap: var(--space-2) var(--space-3); align-items: start; }
.slide-alt-thumb {
    inline-size: 44px; block-size: 44px;
    font-size: var(--fs-100); font-weight: var(--fw-semibold);
}
.slide-alt-thumb img { inline-size: 100%; block-size: 100%; object-fit: cover; display: block; }
.slide-alt-body { display: grid; gap: var(--space-1); min-inline-size: 0; }
.slide-alt-body .field-head { margin: 0; }

/* ─── Studio: the alt text under the phone preview ─────────────────────── */
.phone-alt { margin: var(--space-2) 0 0; font-size: var(--fs-100); line-height: var(--lh-normal); color: var(--text-default); }
.phone-alt strong { color: var(--text-strong); }

@media (max-width: 768px) {
    .growth-toolbar, .growth-toolbar-end { flex-direction: column; align-items: stretch; }
    .growth-tabs, .growth-range, .growth-metric { inline-size: 100%; }
    .growth-sync { justify-content: space-between; }
    .growth-sort-select { display: flex; }
    .growth-type-metric { grid-template-columns: minmax(0, 1fr) auto; }
    .growth-type-metric > .growth-type-label { grid-column: 1 / -1; }
    .growth-type-metric > .text-meta { grid-column: 1 / -1; }
    .growth-inline-row { flex-direction: column; }
    .growth-section-head { flex-wrap: wrap; }
}
@media (max-width: 560px) {
    .growth-kpis { grid-template-columns: 1fr 1fr; }
    .growth-heat { border-spacing: 1px; }
    .heat { block-size: 14px; border-radius: 2px; }
}

/* ─── wave2: posts+studio ─── */
/* Posts — the queue in groups: Upcoming, Needs attention, Done (folded). */
.queue-groups { display: flex; flex-direction: column; gap: var(--space-5); }
.queue-group { display: flex; flex-direction: column; gap: var(--space-3); min-inline-size: 0; }
/* A <details> does not lay its own children out as flex everywhere: spacing by margin. */
details.queue-group { display: block; }
details.queue-group[open] > .card-grid { margin-block-start: var(--space-3); }
.queue-group-title {
    display: flex; align-items: center; gap: var(--space-2);
    margin: 0; font-size: var(--fs-300); font-weight: var(--fw-semibold); color: var(--text-strong);
}
.queue-group-title [data-lucide] { inline-size: var(--icon-md); block-size: var(--icon-md); flex-shrink: 0; }
.queue-group-title svg { color: var(--text-muted); }
.queue-group--attention .queue-group-title svg { color: var(--warning-text); }
summary.queue-group-title {
    min-block-size: var(--tap-target); cursor: pointer; list-style: none;
    border-radius: var(--radius-sm);
}
summary.queue-group-title::-webkit-details-marker { display: none; }
summary.queue-group-title:hover { color: var(--accent-text); }
summary.queue-group-title:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* The composer's folds (reach levers, cover step, the Done group): a chevron at the far end. */
.composer-chevron {
    display: inline-flex; margin-inline-start: auto; color: var(--text-muted);
    transition: transform var(--motion-fast) var(--ease-standard);
}
.composer-chevron [data-lucide] { inline-size: var(--icon-md); block-size: var(--icon-md); }
details[open] > summary .composer-chevron { transform: rotate(180deg); }

/* Posts — "Reach on Instagram", folded unless the row already carries a value. */
details.reach-fields { display: block; }
details.reach-fields > summary.reach-title {
    min-block-size: var(--tap-target); cursor: pointer; list-style: none;
    border-radius: var(--radius-sm);
}
details.reach-fields > summary::-webkit-details-marker { display: none; }
details.reach-fields > summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
details.reach-fields[open] > summary { margin-block-end: var(--space-3); }
.reach-body { display: grid; gap: var(--space-3); }
.reach-body > .form-group { margin: 0; }
.reach-body .form-hint { margin: 0; }

/* Posts — the cover step, folded unless a cover is already chosen; its state stays on the fold. */
details.cover-step > summary.cover-step-head {
    min-block-size: var(--tap-target); margin: 0; cursor: pointer; list-style: none;
    flex-wrap: wrap; border-radius: var(--radius-sm);
}
details.cover-step > summary::-webkit-details-marker { display: none; }
details.cover-step > summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
details.cover-step[open] > summary { margin-block-end: var(--space-2); }
.cover-step-state { margin-inline-start: auto; font-size: var(--fs-100); font-weight: var(--fw-semibold); }
.cover-step-state + .composer-chevron { margin-inline-start: 0; }

/* Posts — the composer's actions stay on screen while its ~20 fields scroll: Studio's save bar. */
.post-composer .modal-actions {
    position: sticky; inset-block-end: 0; z-index: 6;
    align-items: center;
    padding: var(--space-3) var(--space-4);
    border-radius: var(--radius-md);
    background: var(--surface-overlay);
    backdrop-filter: blur(var(--blur-glass)) saturate(180%); -webkit-backdrop-filter: blur(var(--blur-glass)) saturate(180%);
    box-shadow: var(--elev-2);
}
.modal-actions-note {
    display: inline-flex; align-items: center; gap: var(--space-2);
    margin-inline-end: auto; min-inline-size: 0;
    font-size: var(--fs-100); color: var(--text-default);
}
@media (max-width: 768px) {
    .modal-actions-note { flex-basis: 100%; }
}

/* Studio — the drafts filter, and Delete on a card. */
.studio-draft-filter { inline-size: max-content; max-inline-size: 100%; flex-wrap: wrap; margin-block-end: var(--space-3); }
.draft-card-actions { display: flex; justify-content: flex-end; margin-block-start: var(--space-1); }
@media (max-width: 768px) {
    .studio-draft-filter { inline-size: 100%; }
}

/* Studio — "Write it again" stays in the editor: progress, then "Open the new draft", above the head. */
.studio-regen-host:empty { display: none; }
.studio-regen-host:not(:empty) { margin-block-end: var(--space-4); }
.studio-regen-host .studio-progress-card { margin: 0; }

/* Studio — a scheduled draft's preview is a viewer, not a way into a form. */
.phone-stage.is-readonly { cursor: default; }

/* Studio — at the three-lesson limit a chip stays reachable (aria-disabled) and says why when pressed. */
.lesson-chip[aria-disabled='true']:not(.is-picked) { opacity: 0.55; cursor: not-allowed; }
.lesson-chip[aria-disabled='true']:hover { border-color: var(--border-subtle); background: var(--surface-glass); }

/* ─── wave2: reading screens ─── */
/* A stat tile's colour is its message (failed today, the skip-rate band). `.stat-value`
   sets `--text-strong` later in this file than `.text-*` at the top, so at equal
   specificity the tile always won and the band never showed. Two classes outrank one. */
.stat-value.text-danger { color: var(--danger-text); }
.stat-value.text-warning { color: var(--warning-text); }
.stat-value.text-success { color: var(--success-text); }
/* Same cascade trap for a muted line whose colour is the message: "last synced 2 days ago"
   (G5), "synced since this plan was written" (G12), a follower change in a sub-line. */
.text-meta.text-warning, .stat-sub.text-warning { color: var(--warning-text); }
.text-meta.text-danger, .stat-sub.text-danger { color: var(--danger-text); }
.stat-sub.text-success { color: var(--success-text); }

/* SH3: the waiting count on the Inbox nav item. Data, not decoration: hidden at zero. */
.nav-count {
    margin-inline-start: auto;
    min-inline-size: 22px; block-size: 22px; padding-inline: var(--space-2);
    display: inline-flex; align-items: center; justify-content: center;
    border-radius: var(--radius-pill);
    background: var(--danger-soft); color: var(--danger-text);
    font-size: var(--fs-100); font-weight: var(--fw-semibold);
    font-variant-numeric: tabular-nums;
}
.nav-count[hidden] { display: none; }

/* O7: "as of 14:05 · refresh" in the briefing head. */
.briefing-head .briefing-asof { font-size: var(--fs-100); color: var(--text-muted); font-variant-numeric: tabular-nums; }
.briefing-head .btn { margin-inline-start: auto; }
/* O8: what is broken spans the row; what is merely worth knowing shares one. */
.briefing-list .alert-danger { grid-column: 1 / -1; }

/* O1 + G1: the content region between the briefing and the tiles. */
.growth-strip { margin-block-end: var(--space-5); }
.growth-strip .stats-grid { margin-block-end: var(--space-3); }
.stat-trend { display: inline-flex; align-items: center; gap: 2px; font-weight: var(--fw-semibold); }
.stat-trend [data-lucide] { inline-size: var(--icon-sm); block-size: var(--icon-sm); }
.stat-trend.is-up { color: var(--success-text); }
.stat-trend.is-down { color: var(--danger-text); }
.do-first {
    display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2) var(--space-3);
    padding: var(--space-3) var(--space-4);
    border-radius: var(--radius-md);
    border: 1px solid var(--border-subtle);
    border-inline-start: 3px solid var(--accent);
    background: var(--surface-glass); /* a tint, like .alert-card: no blur behind it */
    font-size: var(--fs-200);
}
.do-first > strong { color: var(--text-strong); flex-shrink: 0; }
.do-first > .do-first-text { color: var(--text-default); min-inline-size: 0; flex: 1 1 24ch; }
.do-first [data-lucide] { inline-size: var(--icon-md); block-size: var(--icon-md); flex-shrink: 0; }
.do-first svg { color: var(--accent-text); }
.growth-unavailable { font-size: var(--fs-100); color: var(--text-muted); margin: 0; }
.stat-link { padding: 0; border: 0; background: none; font: inherit; color: var(--accent-text); cursor: pointer; text-decoration: underline; text-underline-offset: 2px; }
.stat-link:hover { color: var(--text-strong); }
.chart-grid--one { grid-template-columns: 1fr; }

/* O11: a FAILED row's reason, under the pill. */
.cell-note { display: block; margin-block-start: 2px; font-size: var(--fs-100); color: var(--text-muted); max-inline-size: 36ch; overflow-wrap: anywhere; }

/* I1 + I7: the filter and the count under the search box. */
.inbox-search .segmented { margin-block-start: var(--space-2); inline-size: 100%; }
.inbox-search .segmented .btn { flex: 1; }
.inbox-count { margin: var(--space-2) 0 0; font-size: var(--fs-100); color: var(--text-muted); }
.inbox-count strong { color: var(--text-strong); }
/* I9: the empty state, with a next step. */
.empty-threads p { margin: 0; }
.empty-threads .empty-hint { margin-block-start: var(--space-2); font-size: var(--fs-100); }
.empty-threads .help-link { margin-block-start: var(--space-2); }
/* I10: the stale strip's Retry. */
#threads-stale { padding: var(--space-2) var(--space-2) 0; }
#threads-stale .inline-error { margin-block-end: 0; flex-wrap: wrap; }
#threads-stale .inline-error > .btn { margin-inline-start: auto; }
/* I4: a day divider in the message log. */
.chat-day { display: flex; align-items: center; gap: var(--space-3); margin-block: var(--space-2); font-size: var(--fs-100); color: var(--text-muted); }
.chat-day::before, .chat-day::after { content: ''; flex: 1; border-block-start: 1px solid var(--border-subtle); }
/* I5: the switch's visible state, and the hint that outlives the placeholder. */
.ai-state { font-size: var(--fs-100); color: var(--text-muted); white-space: nowrap; }
.ai-state.is-on { color: var(--success-text); }
.composer-hint { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2) var(--space-3); margin: var(--space-2) 0 0; font-size: var(--fs-100); color: var(--text-muted); }
.composer-hint > span { flex: 1 1 24ch; min-inline-size: 0; }

/* G6: a range switch dims the numbers it is replacing instead of blanking them. */
/* Only the page's own regions: a busy BUTTON inside them (Sync) says so with its spinner. */
.growth-page > [aria-busy='true'] { opacity: 0.55; pointer-events: none; transition: opacity var(--motion-fast) var(--ease-standard); }
@media (prefers-reduced-motion: reduce) { .growth-page > [aria-busy='true'] { transition: none; } }
/* G1: no plan, no line — and no empty gap in the page's column either. */
#growth-do-first:empty { display: none; }
/* G4: the distance to goal under followers and views. */
.growth-goal { display: grid; gap: var(--space-1); margin-block-start: var(--space-2); }
.growth-goal .growth-bar { inline-size: 100%; block-size: 6px; display: block; }
.growth-goal-line { display: flex; flex-wrap: wrap; gap: var(--space-1) var(--space-2); align-items: baseline; font-size: var(--fs-100); color: var(--text-muted); }
.growth-goal-line .stat-link { font-size: var(--fs-100); }
.growth-goals-form { display: grid; gap: var(--space-3); grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); align-items: end; }
.growth-goals-form .form-group { margin: 0; }
.growth-goals-form .field { text-align: start; font-variant-numeric: tabular-nums; }
/* G12: beside Regenerate. */
.growth-coach-foot .text-meta { margin-inline-end: auto; }

/* A7: the summary above the 30-day strip. */
.chart-summary { margin: 0 0 var(--space-2); font-size: var(--fs-100); color: var(--text-muted); }
.chart-summary strong { color: var(--text-strong); }
/* A4: a keyword chip that opens the activity log — still a chip, now a button. */
.chip-link { cursor: pointer; font-family: inherit; line-height: inherit; }
.chip-link:hover { text-decoration: underline; text-underline-offset: 2px; }

/* ─── wave2: settings+site ─── */
/* Settings → webhook: the URL Meta calls, with its copy button, on the row
   under the verify-token pill (SE4). Same shape as the TikTok register rows:
   label on its own line, the long value beside its button. */
.settings-copy-row {
    display: grid; grid-template-columns: minmax(0, 1fr) auto;
    align-items: center; column-gap: var(--space-2); row-gap: var(--space-1);
}
.settings-copy-row .form-label { grid-column: 1 / -1; margin: 0; }
.settings-copy-row code {
    padding: var(--space-1) var(--space-2);
    border-radius: var(--radius-xs);
    background: var(--surface-sunken);
    overflow-wrap: anywhere;
    font-size: var(--fs-100);
}

/* Activity (V1): the search box and its button travel together, so a phone
   wraps the pair as one row rather than stranding the button. */
.activity-search { display: flex; align-items: center; gap: var(--space-2); flex-wrap: nowrap; min-inline-size: 0; }
.activity-search .field[type='search'] { flex: 1 1 auto; }
@media (max-width: 768px) {
    .activity-search { inline-size: 100%; }
}

/* Campaigns (C8): the failed count is a way into the log. It keeps the stat's
   look, gains the link colour and an underline on hover — never red fill. */
.campaign-stat-link {
    border: 0; background: transparent; padding: 0; margin: 0;
    font: inherit; font-size: var(--fs-100); color: var(--accent-text);
    cursor: pointer; border-radius: var(--radius-xs);
    min-block-size: 24px;
}
.campaign-stat-link:hover { text-decoration: underline; text-underline-offset: 3px; }
.campaign-stat-link:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; box-shadow: 0 0 0 var(--ring) var(--accent-ring); }

/* AI (AI6): «تغييرات غير محفوظة» beside Save, while there are any. */
.ai-save-row { align-items: center; }
.unsaved-note {
    display: inline-flex; align-items: center; gap: var(--space-1);
    font-size: var(--fs-100); color: var(--warning-text);
}
.unsaved-note::before {
    content: ''; inline-size: 7px; block-size: 7px; border-radius: 50%;
    background: var(--warning); flex-shrink: 0;
}

/* ═══════════════════════════════════════════════════════════════════════════
   Monteur (#/monteur). The status strip, the setup steps, the save bar and the
   list editor are the Studio's own classes; what is new here is the review card
   (a 9:16 player beside its copy), the video list and the lessons. Tokens and
   logical properties only.
   ═══════════════════════════════════════════════════════════════════════════ */
.monteur-section { margin-block-end: var(--space-4); }
.monteur-section > .section-title,
.monteur-section .studio-section-head .section-title { margin: 0; }
.monteur-section > .section-title { margin-block-end: var(--space-2); }
.monteur-queue .studio-section-head { margin-block-end: var(--space-3); }
.monteur-group { margin-block-start: var(--space-2); }
.monteur-group .studio-section-head { margin-block-end: var(--space-2); }
.monteur-empty { padding-block: var(--space-6); }

.monteur-clips { list-style: none; margin: 0 0 var(--space-4); padding: 0; display: flex; flex-direction: column; gap: var(--space-4); }
.monteur-clip {
    display: grid; grid-template-columns: minmax(160px, 240px) minmax(0, 1fr);
    gap: var(--space-4); padding: var(--space-4); align-items: start;
}
.monteur-clip--compact { grid-template-columns: 72px minmax(0, 1fr); gap: var(--space-3); padding: var(--space-3); }
.monteur-media {
    display: flex; align-items: center; justify-content: center;
    aspect-ratio: 9 / 16; overflow: hidden;
    background: var(--surface-solid); border: 1px solid var(--border-subtle);
    color: var(--text-muted);
}
.monteur-media video, .monteur-media img { inline-size: 100%; block-size: 100%; object-fit: cover; }
.monteur-media [data-lucide] { inline-size: var(--icon-xl); block-size: var(--icon-xl); }
.monteur-clip-body { display: flex; flex-direction: column; gap: var(--space-3); min-inline-size: 0; }
.monteur-clip-body .form-group { margin-block-end: var(--space-3); }
.monteur-clip-body .post-card-meta { margin: 0; }
.monteur-label { margin: 0 0 var(--space-1); font-size: var(--fs-100); font-weight: var(--fw-semibold); color: var(--text-muted); }
.monteur-hook {
    margin: 0; padding-inline-start: var(--space-3);
    border-inline-start: 3px solid var(--accent);
    font-size: var(--fs-300); color: var(--text-strong); line-height: var(--lh-normal);
}
.monteur-why { margin: 0; font-size: var(--fs-200); color: var(--text-default); line-height: var(--lh-normal); overflow-wrap: anywhere; }
.monteur-score { font-variant-numeric: tabular-nums; flex-shrink: 0; }
.monteur-scores { margin: 0; font-variant-numeric: tabular-nums; }
.monteur-topic { margin: 0; overflow-wrap: anywhere; }
.monteur-label .chip { margin-inline-start: var(--space-1); vertical-align: middle; }
.monteur-tt-line { margin: 0; }
.monteur-tt-link {
    display: inline-flex; align-items: center; gap: var(--space-1);
    min-block-size: var(--tap-target);
    font-size: var(--fs-100); color: var(--accent-text);
}
.monteur-tt-link [data-lucide] { inline-size: var(--icon-sm); block-size: var(--icon-sm); flex-shrink: 0; }
.monteur-tt-link:hover span { text-decoration: underline; text-underline-offset: 2px; }
/* `.text-meta` comes later than `.text-warning`, so a warning in a meta line needs both. */
.text-meta.text-warning { color: var(--warning-text); }
.monteur-rerender { display: flex; align-items: flex-start; gap: var(--space-2); margin: 0 0 var(--space-3); }
.monteur-rerender [data-lucide] { inline-size: var(--icon-md); block-size: var(--icon-md); flex-shrink: 0; margin-block-start: 2px; }
.monteur-clip-savebar { align-items: center; }
.monteur-clip-savebar .text-meta { margin: 0; }
.monteur-extra summary {
    display: flex; align-items: center; min-block-size: var(--tap-target);
    cursor: pointer; color: var(--accent-text); font-weight: var(--fw-semibold); font-size: var(--fs-200);
}
.monteur-extra summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: var(--radius-xs); }
.monteur-extra .monteur-label { margin-block-start: var(--space-2); }
.monteur-approve {
    display: flex; flex-direction: column; gap: var(--space-2);
    padding-block-start: var(--space-3); border-block-start: 1px solid var(--border-subtle);
}
.monteur-approve .form-group { margin: 0; }
.monteur-approve .form-hint { margin: 0; }

.monteur-folder-row { flex-wrap: wrap; }
.monteur-folder-row .field { min-inline-size: min(100%, 16rem); }
.monteur-folder-line { margin: var(--space-2) 0 0; }
.monteur-form .setup-step-body .form-group { margin-block-end: var(--space-2); }
.monteur-platforms { display: flex; flex-wrap: wrap; gap: var(--space-2) var(--space-5); }
.monteur-platforms .check-row { align-items: center; }

/* A state that is neither good nor bad news: "no strong moment", "rejected". */
.status-pill.neutral { background: var(--surface-glass-hover); color: var(--text-default); }

.monteur-sources { list-style: none; margin: var(--space-3) 0 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-2); }
.monteur-source {
    display: flex; align-items: center; flex-wrap: wrap; gap: var(--space-2) var(--space-3);
    padding: var(--space-3);
    border: 1px solid var(--border-subtle); border-radius: var(--radius-sm);
}
.monteur-source-main { flex: 1 1 14rem; min-inline-size: 0; display: flex; flex-direction: column; gap: var(--space-1); }
.monteur-source-main p { margin: 0; }
.monteur-source-name { font-weight: var(--fw-semibold); color: var(--text-strong); overflow-wrap: anywhere; }

.monteur-lessons-summary { margin: var(--space-3) 0 0; font-size: var(--fs-200); color: var(--text-default); }
.monteur-lessons { margin: var(--space-3) 0; padding-inline-start: var(--space-5); display: grid; gap: var(--space-3); }
.monteur-lessons li p { margin: 0; }
.monteur-rule { color: var(--text-strong); font-weight: var(--fw-semibold); line-height: var(--lh-normal); overflow-wrap: anywhere; }

/* ─── Phones: the player stacks above its copy, and Approve is full width ─── */
@media (max-width: 560px) {
    .monteur-clip { grid-template-columns: minmax(0, 1fr); padding: var(--space-3); }
    .monteur-clip > .monteur-media { inline-size: min(100%, 260px); justify-self: center; }
    .monteur-clip--compact { grid-template-columns: 56px minmax(0, 1fr); }
    .monteur-clip--compact > .monteur-media { inline-size: auto; justify-self: stretch; }
    .monteur-approve .btn-primary, .monteur-approve .btn-secondary[type='submit'] { inline-size: 100%; }
    .monteur-source .btn { margin-inline-start: auto; }
}

/* ─── wave3: inbox+analytics data ─── */
/* I8: the thread's name keeps its ellipsis and the IG / FB tag stays whole beside it. */
.thread-who { display: inline-flex; align-items: baseline; min-width: 0; }
.thread-who .platform-tag { flex-shrink: 0; }
/* In the chat header the tag opens the sub-line, so its gap is on the far side. */
.chat-sub .platform-tag { margin-inline: 0 var(--space-2); }

/* I4: who wrote an outbound message, above the bubble. The row turns into a column so the
   bubble keeps its `max-width` against the full row; flex-start / flex-end stay
   writing-mode aware, so the tag sits at the bubble's own edge in both directions. On the
   chat canvas, not on the gradient: a tint on the outbound gradient falls below AA. */
.message-row--tagged { flex-direction: column; gap: var(--space-1); }
.message-row--tagged.row-outbound { align-items: flex-end; }
.message-row--tagged.row-inbound { align-items: flex-start; }
.sender-tag {
    display: inline-flex; align-items: center;
    font-size: var(--fs-100);
    font-weight: var(--fw-semibold);
    padding: 1px var(--space-2);
    border-radius: var(--radius-xs);
    line-height: var(--lh-normal);
}
/* Tints with their -text labels (DESIGN §3), the pairs `.chip-accent` and `.chip` use. */
.sender-tag--ai { background: var(--accent-soft); color: var(--accent-text); }
.sender-tag--operator { background: var(--surface-glass-hover); color: var(--text-default); }

/* A1: the window control above the tiles, and the window each panel names. */
.analytics-toolbar { display: flex; justify-content: flex-end; margin-block-end: var(--space-4); }
.analytics-range { inline-size: max-content; max-inline-size: 100%; }
.analytics-window { flex-shrink: 0; }

/* A5: why DMs fail, most frequent first. */
.analytics-failures { margin-block-end: var(--space-5); }
.failure-reasons { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-2); }
.failure-reason {
    display: flex; align-items: center; gap: var(--space-3);
    padding: var(--space-3);
    border: 1px solid var(--border-subtle); border-radius: var(--radius-sm);
}
.failure-reason-main { flex: 1; min-inline-size: 0; display: flex; flex-direction: column; gap: var(--space-1); }
.failure-reason-main p { margin: 0; }
.failure-reason-main .text-meta { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); }
.failure-reason-text { color: var(--text-strong); overflow-wrap: anywhere; }
.failure-reason-count { font-size: var(--fs-300); color: var(--text-strong); font-variant-numeric: tabular-nums; flex-shrink: 0; }

@media (max-width: 560px) {
    .analytics-range { inline-size: 100%; }
}

/* ─── wave3: growth ─── */
/* G3: a KPI's direction against the window before. The colour is the band's, the words carry it. */
.growth-dir { font-variant-numeric: tabular-nums; }

/* G8: the post keeps its place while the metrics scroll past it. Opaque, or the numbers show
   through it: `--surface-solid` is what the card's glass composites to, and the hover tint is
   laid over it rather than replacing it. */
.growth-posts-table .growth-post-cell,
.growth-posts-table .growth-post-head {
    position: sticky;
    inset-inline-start: 0;
    z-index: 1;
    background: var(--surface-solid);
    border-inline-end: 1px solid var(--border-subtle);
}
.growth-posts-table .data-table tbody tr:hover .growth-post-cell {
    background: linear-gradient(var(--surface-glass-hover), var(--surface-glass-hover)), var(--surface-solid);
}

/* G8: a phone card leads with four numbers; «More» opens the rest. */
.growth-card-key { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.growth-card-key .growth-er { justify-content: flex-start; inline-size: auto; }
.growth-card-key .growth-er .growth-bar { inline-size: 40px; }
.growth-card-key .growth-er-value { text-align: start; min-inline-size: 0; }
.growth-card-more > summary {
    display: flex; align-items: center; gap: var(--space-1);
    min-block-size: 44px; cursor: pointer;
    font-size: var(--fs-200); font-weight: var(--fw-semibold); color: var(--accent-text);
}
.growth-card-more > summary:hover { text-decoration: underline; }
.growth-card-more[open] > summary { margin-block-end: var(--space-2); }

/* G9/G10: best times in four parts of the day. The cell is a button; its colour is a bar above
   the numbers, so the numbers keep the card's own ground and its contrast. */
.growth-dayparts { min-inline-size: 0; border-spacing: var(--space-1); }
.growth-dayparts thead th { padding-block-end: var(--space-1); }
.growth-part-name, .growth-part-hours { display: block; }
.growth-part-name { color: var(--text-default); font-weight: var(--fw-semibold); }
.growth-part-hours { font-weight: var(--fw-regular); }
.growth-slot { padding: 0; text-align: center; vertical-align: middle; }
.growth-slot.is-empty { color: var(--text-muted); block-size: 44px; font-size: var(--fs-200); }
.growth-slot-btn {
    inline-size: 100%; min-block-size: 44px;
    display: flex; flex-direction: column; align-items: stretch; justify-content: center; gap: 2px;
    padding: var(--space-1);
    border: 1px solid var(--border-subtle); border-radius: var(--radius-sm);
    background: transparent; color: var(--text-strong);
    font: inherit; font-variant-numeric: tabular-nums; cursor: pointer;
    transition: background var(--motion-fast) var(--ease-standard);
}
.growth-slot-btn:hover { background: var(--surface-glass-hover); }
.growth-slot-btn[aria-pressed='true'] { background: var(--accent-soft); border-color: var(--accent); }
.growth-slot-btn .heat { display: block; inline-size: 100%; block-size: 6px; border-radius: 3px; }
.growth-slot-avg { font-size: var(--fs-200); font-weight: var(--fw-semibold); line-height: var(--lh-tight); }
.growth-slot-n { font-size: var(--fs-100); color: var(--text-muted); line-height: var(--lh-tight); }
.growth-slot-btn[aria-pressed='true'] .growth-slot-n { color: var(--text-default); }
.growth-slot-caption { margin: var(--space-2) 0 0; min-block-size: 1.5em; }
.growth-best-few { margin: 0 0 var(--space-3); }
@media (prefers-reduced-motion: reduce) { .growth-slot-btn { transition: none; } }

/* G11: the operator's own rate, beside the competitors', measured the same way. */
.growth-own-rate { margin: var(--space-2) 0 0; display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--space-1) var(--space-2); }
.growth-own-rate strong { font-size: var(--fs-300); color: var(--text-strong); font-variant-numeric: tabular-nums; }

/* G7: the SEO view's cards, as one region that fills when the settings land. */
.growth-seo-body { display: flex; flex-direction: column; gap: var(--space-5); min-inline-size: 0; }
