/* ============================================================================
   YouTube Shorts admin panel — the entire design system, hand-written.

   No CDN links, no web fonts, no CSS framework: the container may sit behind a
   proxy with no outbound access, so everything the panel needs is in this file.

   Read it in this order:
     1. Tokens          — the palette and scales everything else is derived from
     2. Reset           — the small amount of normalising we need
     3. Shell           — .app-shell / .sidebar / .content / .page-header
     4. Primitives      — cards, pills, tables, forms, buttons, tabs, banners
     5. Domain pieces   — .stage-strip, .timeline, .scene-grid, .log-view
     6. Utilities
     7. Responsive      — the sidebar folds into a top bar under 800px

   Naming rule: block + `-` + element (`.card-title`), and modifiers read as
   `block-modifier` (`.btn-primary`, `.pill-danger`). Modifiers are always used
   alongside the base class, never on their own.
   ============================================================================ */

/* ---------------------------------------------------------------- 1. Tokens */

:root {
    /* Makes the browser's own chrome — scrollbars, <select> popups, date pickers — render dark.
       Without it a native dropdown opens as a white sheet in the middle of the panel. */
    color-scheme: dark;

    /* Surfaces. Depth comes from three flat steps plus hairlines — no shadows. */
    --bg: #0f1115;
    --bg-deep: #0b0d11; /* sidebar, inputs, log viewer: recessed below the page */
    --surface: #171a21; /* cards, table headers, tabs */
    --surface-raised: #1d212a; /* hover / active surface */
    --border: #242832;
    --border-strong: #303643; /* focus rings, pending stage segments */

    /* Text */
    --text: #e6e8ee;
    --text-muted: #9aa3b2;
    --text-faint: #6b7280;

    /* Accent + status. The status ramp is the only place colour carries meaning. */
    --accent: #7c9cff;
    --accent-quiet: rgba(124, 156, 255, 0.13);
    --accent-line: rgba(124, 156, 255, 0.32);
    --running: #7c9cff;
    --success: #4ade80;
    --warning: #fbbf24;
    --danger: #f87171;
    --status-idle: #6b7280; /* terminal-but-not-a-failure: skipped, rejected */

    /* Tinted washes for pills and banners — the hue at low alpha, never a solid fill. */
    --wash-running: rgba(124, 156, 255, 0.14);
    --wash-success: rgba(74, 222, 128, 0.13);
    --wash-warning: rgba(251, 191, 36, 0.13);
    --wash-danger: rgba(248, 113, 113, 0.13);
    --wash-idle: rgba(107, 114, 128, 0.16);

    /* Type. System stack only. The mono face carries ids, crons, paths and logs —
       anything you might copy, retype or compare character by character. */
    --font-ui: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue",
        Arial, "Noto Sans", sans-serif;
    --font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas,
        "Liberation Mono", monospace;

    --fs-xs: 11px;
    --fs-sm: 12.5px;
    --fs-base: 14px;
    --fs-md: 15px;
    --fs-lg: 19px;
    --fs-xl: 25px;

    --radius-sm: 4px;
    --radius: 6px;
    --radius-lg: 10px;
    --radius-pill: 999px;

    --sidebar-width: 220px;

    /* Was 1200px, which left a 400px gutter of empty page on an ordinary 1080p
       screen. Raised for the one page that genuinely wants the room — a 35-tile
       scene grid — while still stopping short of full-bleed, because the prose
       cards on this page (a whole narration script, a style bible) become
       unreadable past roughly 100 characters a line. */
    --content-max: 1560px;
}

/* ----------------------------------------------------------------- 2. Reset */

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

html,
body {
    margin: 0;
    padding: 0;
}

html {
    /* Prevents the layout shifting sideways when a long table forces a scrollbar. */
    scrollbar-gutter: stable;
}

body {
    background: var(--bg);
    color: var(--text);
    font-family: var(--font-ui);
    font-size: var(--fs-base);
    line-height: 1.55;
    -webkit-font-smoothing: antialiased;
    text-rendering: optimizeLegibility;
    overflow-x: hidden; /* nothing in the panel may scroll the body sideways */
}

h1,
h2,
h3,
h4 {
    margin: 0;
    font-weight: 600;
    line-height: 1.25;
}

p {
    margin: 0 0 0.7em;
}

p:last-child {
    margin-bottom: 0;
}

ul,
ol {
    margin: 0;
    padding-left: 1.2em;
}

a {
    color: var(--accent);
    text-decoration: none;
}

a:hover {
    text-decoration: underline;
}

code,
kbd,
pre,
samp {
    font-family: var(--font-mono);
    font-size: 0.92em;
}

hr {
    border: 0;
    border-top: 1px solid var(--border);
    margin: 20px 0;
}

::selection {
    background: var(--accent-line);
    color: #fff;
}

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

input[type="checkbox"],
input[type="radio"] {
    accent-color: var(--accent);
    width: 15px;
    height: 15px;
    margin: 0;
    cursor: pointer;
}

/* Screen-reader-only text, for icon-only controls. */
.sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

/* ----------------------------------------------------------------- 3. Shell */

.app-shell {
    display: grid;
    grid-template-columns: var(--sidebar-width) minmax(0, 1fr);
    min-height: 100vh;
}

/* Sticky rather than fixed: the grid already reserves the column, so the sidebar
   stays put without any offset arithmetic on the content. */
.sidebar {
    position: sticky;
    top: 0;
    align-self: start;
    height: 100vh;
    display: flex;
    flex-direction: column;
    gap: 18px;
    padding: 20px 14px 16px;
    background: var(--bg-deep);
    border-right: 1px solid var(--border);
    overflow-y: auto;
}

.brand {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 0 6px;
    color: var(--text);
}

.brand:hover {
    text-decoration: none;
}

/* Three segments — the same motif as .stage-strip, because that is what this app is. */
.brand-mark {
    display: flex;
    gap: 2px;
    flex: 0 0 auto;
}

.brand-mark span {
    display: block;
    width: 4px;
    height: 18px;
    border-radius: 1px;
    background: var(--accent);
}

.brand-mark span:nth-child(2) {
    opacity: 0.65;
}

.brand-mark span:nth-child(3) {
    opacity: 0.35;
}

.brand-text {
    display: flex;
    flex-direction: column;
    min-width: 0;
}

.brand-name {
    font-size: var(--fs-md);
    font-weight: 600;
    letter-spacing: -0.01em;
    white-space: nowrap;
}

.brand-sub {
    font-family: var(--font-mono);
    font-size: var(--fs-xs);
    letter-spacing: 0.06em;
    color: var(--text-faint);
    white-space: nowrap;
}

.nav-list {
    display: flex;
    flex-direction: column;
    gap: 2px;
    margin: 0;
    padding: 0;
    list-style: none;
}

.nav-link {
    display: block;
    padding: 7px 10px 7px 12px;
    border-radius: var(--radius);
    border-left: 2px solid transparent;
    color: var(--text-muted);
    font-size: var(--fs-base);
    white-space: nowrap;
}

.nav-link:hover {
    background: var(--surface);
    color: var(--text);
    text-decoration: none;
}

/* NavLink adds `active` itself; we never set it by hand. */
.nav-link.active {
    background: var(--accent-quiet);
    border-left-color: var(--accent);
    color: var(--text);
    font-weight: 500;
}

.sidebar-footer {
    margin-top: auto;
    display: flex;
    flex-direction: column;
    gap: 10px;
    padding-top: 14px;
    border-top: 1px solid var(--border);
}

.sidebar-note {
    font-family: var(--font-mono);
    font-size: var(--fs-xs);
    letter-spacing: 0.04em;
    color: var(--text-faint);
    padding: 0 8px;
}

.content {
    min-width: 0;
    width: 100%;
    max-width: var(--content-max);
    padding: 28px 32px 64px;
}

.page-header {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: 20px;
    flex-wrap: wrap;
    padding-bottom: 14px;
    margin-bottom: 22px;
    border-bottom: 1px solid var(--border);
}

.page-header-main {
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 4px;
}

.page-back {
    font-family: var(--font-mono);
    font-size: var(--fs-xs);
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--text-muted);
}

.page-title {
    font-size: var(--fs-xl);
    letter-spacing: -0.02em;
}

.page-subtitle {
    color: var(--text-muted);
    font-size: var(--fs-sm);
}

.page-actions {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
}

.page-loading {
    padding: 40px 0;
    color: var(--text-muted);
    font-family: var(--font-mono);
    font-size: var(--fs-sm);
}

.section {
    margin-top: 30px;
}

.section-title {
    font-size: var(--fs-md);
    margin-bottom: 12px;
    display: flex;
    align-items: center;
    gap: 10px;
}

/* ------------------------------------------------------------ 4. Primitives */

/* --- cards --- */

.card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    padding: 16px 18px;
    min-width: 0;
}

.card-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
    gap: 14px;
}

.card-header {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 12px;
    margin-bottom: 12px;
}

.card-title {
    font-size: var(--fs-md);
    font-weight: 600;
    letter-spacing: -0.01em;
    display: flex;
    align-items: center;
    gap: 8px;
    min-width: 0;
}

.card-title a {
    color: inherit;
}

/* The one-line "cron · next run · 1/1 today" strip under a card title. */
.card-meta {
    display: flex;
    flex-wrap: wrap;
    gap: 4px 12px;
    font-size: var(--fs-sm);
    color: var(--text-muted);
}

.card-body {
    display: flex;
    flex-direction: column;
    gap: 12px;
    min-width: 0;
}

.card-footer {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
    margin-top: 14px;
    padding-top: 12px;
    border-top: 1px solid var(--border);
}

/* Whole-card link. Keeps the hover affordance on the card, not just the title. */
.card-link {
    display: block;
    color: inherit;
}

.card-link:hover {
    text-decoration: none;
}

.card-link:hover .card {
    border-color: var(--border-strong);
    background: var(--surface-raised);
}

/* --- key/value rows: the workhorse of every detail page --- */

.kv {
    display: grid;
    grid-template-columns: minmax(120px, 190px) minmax(0, 1fr);
    gap: 8px 16px;
    margin: 0;
    font-size: var(--fs-sm);
}

.kv-key {
    font-family: var(--font-mono);
    font-size: var(--fs-xs);
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--text-faint);
    padding-top: 2px;
}

/* margin:0 because these are normally <dd> elements, which browsers indent by default. */
.kv-value {
    min-width: 0;
    margin: 0;
    color: var(--text);
    overflow-wrap: anywhere;
}

/* --- stat blocks --- */

.stat-row {
    display: flex;
    flex-wrap: wrap;
    gap: 12px 28px;
}

.stat {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
}

.stat-value {
    font-family: var(--font-mono);
    font-size: var(--fs-lg);
    font-weight: 600;
    letter-spacing: -0.01em;
}

.stat-label {
    font-family: var(--font-mono);
    font-size: var(--fs-xs);
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--text-faint);
}

/* --- pills --- */

.pill {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 2px 9px;
    border-radius: var(--radius-pill);
    border: 1px solid var(--border-strong);
    background: var(--wash-idle);
    color: var(--text-muted);
    font-size: var(--fs-xs);
    font-weight: 500;
    letter-spacing: 0.02em;
    white-space: nowrap;
    line-height: 1.7;
}

.pill-sm {
    padding: 1px 7px;
    font-size: 10px;
}

/* One modifier per status family. Anything in flight is accent blue, anything
   waiting on a human is amber, done is green, broken is red, and a terminal
   non-failure (skipped, rejected) is grey. */
.pill-running {
    background: var(--wash-running);
    border-color: rgba(124, 156, 255, 0.35);
    color: #b9c8ff;
}

.pill-success {
    background: var(--wash-success);
    border-color: rgba(74, 222, 128, 0.32);
    color: #86efac;
}

.pill-warning {
    background: var(--wash-warning);
    border-color: rgba(251, 191, 36, 0.32);
    color: #fcd34d;
}

.pill-danger {
    background: var(--wash-danger);
    border-color: rgba(248, 113, 113, 0.34);
    color: #fca5a5;
}

.pill-muted {
    background: var(--wash-idle);
    border-color: var(--border-strong);
    color: var(--text-faint);
}

.pill-neutral {
    background: transparent;
    border-color: var(--border-strong);
    color: var(--text-muted);
}

/* --- dots: the enabled/disabled indicator on a pipeline --- */

.dot {
    display: inline-block;
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: var(--status-idle);
    flex: 0 0 auto;
}

.dot-on {
    background: var(--success);
    box-shadow: 0 0 0 3px var(--wash-success);
}

.dot-off {
    background: var(--status-idle);
    box-shadow: 0 0 0 3px var(--wash-idle);
}

/* --- badges and tags --- */

.badge {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 20px;
    padding: 0 6px;
    border-radius: var(--radius-pill);
    background: var(--surface-raised);
    border: 1px solid var(--border);
    font-family: var(--font-mono);
    font-size: var(--fs-xs);
    color: var(--text-muted);
}

.tag {
    display: inline-block;
    padding: 1px 8px;
    border-radius: var(--radius-sm);
    background: var(--bg-deep);
    border: 1px solid var(--border);
    font-family: var(--font-mono);
    font-size: var(--fs-xs);
    color: var(--text-muted);
}

/* --- tables --- */

/* Every .table must be wrapped in this. Wide tables scroll inside themselves. */
.table-scroll {
    overflow-x: auto;
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    background: var(--surface);
}

.table {
    width: 100%;
    border-collapse: collapse;
    font-size: var(--fs-sm);
}

.table th {
    text-align: left;
    padding: 9px 14px;
    background: var(--bg-deep);
    border-bottom: 1px solid var(--border);
    font-family: var(--font-mono);
    font-size: var(--fs-xs);
    font-weight: 500;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--text-faint);
    white-space: nowrap;
}

.table td {
    padding: 10px 14px;
    border-bottom: 1px solid var(--border);
    vertical-align: middle;
}

.table tbody tr:last-child td {
    border-bottom: 0;
}

.table tbody tr:hover {
    background: var(--surface-raised);
}

.table-compact td {
    padding: 6px 12px;
}

.table .num {
    text-align: right;
    font-family: var(--font-mono);
    font-variant-numeric: tabular-nums;
}

/* A whole row that navigates. Put it on the <tr>. */
.row-link {
    cursor: pointer;
}

.row-link:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: -2px;
}

/* --- forms --- */

/* Two columns on a roomy screen, one when it gets tight. */
.form-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
    gap: 16px 20px;
    align-items: start;
}

.field {
    display: flex;
    flex-direction: column;
    gap: 5px;
    min-width: 0;
}

/* Full-width field inside a .form-grid — prompts, descriptions, style bibles. */
.field-wide {
    grid-column: 1 / -1;
}

/* Two controls side by side inside one .field (min/max pairs). */
.field-row {
    display: flex;
    gap: 10px;
    flex-wrap: wrap;
}

.field-label {
    font-family: var(--font-mono);
    font-size: var(--fs-xs);
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--text-muted);
}

.field-hint {
    font-size: var(--fs-sm);
    color: var(--text-faint);
    line-height: 1.45;
}

.field-error,
.validation-message {
    font-size: var(--fs-sm);
    color: var(--danger);
}

/* ValidationSummary renders <ul class="validation-errors">. */
.validation-errors {
    list-style: none;
    padding: 0;
    margin: 0;
    color: var(--danger);
    font-size: var(--fs-sm);
}

/* The class-less selectors are a safety net: a bare <input> inside a .form-grid
   still looks right if someone forgets .input. */
.input,
.textarea,
.select,
.form-grid input:not([type="checkbox"]):not([type="radio"]):not([type="file"]),
.form-grid textarea,
.form-grid select {
    width: 100%;
    padding: 7px 10px;
    background: var(--bg-deep);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    color: var(--text);
    font-family: var(--font-ui);
    font-size: var(--fs-base);
    line-height: 1.45;
}

.input:hover,
.textarea:hover,
.select:hover {
    border-color: var(--border-strong);
}

.input:focus,
.textarea:focus,
.select:focus,
.form-grid input:not([type="checkbox"]):not([type="radio"]):not([type="file"]):focus,
.form-grid textarea:focus,
.form-grid select:focus {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-quiet);
}

.input:disabled,
.textarea:disabled,
.select:disabled {
    opacity: 0.55;
    cursor: not-allowed;
}

.input::placeholder,
.textarea::placeholder {
    color: var(--text-faint);
}

.textarea,
.form-grid textarea {
    min-height: 92px;
    resize: vertical;
    font-family: var(--font-mono);
    font-size: var(--fs-sm);
    line-height: 1.6;
}

/* For the system prompt: a real editing surface, not a two-line box. */
.textarea-tall {
    min-height: 340px;
}

.select,
.form-grid select {
    cursor: pointer;
}

/* Put this on the <label> that wraps a checkbox and its text. */
.checkbox {
    display: flex;
    align-items: center;
    gap: 9px;
    cursor: pointer;
    font-size: var(--fs-base);
    padding-top: 4px;
    user-select: none;
}

/* Input plus a trailing button — the password reveal toggle on the YouTube tab. */
.input-group {
    display: flex;
    gap: 6px;
    align-items: stretch;
}

.input-group .input {
    flex: 1 1 auto;
    min-width: 0;
}

.form-actions {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
    margin-top: 20px;
    padding-top: 16px;
    border-top: 1px solid var(--border);
}

/* --- buttons --- */

.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 7px;
    padding: 7px 14px;
    border-radius: var(--radius);
    border: 1px solid var(--border-strong);
    background: var(--surface-raised);
    color: var(--text);
    font-family: var(--font-ui);
    font-size: var(--fs-base);
    font-weight: 500;
    line-height: 1.4;
    cursor: pointer;
    white-space: nowrap;
    transition: background 120ms ease, border-color 120ms ease;
}

.btn:hover {
    background: #262b36;
    border-color: #3a4150;
    text-decoration: none;
}

.btn:disabled,
.btn[disabled] {
    opacity: 0.45;
    cursor: not-allowed;
}

.btn:disabled:hover,
.btn[disabled]:hover {
    background: var(--surface-raised);
    border-color: var(--border-strong);
}

.btn-primary {
    background: var(--accent);
    border-color: var(--accent);
    color: #0b1020;
}

.btn-primary:hover {
    background: #93adff;
    border-color: #93adff;
}

.btn-primary:disabled:hover {
    background: var(--accent);
    border-color: var(--accent);
}

.btn-danger {
    background: var(--wash-danger);
    border-color: rgba(248, 113, 113, 0.4);
    color: #fca5a5;
}

.btn-danger:hover {
    background: rgba(248, 113, 113, 0.2);
    border-color: var(--danger);
    color: #fecaca;
}

.btn-ghost {
    background: transparent;
    border-color: var(--border);
    color: var(--text-muted);
}

.btn-ghost:hover {
    background: var(--surface);
    border-color: var(--border-strong);
    color: var(--text);
}

.btn-sm {
    padding: 4px 10px;
    font-size: var(--fs-sm);
}

.btn-block {
    width: 100%;
}

.btn-group {
    display: inline-flex;
    gap: 6px;
    flex-wrap: wrap;
}

/* --- tabs --- */

/* Underline tabs rather than folder tabs: no negative margins to be clipped by the
   strip's own overflow, and the active marker speaks the same language as the
   sidebar's left accent bar. */
.tabs {
    display: flex;
    gap: 4px;
    margin-bottom: 22px;
    border-bottom: 1px solid var(--border);
    overflow-x: auto;
    scrollbar-width: thin;
}

.tab {
    flex: 0 0 auto;
    padding: 8px 13px;
    border: 0;
    border-bottom: 2px solid transparent;
    border-radius: var(--radius-sm) var(--radius-sm) 0 0;
    background: transparent;
    color: var(--text-muted);
    font-family: var(--font-ui);
    font-size: var(--fs-base);
    cursor: pointer;
    white-space: nowrap;
}

.tab:hover {
    color: var(--text);
    background: var(--surface);
    text-decoration: none;
}

.tab-active,
.tab-active:hover {
    background: transparent;
    border-bottom-color: var(--accent);
    color: var(--text);
    font-weight: 500;
}

/* --- banners --- */

.banner {
    display: flex;
    align-items: flex-start;
    gap: 11px;
    padding: 11px 14px;
    border: 1px solid var(--border-strong);
    border-left-width: 3px;
    border-radius: var(--radius);
    background: var(--surface);
    font-size: var(--fs-sm);
    color: var(--text);
}

.banner-icon {
    flex: 0 0 auto;
    line-height: 1.5;
    font-size: var(--fs-base);
}

.banner-body {
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 3px;
}

.banner-title {
    font-weight: 600;
    font-size: var(--fs-base);
}

.banner-info {
    border-color: rgba(124, 156, 255, 0.3);
    border-left-color: var(--accent);
    background: var(--wash-running);
}

.banner-warn {
    border-color: rgba(251, 191, 36, 0.28);
    border-left-color: var(--warning);
    background: var(--wash-warning);
}

.banner-error {
    border-color: rgba(248, 113, 113, 0.3);
    border-left-color: var(--danger);
    background: var(--wash-danger);
}

.banner-success {
    border-color: rgba(74, 222, 128, 0.28);
    border-left-color: var(--success);
    background: var(--wash-success);
}

/* --- progress --- */

.progress {
    position: relative;
    width: 100%;
    height: 6px;
    border-radius: var(--radius-pill);
    background: var(--bg-deep);
    border: 1px solid var(--border);
    overflow: hidden;
}

.progress-bar {
    display: block;
    height: 100%;
    width: 0;
    border-radius: inherit;
    background: var(--accent);
    transition: width 300ms ease;
}

/* No percentage to show — a run is somewhere in the middle of a stage. */
.progress-indeterminate .progress-bar {
    width: 34%;
    animation: progress-sweep 1.5s ease-in-out infinite;
}

@keyframes progress-sweep {
    0% {
        transform: translateX(-110%);
    }
    100% {
        transform: translateX(320%);
    }
}

.progress-label {
    display: flex;
    justify-content: space-between;
    gap: 12px;
    margin-bottom: 5px;
    font-family: var(--font-mono);
    font-size: var(--fs-xs);
    letter-spacing: 0.04em;
    color: var(--text-muted);
}

/* --- empty state --- */

.empty-state {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 8px;
    padding: 46px 24px;
    border: 1px dashed var(--border-strong);
    border-radius: var(--radius-lg);
    background: rgba(23, 26, 33, 0.5);
    text-align: center;
}

.empty-state-icon {
    font-size: 22px;
    color: var(--text-faint);
    line-height: 1;
}

.empty-state-title {
    font-size: var(--fs-md);
    font-weight: 600;
}

.empty-state-text {
    max-width: 46ch;
    color: var(--text-muted);
    font-size: var(--fs-sm);
}

.empty-state-actions {
    display: flex;
    gap: 8px;
    flex-wrap: wrap;
    margin-top: 6px;
}

/* --- paginator --- */

.paginator {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 14px;
    flex-wrap: wrap;
    margin-top: 14px;
}

.paginator-info {
    font-family: var(--font-mono);
    font-size: var(--fs-xs);
    letter-spacing: 0.04em;
    color: var(--text-faint);
}

.paginator-controls {
    display: flex;
    align-items: center;
    gap: 8px;
}

/* --- filter / action bar above a table --- */

.toolbar {
    display: flex;
    align-items: flex-end;
    gap: 12px;
    flex-wrap: wrap;
    margin-bottom: 14px;
}

.toolbar-field {
    display: flex;
    flex-direction: column;
    gap: 4px;
    min-width: 170px;
}

/* --- ConfirmButton's expanded state --- */

.confirm-inline {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
    padding: 9px 11px;
    border: 1px solid rgba(248, 113, 113, 0.34);
    border-radius: var(--radius);
    background: var(--wash-danger);
}

.confirm-prompt {
    font-size: var(--fs-sm);
    color: var(--text);
}

.confirm-inline .input {
    width: auto;
    min-width: 180px;
    flex: 1 1 180px;
    font-family: var(--font-mono);
    font-size: var(--fs-sm);
}

/* -------------------------------------------------- 5. Domain-shaped pieces */

/* --- the stage strip: this app's signature element ---------------------------
   A run is a fixed, ordered sequence of stages, so its progress is drawn as an
   ordered ladder of segments rather than a single bar. Same motif appears in the
   brand mark, on dashboard cards, and inline in the run list.

   Bare:      <div class="stage-strip">
                <span class="stage-seg stage-seg-done"></span> ...
   Labelled:  <ol class="stage-strip stage-strip-labelled">
                <li class="stage-seg stage-seg-done">
                  <span class="stage-label">Script</span>
                  <span class="stage-time">4.2s</span>
                </li> ...
   Modifiers set --seg, which is all the bar reads.                            */

.stage-strip {
    display: flex;
    gap: 3px;
    align-items: stretch;
    margin: 0;
    padding: 0;
    list-style: none;
    min-width: 0;
}

.stage-seg {
    --seg: var(--border-strong);
    flex: 1 1 0;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 5px;
}

.stage-seg::before {
    content: "";
    display: block;
    height: 5px;
    border-radius: 2px;
    background: var(--seg);
}

.stage-seg-pending {
    --seg: var(--border-strong);
}

.stage-seg-done {
    --seg: var(--success);
}

.stage-seg-running {
    --seg: var(--running);
}

.stage-seg-failed {
    --seg: var(--danger);
}

.stage-seg-skipped {
    --seg: var(--status-idle);
}

.stage-seg-running::before {
    animation: seg-pulse 1.4s ease-in-out infinite;
}

@keyframes seg-pulse {
    0%,
    100% {
        opacity: 1;
    }
    50% {
        opacity: 0.4;
    }
}

.stage-strip-labelled {
    gap: 6px;
}

.stage-label {
    font-family: var(--font-mono);
    font-size: var(--fs-xs);
    letter-spacing: 0.03em;
    color: var(--text-muted);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.stage-time {
    font-family: var(--font-mono);
    font-size: var(--fs-xs);
    font-variant-numeric: tabular-nums;
    color: var(--text-faint);
}

/* --- vertical status timeline (a video's walk through VideoStatus) --- */

.timeline {
    display: flex;
    flex-direction: column;
    margin: 0;
    padding: 0;
    list-style: none;
}

.timeline-step {
    position: relative;
    display: flex;
    align-items: baseline;
    gap: 12px;
    padding: 0 0 14px 22px;
    color: var(--text-faint);
    font-size: var(--fs-sm);
}

/* marker */
.timeline-step::before {
    content: "";
    position: absolute;
    left: 0;
    top: 6px;
    width: 9px;
    height: 9px;
    border-radius: 50%;
    border: 1px solid var(--border-strong);
    background: var(--bg);
}

/* connector to the next step */
.timeline-step::after {
    content: "";
    position: absolute;
    left: 4px;
    top: 16px;
    bottom: 0;
    width: 1px;
    background: var(--border);
}

.timeline-step:last-child {
    padding-bottom: 0;
}

.timeline-step:last-child::after {
    display: none;
}

.timeline-step-done {
    color: var(--text);
}

.timeline-step-done::before {
    background: var(--success);
    border-color: var(--success);
}

.timeline-step-current {
    color: var(--text);
    font-weight: 500;
}

.timeline-step-current::before {
    background: var(--running);
    border-color: var(--running);
    box-shadow: 0 0 0 3px var(--wash-running);
}

.timeline-step-failed {
    color: #fca5a5;
}

.timeline-step-failed::before {
    background: var(--danger);
    border-color: var(--danger);
}

.timeline-label {
    min-width: 0;
}

.timeline-note {
    font-family: var(--font-mono);
    font-size: var(--fs-xs);
    color: var(--text-faint);
}

/* --- scene grid --- */

/* Three across on a full-width desktop, not the six or seven the original 140px
   minimum packed in. A tile carries a video you are meant to actually watch plus a
   paragraph of narration and a whole visual prompt, and at ~200px the clip was a
   postage stamp and the mono prompt wrapped every four words.

   420px is chosen to sit in the middle of the band that yields exactly three: on
   the ~1496px of usable width this page has, a fourth column needs the minimum
   below 362px and a third needs it below 488px. Picking the middle means a slightly
   different window width does not flip the count. Narrower viewports still fall to
   two and then one on their own — this is auto-fill, not a fixed three. */
.scene-grid {
    display: grid;
    /* min(420px, 100%), not a bare 420px: on a viewport narrower than the minimum,
       auto-fill still lays a 420px track and the whole page scrolls sideways. */
    grid-template-columns: repeat(auto-fill, minmax(min(420px, 100%), 1fr));
    gap: 20px;
}

.scene-tile {
    display: flex;
    flex-direction: column;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    overflow: hidden;
    min-width: 0;
}

/* Dimmed rather than hidden: a skipped shot still carries its slice of the story, and un-skipping it is one click. */
.scene-tile-skipped {
    opacity: 0.55;
    border-style: dashed;
}

/* The box is the shape the PIPELINE builds, not a constant. It was hardcoded 9:16
   back when vertical was the only shape the process could produce; a horizontal
   pipeline shown that way wastes two thirds of every tile's height and — with
   object-fit doing the fitting — shows a centre crop rather than the frame the
   operator is trying to review. `.scene-thumb-wide` is set from
   VisualSettings.Orientation. */
.scene-thumb {
    display: block;
    width: 100%;
    aspect-ratio: 9 / 16;

    /* contain, not cover: a thumbnail that crops is lying about the shot. Where the
       source matches the canvas — Veo's 16:9 in a horizontal pipeline — the two are
       identical anyway, and where it does not, the assembler blur-fills rather than
       crops, so showing the whole frame is the closer preview. */
    object-fit: contain;
    background: var(--bg-deep);
    border-bottom: 1px solid var(--border);
}

.scene-thumb-missing {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    aspect-ratio: 9 / 16;
    background: var(--bg-deep);
    border-bottom: 1px solid var(--border);
    color: var(--text-faint);
    font-family: var(--font-mono);
    font-size: var(--fs-xs);
    text-align: center;
    padding: 8px;
}

/* Applied alongside .scene-thumb OR .scene-thumb-missing, so it has to come after
   both — all three are single-class selectors, and the last one wins. Set from
   VisualSettings.Orientation, so an empty tile keeps the same shape as a filled one
   and the grid does not reflow as scenes generate. */
.scene-thumb-wide {
    aspect-ratio: 16 / 9;
}

/* Padding and gap scaled with the tile. The old 9/10/11 was sized for a ~200px
   card, where any more would have left no room for the text; on a ~485px one it
   reads as text jammed against the border. */
.scene-body {
    display: flex;
    flex-direction: column;
    gap: 9px;
    padding: 14px 16px 16px;
    min-width: 0;
}

.scene-order {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    font-family: var(--font-mono);
    font-size: var(--fs-xs);
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--text-faint);
}

.scene-text {
    font-size: var(--fs-sm);
    line-height: 1.45;
    color: var(--text);
}

.scene-prompt {
    font-family: var(--font-mono);
    font-size: var(--fs-xs);
    line-height: 1.5;
    color: var(--text-muted);
    overflow-wrap: anywhere;
}

.scene-meta {
    font-family: var(--font-mono);
    font-size: var(--fs-xs);
    color: var(--text-faint);
}

/* --- log viewer --- */

.log-view {
    max-height: 420px;
    overflow: auto;
    padding: 10px 12px;
    background: var(--bg-deep);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    font-family: var(--font-mono);
    font-size: var(--fs-sm);
    line-height: 1.6;
}

.log-line {
    display: grid;
    grid-template-columns: 72px 130px minmax(0, 1fr);
    gap: 12px;
    padding: 1px 0;
}

.log-time {
    color: var(--text-faint);
    font-variant-numeric: tabular-nums;
}

.log-stage {
    color: var(--accent);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.log-message {
    color: var(--text);
    overflow-wrap: anywhere;
}

/* --- media players --- */

.player {
    display: block;
    width: 100%;
    max-width: 320px;
    aspect-ratio: 9 / 16;
    background: #000;
    border: 1px solid var(--border);
    border-radius: var(--radius);
}

.audio-player {
    width: 100%;
    max-width: 380px;
    height: 34px;
}

/* --- login --- */

.login-shell {
    min-height: 100vh;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 24px;
}

.login-card {
    display: flex;
    flex-direction: column;
    gap: 16px;
    width: 100%;
    max-width: 340px;
    padding: 26px 24px;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
}

.login-brand {
    display: flex;
    align-items: center;
    gap: 10px;
    padding-bottom: 14px;
    border-bottom: 1px solid var(--border);
}

.login-hint {
    font-family: var(--font-mono);
    font-size: var(--fs-xs);
    letter-spacing: 0.04em;
    color: var(--text-faint);
    text-align: center;
}

/* -------------------------------------------------------------- 6. Utilities */

/* Ids, crons, paths, durations, YouTube ids — anything you compare or copy. */
.mono {
    font-family: var(--font-mono);
    font-size: 0.94em;
    font-variant-numeric: tabular-nums;
}

.muted {
    color: var(--text-muted);
}

.faint {
    color: var(--text-faint);
}

.small {
    font-size: var(--fs-sm);
}

.strong {
    font-weight: 600;
}

.nowrap {
    white-space: nowrap;
}

.truncate {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    min-width: 0;
}

.break-word {
    overflow-wrap: anywhere;
}

/* Narration scripts and prompts: keep the author's line breaks. */
/* Prose read by a human — a narration script, a style bible. Capped in characters
   rather than pixels because the cap is about the eye's line length, not the page:
   widening --content-max let these run to ~140 characters, which is past the point
   where finding the next line back becomes work. */
.pre-wrap {
    white-space: pre-wrap;
    overflow-wrap: anywhere;
    line-height: 1.7;
    max-width: 100ch;
}

.right {
    text-align: right;
}

.center {
    text-align: center;
}

/* Vertical rhythm without per-page margins. */
.stack {
    display: flex;
    flex-direction: column;
    gap: 18px;
    min-width: 0;
}

.stack-tight {
    display: flex;
    flex-direction: column;
    gap: 8px;
    min-width: 0;
}

.row {
    display: flex;
    align-items: center;
    gap: 12px;
    flex-wrap: wrap;
    min-width: 0;
}

.row-tight {
    display: flex;
    align-items: center;
    gap: 6px;
    flex-wrap: wrap;
    min-width: 0;
}

.spacer {
    flex: 1 1 auto;
}

.divider {
    height: 1px;
    background: var(--border);
    border: 0;
    margin: 0;
}

/* Two panels side by side, collapsing to one column when narrow. */
.split {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
    gap: 16px;
    align-items: start;
}

/* Detail pages: a wide main column plus a narrow facts rail. */
.split-aside {
    display: grid;
    grid-template-columns: minmax(0, 2fr) minmax(240px, 1fr);
    gap: 20px;
    align-items: start;
}

/* ------------------------------------------------------------ 7. Responsive */

@media (max-width: 1080px) {
    .split-aside {
        grid-template-columns: minmax(0, 1fr);
    }
}

/* Under ~800px the sidebar stops being a column and becomes a top bar with a
   horizontally scrolling nav. No JS, no hamburger — six links fit in a strip. */
@media (max-width: 800px) {
    .app-shell {
        grid-template-columns: minmax(0, 1fr);
    }

    .sidebar {
        position: static;
        height: auto;
        flex-direction: row;
        align-items: center;
        gap: 14px;
        padding: 10px 14px;
        border-right: 0;
        border-bottom: 1px solid var(--border);
        overflow-x: auto;
    }

    .brand {
        flex: 0 0 auto;
    }

    .brand-sub {
        display: none;
    }

    /* Don't let the strip compress the links — it scrolls instead. */
    .nav-list {
        flex: 0 0 auto;
        flex-direction: row;
        gap: 4px;
    }

    .nav-link {
        border-left: 0;
        border-bottom: 2px solid transparent;
        border-radius: var(--radius) var(--radius) 0 0;
        padding: 5px 10px;
        font-size: var(--fs-sm);
    }

    .nav-link.active {
        border-left: 0;
        border-bottom-color: var(--accent);
    }

    .sidebar-footer {
        flex: 0 0 auto;
        margin-top: 0;
        margin-left: auto;
        flex-direction: row;
        align-items: center;
        padding-top: 0;
        border-top: 0;
    }

    .sidebar-note {
        display: none;
    }

    .content {
        padding: 20px 16px 48px;
    }

    .page-title {
        font-size: 21px;
    }

    .kv {
        grid-template-columns: minmax(0, 1fr);
        gap: 2px 0;
    }

    .kv-value {
        margin-bottom: 8px;
    }

    .log-line {
        grid-template-columns: 62px minmax(0, 1fr);
    }

    .log-stage {
        display: none;
    }
}

@media (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after {
        animation-duration: 0.001ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.001ms !important;
    }

    .progress-indeterminate .progress-bar {
        width: 100%;
        opacity: 0.5;
    }
}

/* ---------------------------------------------------------------------------
   Per-scene repair and the version history on /videos/{id}.
   --------------------------------------------------------------------------- */

/* A quieter button for actions that sit inside a dense list — one per scene tile,
   one per version row — where the full-size .btn would dominate the content it
   belongs to. */
.btn-small {
    padding: 4px 9px;
    font-size: var(--fs-xs);
    gap: 5px;
}

/* Same weight as .btn-ghost but reading as a real action rather than a fallback:
   Rebuild is a deliberate choice sitting beside Approve, not an escape hatch. */
.btn-secondary {
    background: var(--surface-raised);
    border-color: var(--border-strong);
    color: var(--text);
}
.btn-secondary:hover {
    background: var(--surface);
    border-color: var(--accent);
}

/* Sits directly under the tile's Scene N line, because it says which of that
   scene's visuals is on screen — metadata about the picture above it, not one of
   the actions grouped at the bottom. Only rendered when a scene has more than one
   version, so it never takes space on an ordinary breakdown. */
.scene-versions {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: var(--fs-xs);
    color: var(--text-muted);
}

/* Square-ish, so a single chevron does not sit in a button shaped for a word. */
.scene-versions .btn-small {
    padding: 2px 8px;
    line-height: 1.2;
}

/* The line above the cast editor: what this shot sends, and the toggle. Its own flex
   row so the button keeps a gap from the text instead of butting against the comma
   at the end of it. */
.scene-cast-head {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: var(--fs-xs);
    color: var(--text-faint);
}

/* The per-shot cast editor.

   A column, not a wrapping row. Six subjects whose names run to "Small knitted brown
   teddy — picture 2" tile into one unbroken paragraph at tile width, labels break
   across lines, and the checkbox you are aiming at ends up wedged between two words.
   One row each costs six lines and is legible. */
.scene-cast {
    display: flex;
    flex-direction: column;
    margin-top: 6px;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    background: var(--bg-deep);
    overflow: hidden;
}

.scene-cast label {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 5px 8px;
    cursor: pointer;
    font-size: var(--fs-xs);
    user-select: none;
}

.scene-cast label:hover {
    background: var(--surface-raised);
}

/* Right-aligned so the bound picture reads as a column of its own down the list,
   which is what makes "two subjects share picture 1" visible at a glance. */
.scene-cast .scene-cast-slot {
    margin-left: auto;
    color: var(--text-faint);
    white-space: nowrap;
}

.scene-cast .scene-cast-note {
    padding: 5px 8px;
    border-top: 1px solid var(--border);
    color: var(--text-faint);
    font-size: var(--fs-xs);
}

/* Pushed to the bottom of the tile with a rule above it, so the buttons read as
   actions on the scene rather than as more of its metadata. */
.scene-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin-top: 10px;
    padding-top: 10px;
    border-top: 1px solid var(--border);
}

.version-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 2px;
}

/* Three columns: identity, when-and-what, and the download. The middle one takes
   the slack so the button stays hard right however long a music bed's name is. */
.version-row {
    display: grid;
    grid-template-columns: auto 1fr auto;
    align-items: center;
    gap: 12px;
    padding: 9px 4px;
    border-bottom: 1px solid var(--border);
}
.version-row:last-child {
    border-bottom: none;
}

.version-main {
    display: flex;
    align-items: center;
    gap: 8px;
}

/* Allowed to shrink and ellipsize — a folder/track name can be long, and it is the
   least important thing in the row. */
.version-meta {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

@media (max-width: 640px) {
    /* Stacked rather than squeezed: three columns in a phone-width sidebar leaves
       nothing readable. */
    .version-row {
        grid-template-columns: 1fr;
        gap: 4px;
    }
}

/* The inline visual-prompt editor in a scene tile. Monospaced and small, because what
   is being edited is a model prompt rather than prose, and the tile is narrow. */
.scene-prompt-edit {
    width: 100%;
    margin-top: 4px;
    font-family: var(--font-mono);
    font-size: var(--fs-xs);
    line-height: 1.5;
    resize: vertical;
}

/* A card nested inside another card — the new-scene form sitting inside the Scenes
   panel. Recessed rather than raised so it reads as part of that panel rather than
   as a second one competing with it. */
.card.inset {
    background: var(--surface);
    border-style: dashed;
    margin-bottom: 12px;
}
