/* ============================================
   ClipForge — Premium Design System
   ============================================ */

:root {
    --bg-primary: #07070d;
    --bg-secondary: #0e0e18;
    --bg-tertiary: #141422;
    --bg-card: rgba(255,255,255,0.035);
    --bg-card-hover: rgba(255,255,255,0.06);
    --border-subtle: rgba(255,255,255,0.06);
    --border-hover: rgba(255,255,255,0.12);
    /* audit53 A11Y-04: dedicated input boundary token. --border-subtle
       blends to ≈1.12:1 against the page background (WCAG 1.4.11
       needs 3:1 for component identification); white at 0.36 alpha
       computes to 3.21:1 on --bg-primary, 3.31:1 on --bg-secondary,
       3.35:1 on --bg-tertiary. Card borders keep --border-subtle —
       1.4.11 does not apply to them. */
    --border-input: rgba(255,255,255,0.36);
    /* audit53 A11Y-01: button-grade gradient. --gradient-primary
       (#8B5CF6 → #06B6D4) computes to 4.23:1 → 2.43:1 under white
       text — below the 4.5:1 AA floor at EVERY point. This darkened
       pair computes to 4.69:1 (#8355EC) → 5.93:1 (#0369A1), and a
       blend's luminance never exceeds the worse endpoint's, so the
       whole gradient passes. --gradient-primary stays for brand /
       decorative use (logo mark, progress fills, slider thumb),
       where no text sits on it. */
    --gradient-btn: linear-gradient(135deg, #8355EC 0%, #0369A1 100%);
    --text-primary: #f0f0f8;
    --text-secondary: #a0a0b8;
    --text-muted: #8a8aa3;
    --text-error: #f87171;
    --focus-ring: #8b5cf6;
    --accent-purple: #8B5CF6;
    /* v2.13.0 (UX2-04/05/09/15): text-grade purple for SMALL text on
       dark or purple-tinted surfaces. --accent-purple (#8B5CF6) as
       text computes to 2.5–4.5:1 on those tints (below the 4.5:1 AA
       floor); #C4B5FD computes to 5.9–10.3:1 on every one of them
       (badge tints incl. the pulse peak, social-button hover tint,
       toast tint, nav/brand/phase tints — verified arithmetically
       in the wave notes). --accent-purple stays for large text,
       icons, borders and fills, where it passes. */
    --accent-purple-light: #C4B5FD;
    --accent-cyan: #22D3EE;
    --accent-green: #34D399;
    --accent-orange: #FBBF24;
    --accent-red: #F87171;
    /* audit53 UX-06: the two accent hexes inline styles kept
       re-typing (engine badges, format badges, action buttons) are
       tokens now — new markup should reference these, not raw hex. */
    --accent-blue: #60a5fa;
    --accent-pink: #f472b6;
    /* audit53 UX-06/UX-26: spacing scale. New layout code composes
       margins/padding from these steps instead of one-off px. */
    --space-1: 4px;
    --space-2: 8px;
    --space-3: 12px;
    --space-4: 16px;
    --space-5: 24px;
    --space-6: 32px;
    /* audit53 UX-07: type scale. --text-xs is the floor and is for
       meta/labels only; body copy sits at --text-base or above. */
    --text-xs: 0.75rem;
    --text-sm: 0.82rem;
    --text-base: 0.9rem;
    --text-lg: 1rem;
    --text-xl: 1.2rem;
    --gradient-primary: linear-gradient(135deg, #8B5CF6 0%, #06B6D4 100%);
    --gradient-glass: linear-gradient(135deg, rgba(255,255,255,0.06), rgba(255,255,255,0.02));
    --shadow-md: 0 8px 32px rgba(0,0,0,0.5);
    --shadow-glow-sm: 0 0 20px rgba(139,92,246,0.1);
    --radius-sm: 10px;
    --radius-md: 14px;
    --radius-lg: 20px;
    --radius-xl: 28px;
    --font: 'Inter', 'Cairo', -apple-system, BlinkMacSystemFont, sans-serif;
    --font-mono: 'JetBrains Mono', monospace;
    --ease: cubic-bezier(0.4, 0, 0.2, 1);
    --ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
}

*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }
html { font-size: 16px; scroll-behavior: smooth; }

body {
    font-family: var(--font);
    background: var(--bg-primary);
    color: var(--text-primary);
    line-height: 1.6;
    min-height: 100vh;
    min-height: 100dvh; /* audit53 R10-08: dynamic viewport units where supported */
    overflow-x: hidden;
    -webkit-font-smoothing: antialiased;
}

a { color: inherit; text-decoration: none; }
button { cursor: pointer; font-family: var(--font); border: none; background: none; }
input, select, textarea { font-family: var(--font); }

/* --- Ambient Background --- */
.ambient-bg { position: fixed; inset: 0; z-index: 0; pointer-events: none; overflow: hidden; }
.ambient-orb {
    position: absolute;
    border-radius: 50%;
    filter: blur(120px);
    opacity: 0.35;
}
.ambient-orb-1 {
    width: 600px; height: 600px;
    background: radial-gradient(circle, rgba(139,92,246,0.4), transparent 70%);
    top: -15%; left: -10%;
    animation: orbFloat1 25s ease-in-out infinite;
}
.ambient-orb-2 {
    width: 500px; height: 500px;
    background: radial-gradient(circle, rgba(6,182,212,0.3), transparent 70%);
    top: 50%; right: -10%;
    animation: orbFloat2 30s ease-in-out infinite;
}
.ambient-orb-3 {
    width: 400px; height: 400px;
    background: radial-gradient(circle, rgba(244,114,182,0.25), transparent 70%);
    bottom: -10%; left: 30%;
    animation: orbFloat3 20s ease-in-out infinite;
}
@keyframes orbFloat1 { 0%,100%{transform:translate(0,0)} 50%{transform:translate(60px,40px)} }
@keyframes orbFloat2 { 0%,100%{transform:translate(0,0)} 50%{transform:translate(-50px,-60px)} }
@keyframes orbFloat3 { 0%,100%{transform:translate(0,0)} 50%{transform:translate(40px,-30px)} }
@keyframes fadeIn { from{opacity:0} to{opacity:1} }
@keyframes scaleIn { from{opacity:0;transform:scale(0.95)} to{opacity:1;transform:scale(1)} }

/* --- Navbar --- */
.navbar {
    position: fixed; top: 0; left: 0; right: 0; z-index: 100;
    background: rgba(7,7,13,0.75);
    backdrop-filter: blur(24px) saturate(1.2);
    border-bottom: 1px solid var(--border-subtle);
}
.nav-inner {
    max-width: 1200px; margin: 0 auto; /* audit53 UX-13: match .main-content so the bar and the page share one measure */
    display: flex; align-items: center; justify-content: space-between;
    padding: 0 2rem; height: 60px;
}
.nav-brand { display: flex; align-items: center; gap: 10px; }
.brand-icon {
    width: 32px; height: 32px;
    background: var(--gradient-primary);
    border-radius: 9px; display: flex; align-items: center; justify-content: center;
    box-shadow: 0 4px 16px rgba(139,92,246,0.3);
}
.brand-name {
    font-size: 1.15rem; font-weight: 800; letter-spacing: -0.3px;
    background: var(--gradient-primary);
    -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text;
}
.brand-badge {
    font-size: 0.55rem; font-weight: 700; text-transform: uppercase;
    padding: 2px 6px; border-radius: 5px;
    background: rgba(139,92,246,0.15); color: var(--accent-purple-light); /* v2.13.0 (UX2-15): was 4.17:1, now 9.58:1 */
    letter-spacing: 1px; border: 1px solid rgba(139,92,246,0.2);
}
.nav-links { display: flex; gap: 2px; }
.nav-link {
    display: flex; align-items: center; gap: 7px;
    padding: 7px 14px; border-radius: 8px;
    font-size: 0.82rem; font-weight: 500;
    color: var(--text-muted);
    transition: all 0.25s var(--ease);
}
.nav-link:hover { color: var(--text-secondary); background: rgba(255,255,255,0.04); }
.nav-link.active { color: var(--accent-purple-light); background: rgba(139,92,246,0.08); } /* v2.13.0 (UX2-15): was 4.47:1, now 10.27:1 */

/* --- Main --- */
.main-content {
    position: relative; z-index: 1;
    max-width: 1200px; margin: 0 auto;
    padding: 80px 2rem 3rem;
    min-height: 100vh;
    min-height: 100dvh; /* audit53 R10-08 */
}

/* --- Glass Card --- */
/* audit53 UX-27 (stated rule): .glass-card (translucent + blur) is
   for TOP-LEVEL page cards whose backdrop is the ambient page
   background. Cards rendered inside overlays/modals or nested
   inside another card use the SOLID .setting-card surface instead —
   glass-on-glass blurs text and muddies contrast. */
.glass-card {
    background: var(--gradient-glass);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-lg);
    backdrop-filter: blur(16px);
    transition: all 0.3s var(--ease);
    overflow-wrap: anywhere;
}
.glass-card:hover { border-color: var(--border-hover); box-shadow: var(--shadow-glow-sm); }

/* --- Buttons --- */
.btn {
    display: inline-flex; align-items: center; gap: 8px;
    padding: 10px 22px; border-radius: var(--radius-sm);
    font-size: 0.84rem; font-weight: 600;
    transition: all 0.25s var(--ease); border: 1px solid transparent;
    position: relative; overflow: hidden;
}
.btn-primary {
    background: var(--gradient-btn); color: white; /* audit53 A11Y-01: ≥4.5:1 at both ends */
    box-shadow: 0 4px 20px rgba(139,92,246,0.25);
    min-height: 44px;
}
.btn-primary:hover { transform: translateY(-1px); box-shadow: 0 6px 30px rgba(139,92,246,0.35); }
.btn-primary:active { transform: translateY(0); }
.btn-secondary {
    background: var(--bg-card); color: var(--text-primary);
    border: 1px solid var(--border-subtle);
}
.btn-secondary:hover { background: var(--bg-card-hover); border-color: var(--border-hover); }
.btn-danger {
    background: rgba(248,113,113,0.1); color: var(--accent-red);
    border: 1px solid rgba(248,113,113,0.15);
}
.btn-danger:hover { background: rgba(248,113,113,0.18); }
.btn-sm { padding: 6px 14px; font-size: 0.78rem; min-height: 36px; }
.btn-lg { padding: 14px 30px; font-size: 0.95rem; border-radius: var(--radius-md); }
.btn:disabled { opacity: 0.4; cursor: not-allowed; transform: none !important; }

/* --- Inputs --- */
.input-group { display: flex; flex-direction: column; gap: 6px; }
.input-group label {
    font-size: 0.72rem; font-weight: 600; color: var(--text-muted);
    text-transform: uppercase; letter-spacing: 0.8px;
}
.input {
    padding: 11px 16px;
    background: rgba(255,255,255,0.03);
    border: 1px solid var(--border-input); /* audit53 A11Y-04: ≥3:1 boundary */
    border-radius: var(--radius-sm);
    color: var(--text-primary); font-size: 1rem;
    transition: all 0.25s var(--ease); outline: none;
}
.input:focus { border-color: var(--accent-purple); box-shadow: 0 0 0 3px rgba(139,92,246,0.1); }
.input::placeholder { color: var(--text-muted); }
/* C8-M-35/M-52: the create form's URL field carries .input-lg; the rule
   was swept away in the W4 broom, leaving the class inert and the iOS
   focus-zoom fix (font-size ≥16px) resting on .input alone. */
.input-lg { font-size: 1rem; }

select.input { cursor: pointer; }

/* --- Hero Section --- */
.hero-section { text-align: center; padding: 4rem 0 2rem; }
.hero-title {
    font-size: 3rem; font-weight: 900; letter-spacing: -1.5px;
    line-height: 1.1; margin-bottom: 0.75rem;
}
.hero-title-gradient {
    background: var(--gradient-primary);
    -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text;
}
.hero-subtitle { font-size: 1.05rem; color: var(--text-secondary); margin-bottom: 2.5rem; max-width: 500px; margin-left: auto; margin-right: auto; }
.url-input-wrapper {
    display: flex; gap: 10px; max-width: 680px; margin: 0 auto 2rem;
    background: rgba(255,255,255,0.03);
    border: 1px solid var(--border-input); /* audit53 A11Y-04: this wrapper IS the Home URL field's boundary (the inner input is borderless) */
    border-radius: var(--radius-md); padding: 6px;
    transition: all 0.3s var(--ease);
}
.url-input-wrapper:focus-within { border-color: var(--accent-purple); box-shadow: 0 0 0 3px rgba(139,92,246,0.08); }
.url-input-wrapper .input { flex: 1; border: none; background: transparent; padding: 12px 16px; }
.url-input-wrapper .input:focus { box-shadow: none; }
.url-input-wrapper .btn { border-radius: 10px; }

.clip-thumb-container {
    position: relative;
    width: 100%;
    aspect-ratio: 9 / 16;
    overflow: hidden;
    border-radius: 8px;
    background: var(--bg-tertiary);
    background-size: cover;
    background-position: center;
    cursor: pointer;
    flex-shrink: 0;
}
/* audit53 UX-08: .video-card was defined twice (here and in the
   library-grid section below) with conflicting radius/transition;
   the cascade silently took radius-md + 0.25s from the later rule.
   Merged into this single rule carrying exactly the values the
   cascade used to compute, plus the later block's hover. */
.video-card {
    background: var(--bg-card); border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md); overflow: hidden;
    transition: all 0.25s var(--ease); cursor: pointer;
}
.video-card:hover {
    border-color: var(--accent-purple);
    transform: translateY(-3px);
    box-shadow: var(--shadow-glow-sm);
}



/* --- Progress --- */
/* C4-40: the card spans the same 1200px content column as its sibling
   cards (was a centred 680px island with a different margin rhythm). */
.progress-container { margin-bottom: 1rem; padding: 2rem; }
.progress-stages { display: flex; justify-content: space-between; margin-bottom: 1.25rem; position: relative; }
.progress-stage { display: flex; flex-direction: column; align-items: center; gap: 8px; z-index: 1; }
.progress-dot {
    width: 36px; height: 36px; border-radius: 50%;
    background: var(--bg-tertiary); border: 2px solid var(--border-subtle);
    display: flex; align-items: center; justify-content: center;
    font-size: 0.75rem; transition: all 0.4s var(--ease);
}
.progress-dot.active { background: var(--gradient-btn); border-color: var(--accent-purple); box-shadow: 0 0 20px rgba(139,92,246,0.35); } /* audit53 A11Y-01: the dot carries a numeral — same button-grade gradient */
.progress-dot.done { background: var(--accent-green); border-color: var(--accent-green); }
.progress-label { font-size: 0.68rem; color: var(--text-muted); text-align: center; font-weight: 500; }
.progress-bar-track { width: 100%; height: 4px; background: var(--bg-tertiary); border-radius: 2px; overflow: hidden; }
.progress-bar-fill {
    height: 100%; background: var(--gradient-primary); border-radius: 2px;
    transition: width 0.6s var(--ease); position: relative;
}
.progress-bar-fill::after {
    content: ''; position: absolute; inset: 0;
    background: linear-gradient(90deg, transparent, rgba(255,255,255,0.35), transparent);
    animation: shimmer 2s ease infinite;
}
@keyframes shimmer { 0%{transform:translateX(-100%)} 100%{transform:translateX(100%)} }
.progress-message { text-align: center; margin-top: 1rem; font-size: 0.85rem; color: var(--text-secondary); font-weight: 500; display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: 8px; overflow-wrap: anywhere; }
.progress-spinner { display: inline-block; width: 14px; height: 14px; border: 2px solid var(--border-subtle); border-top-color: var(--accent-purple); border-radius: 50%; animation: spin 0.8s linear infinite; }
@keyframes spin { to { transform: rotate(360deg); } }

/* Download sub-progress bar */
.dl-progress-wrap { margin-top: 1rem; }
.dl-progress-track { width: 100%; height: 6px; background: var(--bg-tertiary); border-radius: 3px; overflow: hidden; }
.dl-progress-fill {
    height: 100%; width: 0%;
    background: linear-gradient(90deg, #34d399, #10b981);
    border-radius: 3px;
    transition: width 0.4s ease;
    position: relative;
}
.dl-progress-fill::after {
    content: ''; position: absolute; inset: 0;
    background: linear-gradient(90deg, transparent, rgba(255,255,255,0.4), transparent);
    animation: shimmer 1.2s ease infinite;
}
.dl-progress-meta { margin-top: 4px; font-size: 0.72rem; color: var(--text-muted); text-align: right; font-variant-numeric: tabular-nums; letter-spacing: 0.02em; }

/* Per-activity progress rows (v209): during Phase 1's parallel window
   the transcription and the background video download each own a row
   inside the progress card, so the two concurrent activities no
   longer overwrite the one shared status line. A row whose activity
   has no honest % (estimated download total / plan-less
   transcription) runs the indeterminate slide instead of a number. */
.progress-activities { margin-top: 1rem; display: flex; flex-direction: column; gap: 0.6rem; }
.progress-activity-line { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; }
.progress-activity-text { font-size: 0.78rem; color: var(--text-secondary); font-weight: 500; text-align: left; overflow-wrap: anywhere; }
.progress-activity-meta { font-size: 0.78rem; color: var(--text-secondary); font-variant-numeric: tabular-nums; white-space: nowrap; }
.progress-activity-track { width: 100%; height: 3px; background: var(--bg-tertiary); border-radius: 2px; overflow: hidden; margin-top: 4px; }
.progress-activity-fill { height: 100%; width: 0%; background: var(--gradient-primary); border-radius: 2px; transition: width 0.4s ease; }
.progress-activity-track.progress-activity-indeterminate .progress-activity-fill { width: 40%; animation: progress-activity-slide 1.2s ease-in-out infinite; }
@keyframes progress-activity-slide { 0% { transform: translateX(-110%); } 100% { transform: translateX(260%); } }

/* Verbose progress log panel */
.progress-log {
    display: none;
    margin-top: 1.25rem;
    background: rgba(0,0,0,0.35);
    border: 1px solid rgba(255,255,255,0.06);
    border-radius: 8px;
    padding: 10px 12px;
    max-height: 220px;
    overflow-y: auto;
    font-family: 'JetBrains Mono', monospace;
    font-size: 0.7rem;
    line-height: 1.7;
    scrollbar-width: thin;
    scrollbar-color: rgba(139,92,246,0.4) transparent;
}
.progress-log::-webkit-scrollbar { width: 4px; }
.progress-log::-webkit-scrollbar-thumb { background: rgba(139,92,246,0.4); border-radius: 2px; }
.log-line { display: flex; gap: 8px; align-items: baseline; }
.log-ts { color: rgba(255,255,255,0.55); flex-shrink: 0; font-size: 0.62rem; }
.log-icon { flex-shrink: 0; width: 12px; text-align: center; }
.log-text { word-break: break-word; overflow-wrap: anywhere; flex: 1; }

/* --- Clip Cards --- */
.clip-card {
    background: var(--bg-card); border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md); overflow: hidden;
    cursor: pointer; transition: all 0.3s var(--ease);
}
/* audit53 UX-08: the hover also gains the background tint the old
   duplicate rule (removed below) used to add — lift AND tint, one rule. */
.clip-card:hover { border-color: var(--accent-purple); background: rgba(124, 58, 237, 0.04); box-shadow: var(--shadow-glow-sm); transform: translateY(-2px); }

/* --- Modal --- */
.modal-overlay {
    position: fixed; inset: 0; z-index: 200;
    background: rgba(0,0,0,0.75); backdrop-filter: blur(12px);
    display: flex; align-items: center; justify-content: center; padding: 2rem;
    animation: fadeIn 0.2s ease;
}
.modal-content {
    background: var(--bg-secondary); border: 1px solid var(--border-subtle);
    border-radius: var(--radius-xl); max-width: 1000px; width: 100%;
    max-height: 90vh; max-height: 90dvh; /* audit53 R10-08 */
    overflow-y: auto;
    box-shadow: var(--shadow-md), 0 0 60px rgba(139,92,246,0.08);
    animation: modalSlide 0.35s var(--ease-spring);
}
@keyframes modalSlide { from{opacity:0;transform:translateY(16px) scale(0.97)} to{opacity:1;transform:translateY(0) scale(1)} }
.modal-header { display: flex; align-items: center; justify-content: space-between; padding: 1.5rem; border-bottom: 1px solid var(--border-subtle); }
.modal-header h2 { font-size: 1.15rem; font-weight: 700; }
.modal-close {
    width: 34px; height: 34px; border-radius: 8px;
    display: flex; align-items: center; justify-content: center;
    color: var(--text-muted); transition: all 0.2s var(--ease);
    font-size: 1.1rem;
}
.modal-close:hover { background: var(--bg-card-hover); color: var(--text-primary); }
.modal-body { padding: 1.5rem; }

/* --- Access Key Gate (in-app sign-in; above every other overlay) --- */
.apikey-gate-overlay { z-index: 1000; }
.apikey-gate-content { max-width: 430px; }
.apikey-gate-error {
    margin-top: 0.85rem; padding: 0.65rem 0.85rem;
    background: rgba(248,113,113,0.12); border: 1px solid rgba(248,113,113,0.35);
    border-radius: var(--radius-md); color: var(--accent-red); font-size: 0.85rem;
}
.apikey-gate-remember-row {
    display: flex; gap: 0.6rem; align-items: flex-start;
    margin-top: 1.1rem; cursor: pointer; font-size: 0.9rem;
}
.apikey-gate-remember-row input[type="checkbox"] {
    margin-top: 3px; width: 17px; height: 17px; flex-shrink: 0;
    accent-color: var(--accent-purple);
}
.clip-editor { display: grid; grid-template-columns: 1fr 1fr; gap: 1.5rem; }
/* audit53 R10-01 (CONFIRMED by static geometry): the preview box is
   aspect-ratio-sized with overflow:hidden, and the <video> was
   height:100% — so the in-flow .clip-editor-transport row after it
   STARTED at y = 100% of the box (plus its 6px margin) and was
   clipped away at every viewport (max-height clamping only made it
   worse). The box is now a flex column: the video takes the
   remaining height (object-fit:contain keeps the picture itself
   true 9:16, letterboxed) and the transport row gets its own
   in-flow band at the bottom of the box — always visible. */
/* v2.13.0 (UX2-08): the dead position:sticky is gone (the markup's
   inline position:relative always overrode it), and the video now
   lives in .clip-editor-stage — the caption mock and the error
   overlay are positioned against the STAGE (the video area only),
   so neither can paint over the transport band below it. */
.clip-editor-preview { aspect-ratio: 9/16; max-height: 500px; border-radius: var(--radius-md); overflow: hidden; background: #000; display: flex; flex-direction: column; }
.clip-editor-stage { position: relative; flex: 1 1 auto; min-height: 0; display: flex; }
.clip-editor-preview video { width: 100%; height: auto; flex: 1 1 auto; min-height: 0; object-fit: contain; }
.clip-editor-preview #clip-preview-placeholder { height: auto !important; flex: 1 1 auto; min-height: 0; }
.clip-editor-transport { flex: 0 0 auto; }
.clip-editor-controls { display: flex; flex-direction: column; gap: 1.25rem; }
.control-group {
    padding: 1rem; background: var(--bg-card); border-radius: var(--radius-md);
    border: 1px solid var(--border-subtle);
}
.control-group h3 {
    font-size: 0.78rem; font-weight: 700; color: var(--text-muted);
    text-transform: uppercase; letter-spacing: 0.6px; margin-bottom: 0.75rem;
}

/* --- Sliders --- */
.slider-group { display: flex; flex-direction: column; gap: 6px; margin-bottom: 0.75rem; }
.slider-header { display: flex; justify-content: space-between; font-size: 0.78rem; }
.slider-label { color: var(--text-secondary); }
.slider-value { color: var(--accent-cyan); font-weight: 700; font-family: var(--font-mono); font-size: 0.75rem; }
input[type="range"] { -webkit-appearance: none; width: 100%; height: 4px; border-radius: 2px; background: var(--bg-tertiary); outline: none; }
input[type="range"]::-webkit-slider-thumb {
    /* audit53 R10-05: 24px thumb (was 16px) — the WCAG 2.5.8 AA
       target-size floor for the app's many slider controls. */
    -webkit-appearance: none; width: 24px; height: 24px; border-radius: 50%;
    background: var(--gradient-primary); cursor: pointer;
    box-shadow: 0 2px 10px rgba(139,92,246,0.4);
}
/* audit53 R10-05: Firefox parity — the WebKit thumb/track rules
   above never applied there, so FF fell back to the UA's small
   thumb and a differently-styled track. */
input[type="range"]::-moz-range-thumb {
    width: 24px; height: 24px; border: none; border-radius: 50%;
    background: var(--gradient-primary); cursor: pointer;
    box-shadow: 0 2px 10px rgba(139,92,246,0.4);
}
input[type="range"]::-moz-range-track {
    width: 100%; height: 4px; border-radius: 2px; background: var(--bg-tertiary);
}
/* audit53 R10-05: one checkbox size floor (18px). Class rules with
   their own sizes (.clip-select-cb 20px, .playlist-card-cb 22px,
   the gate remember box 17px is a deliberate compact exception)
   outrank this element rule where they exist. */
input[type="checkbox"] { width: 18px; height: 18px; accent-color: var(--accent-purple); }
/* audit53 R10-05: coarse pointers get real touch targets — small
   buttons grow to 40px, standard buttons and modal close to 44px. */
@media (pointer: coarse) {
    .btn-sm { min-height: 40px; }
    .btn { min-height: 44px; }
    .modal-close { width: 44px; height: 44px; }
    /* v2.13.0 (UX2-11): the controls the first pass missed join
       the floors — banner dismiss + transcript close + the two
       bespoke speed selects to 40px; the "Why this clip" summary
       becomes a real row; the selection checkboxes reach the
       WCAG 2.5.8 24px AA minimum (element+class selectors so they
       outrank the later base rules for these classes). */
    .firstrun-banner-close { min-width: 40px; min-height: 40px; }
    .te-close-btn { min-width: 40px; min-height: 40px; }
    #clip-playback-speed { min-height: 40px; }
    #te-speed { min-height: 40px; }
    .clip-card .clip-why > summary { min-height: 40px; display: flex; align-items: center; }
    input.clip-select-cb { width: 24px; height: 24px; }
    input.playlist-card-cb { width: 24px; height: 24px; }
}
/* C6-41 / C7-53: explicit focus outlines for range / checkbox / colour
   inputs. Cascade note: the global :focus-visible rule near the top of
   this file is specificity (0,1,0) and LOSES to the outline:none on
   input[type="range"] above (0,1,1); the later input:focus-visible list
   rule ties (0,1,1) and wins for range only by source order. These
   (0,2,1) rules make the outline explicit and order-independent. */
input[type="range"]:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 4px; }
input[type="checkbox"]:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }
input[type="color"]:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }

/* --- Toast --- */
.toast-container { position: fixed; bottom: 1.5rem; bottom: calc(1.5rem + env(safe-area-inset-bottom)); /* audit53 R10-09 */ right: 1.5rem; z-index: 400; display: flex; flex-direction: column; gap: 8px; max-height: calc(100vh - 3rem); max-height: calc(100dvh - 3rem); /* audit53 R10-08 */ overflow: hidden; pointer-events: none; }
.toast {
    padding: 12px 20px; border-radius: var(--radius-sm);
    font-size: 0.84rem; font-weight: 500;
    backdrop-filter: blur(16px); animation: toastSlide 0.35s var(--ease-spring);
    max-width: min(380px, calc(100vw - 2rem)); transition: opacity 0.3s; pointer-events: auto; overflow-wrap: anywhere;
}
.toast-success { background: rgba(52,211,153,0.12); border: 1px solid rgba(52,211,153,0.2); color: var(--accent-green); }
.toast-error { background: rgba(248,113,113,0.12); border: 1px solid rgba(248,113,113,0.2); color: var(--accent-red); }
.toast-info { background: rgba(139,92,246,0.12); border: 1px solid rgba(139,92,246,0.2); color: var(--accent-purple-light); } /* v2.13.0 (UX2-09): was 4.32:1, now 9.90:1 */
/* audit53 UX-11: showToast has emitted a 'warning' type (with a
   "⚠ Warning:" label) since long ago, but no .toast-warning rule
   existed — warning toasts rendered as unstyled default text. */
.toast-warning { background: rgba(251,191,36,0.12); border: 1px solid rgba(251,191,36,0.2); color: var(--accent-orange); }
@keyframes toastSlide { from{opacity:0;transform:translateX(20px) scale(0.95)} to{opacity:1;transform:translateX(0) scale(1)} }

/* --- Settings --- */


/* --- Empty State --- */
.empty-state { text-align: center; padding: 5rem 2rem; color: var(--text-muted); }
.empty-state h3 { font-size: 1.15rem; color: var(--text-secondary); margin-bottom: 0.5rem; font-weight: 600; }
.empty-state p { font-size: 0.9rem; }

/* --- Spinner --- */
.spinner { width: 22px; height: 22px; border: 2.5px solid var(--border-subtle); border-top-color: var(--accent-purple); border-radius: 50%; animation: spin 0.7s linear infinite; }

/* --- Section Headers --- */
.section-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 1.5rem; }
.section-title { font-size: 1.6rem; font-weight: 800; letter-spacing: -0.5px; }

/* =============================================================================
   audit53 W5a — phase indicator (UX-03), current-panel marker
   (UX-21), action-menu disclosure (UX-19), sticky Settings save
   bar (UX-04), Library skeleton (UX-23), stretched card link
   (A11Y-03), clip "why" disclosure (UX-02), static ambient (UX-22)
   ============================================================================= */

/* UX-03: the video page's phase stepper — text labels, never
   colour alone: done steps are green AND bold-free with a ✓ in the
   label text itself (the markup carries it), the current step is
   bold, todo steps are muted. */
.phase-indicator { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-top: 12px; font-size: 0.8rem; }
.phase-step { color: var(--text-muted); }
.phase-step.done { color: var(--accent-green); }
.phase-step.current { color: var(--text-primary); font-weight: 700; }
.phase-step.active { color: var(--accent-purple-light); font-weight: 600; } /* v2.13.0 (UX2-15): was 4.44:1, now ≥10:1 */
.phase-sep { color: var(--text-muted); }

/* UX-21: the panel the user is currently working in gets a visible
   ring so long pages have an obvious "you are here". */
.phase-current { border-color: rgba(139,92,246,0.45) !important; box-shadow: var(--shadow-glow-sm); }

/* UX-19: the clip action bar's secondary text tools live behind
   one disclosure; the panel floats above the sticky bar. */
.action-menu { position: relative; }
.action-menu > summary { list-style: none; }
.action-menu > summary::-webkit-details-marker { display: none; }
.action-menu-panel {
    position: absolute; bottom: calc(100% + 6px); left: 0; z-index: 30;
    display: flex; flex-direction: column; gap: 6px;
    background: var(--bg-secondary); border: 1px solid var(--border-subtle);
    border-radius: 10px; padding: 8px; min-width: 230px;
    box-shadow: var(--shadow-md);
}
.action-menu-panel .btn { justify-content: flex-start; }

/* UX-02: reason + scores collapse behind one summary line. */
.clip-why > summary { cursor: pointer; font-size: 0.75rem; color: var(--text-muted); margin-bottom: 6px; }

/* UX-04: sticky Settings save bar — appears only while the form is
   dirty (settings.js toggles [hidden]); mirrors the main Save
   button so a long page never strands unsaved edits. */
.settings-savebar {
    position: sticky; bottom: 0; z-index: 10;
    display: flex; align-items: center; gap: 12px;
    padding: 12px 16px; margin-top: 1rem;
    /* v2.13.0 (UX2-13): keep the Save button out of the home-
       indicator gesture zone on notched phones (the sibling
       .action-bar got this in R10-09; this bar was missed). */
    padding-bottom: calc(12px + env(safe-area-inset-bottom));
    background: rgba(7,7,13,0.95); backdrop-filter: blur(16px);
    border-top: 1px solid var(--border-subtle);
    font-size: 0.85rem; color: var(--text-secondary);
}
.settings-savebar[hidden] { display: none; }

/* UX-23: Library first-paint skeleton cards (shimmer killed by the
   global reduced-motion rule like every other animation). */
.skeleton-card {
    aspect-ratio: 4 / 3; border-radius: var(--radius-lg);
    background: linear-gradient(100deg, var(--bg-tertiary) 40%, rgba(255,255,255,0.06) 50%, var(--bg-tertiary) 60%);
    background-size: 200% 100%;
    animation: skeletonShimmer 1.4s ease-in-out infinite;
}
@keyframes skeletonShimmer { from { background-position: 120% 0; } to { background-position: -80% 0; } }

/* A11Y-03: the Library card's title is a real link stretched over
   the whole card; the actions row lifts above the overlay so its
   buttons stay clickable (it carries position:relative inline in
   the markup). */
.video-card .card-title-link { color: inherit; text-decoration: none; }
.video-card .card-title-link::after { content: ''; position: absolute; inset: 0; }

/* UX-22: long reading/working pages (video detail, Settings) get a
   static ambient background — app.js toggles the class per route. */
body.static-ambient .ambient-orb { animation: none; }

/* --- Responsive --- */
/* audit53 UX-13: the breakpoint set is 1100 (tablet tier) / 768
   (stack) / 700 (clip-editor transport) / 600 (phone) — one block
   per width, merged below; do not add a second block for a width
   that already has one. */
@media (max-width: 768px) {
    .nav-inner { padding: 0 1rem; }
    .nav-link span { display: none; }
    .main-content { padding: 72px 1rem 2rem; }
    .hero-title { font-size: 2rem; }
    .url-input-wrapper { flex-direction: column; padding: 8px; }
    .clip-editor { grid-template-columns: 1fr; }
    .video-grid { grid-template-columns: 1fr; }
    /* C7-37: on small screens the stacked editor preview must not fill
       the first viewport, and the modal overlay gives up its 2rem
       padding so the editor column keeps usable width. */
    .clip-editor-preview { max-height: 38vh; }
    .modal-overlay { padding: 0.75rem; }
}

/* audit53 UX-13: tablet tier — the range between the phone stack
   and full desktop: the editor keeps two columns but the 9:16
   preview is capped lower so the controls column stays reachable,
   and Settings rows breathe at one card per row band edge. */
@media (min-width: 769px) and (max-width: 1100px) {
    .clip-editor-preview { max-height: 420px; }
    .clip-editor { gap: 1rem; }
}

@media (max-width: 600px) {
    .modal-content {
        max-width: 95vw;
        max-height: 90vh;
        max-height: 90dvh; /* audit53 R10-08 */
        /* audit53 R10-11: no padding here — the old 16px stacked on
           top of .modal-header/.modal-body's own padding, spending
           80px of a 320px screen on double inset. The header/body
           shrink below replaces it. */
    }
    .modal-header, .modal-body { padding: 1rem; }
    .action-bar { flex-direction: column; flex-wrap: wrap; max-height: 40vh; overflow-y: auto; }
    /* C8-M-56: the forced 2-col made ~142px cards at 360px — one
       readable column at phone widths instead. */
    .playlist-grid { grid-template-columns: 1fr; }
    /* audit53 UX-13 merge: the navbar-wrap rules below used to live
       in a SECOND max-width:600px block ~450 lines later, and the
       render-setup / video-header rules in a THIRD. Every base rule
       they override precedes this block, so the merge changes no
       computed style — it only removes the "which block wins?"
       trap for the next editor. */
    /* SB-6: #live-status is an inline-styled nowrap pill inside .nav-inner;
       constrain it (max-width + ellipsis) and let the bar wrap instead of
       clipping the Settings link on ~360px viewports. font-size/padding
       need !important to beat the pill's inline style at this size only. */
    .nav-inner { flex-wrap: wrap; height: auto; min-height: 60px; }
    /* audit53 R10-02: the wrapped two-row navbar stands ~90–100px
       tall, but .main-content kept the one-row offset (72px from
       the ≤768 block) — page tops slid under the fixed bar. 104px
       clears the wrapped bar with the same breathing room the
       desktop 80px offset gives the 60px bar. */
    .main-content { padding-top: 104px; }
    #live-status {
        max-width: 38vw; overflow: hidden; text-overflow: ellipsis;
        font-size: 0.65rem !important; padding: 2px 8px !important;
    }
    /* C10-57: progress stepper shrinks instead of overflowing. */
    .progress-stages { gap: 4px; }
    .progress-dot { width: 28px; height: 28px; font-size: 0.65rem; }
    /* audit53 R10-10: C10-57 solved the overflow by shrinking the
       phase labels to 0.58rem (≈9.3px) — below legibility for the
       one thing a phone user opens the page to check. Floor the
       labels at 0.68rem (11px) and let them wrap to two lines
       instead of shrinking. */
    .progress-label { font-size: 0.68rem; max-width: 64px; line-height: 1.25; }
    #render-setup-modal div[style*="justify-content:flex-end"] { flex-wrap: wrap; }
    /* R10-06: the video-detail header row (120px flex-shrink:0
       thumbnail + text column + delete button) never stacks, so at
       360px the title column is squeezed to ~96px. The header div
       gains .video-detail-head in video-list.js (routed with
       R10-03's classes); these are the rules it activates. The
       thumbnail's 120px width is inline, hence the one deliberate
       !important. */
    .video-detail-head { flex-wrap: wrap; }
    .video-detail-head > img { width: 80px !important; }
}

/* audit53 R10-12: short landscape screens (a phone on its side):
   the editor stacks so the preview is not squeezed beside the
   controls, the fullscreen player leaves room for its own control
   row (its video carries an inline 85dvh cap — hence !important),
   and the player controls wrap instead of overflowing. */
@media (orientation: landscape) and (max-height: 500px) {
    .clip-editor { grid-template-columns: 1fr; }
    .video-player-modal video { max-height: 78dvh !important; }
    .fp-controls { flex-wrap: wrap; }
}

/* =============================================================================
   New: Clip Selector & 3-Phase UI
   ============================================================================= */

/* Clip selection checkbox */
.clip-select-cb {
    width: 20px;
    height: 20px;
    accent-color: var(--accent-purple);
    cursor: pointer;
    flex-shrink: 0;
    margin-top: 2px;
}
.clip-select-cb:disabled { opacity: 0.5; cursor: not-allowed; }

/* Sort buttons */
.sort-btn {
    font-size: 0.75rem;
    padding: 4px 10px;
    border-radius: 20px;
    cursor: pointer;
    transition: all 0.2s;
}

/* Floating action bar */
.action-bar {
    position: sticky;
    bottom: 0;
    background: rgba(7, 7, 13, 0.95);
    backdrop-filter: blur(16px);
    padding: 12px 16px;
    padding-bottom: calc(12px + env(safe-area-inset-bottom)); /* audit53 R10-09: keep the sticky bar's buttons out of the home-indicator zone */
    border-top: 1px solid var(--border-subtle);
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
    z-index: 10;
    border-radius: 12px 12px 0 0;
    box-shadow: 0 -4px 20px rgba(0,0,0,0.5);
}

/* Playlist grid */
.playlist-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
    gap: 12px;
    margin-top: 12px;
}

.playlist-card {
    background: var(--bg-secondary);
    border: 1px solid var(--border-subtle);
    border-radius: 10px;
    overflow: hidden;
    cursor: pointer;
    transition: all 0.2s;
    position: relative;
}
.playlist-card:hover {
    border-color: var(--accent-purple);
    transform: translateY(-2px);
    box-shadow: 0 4px 12px rgba(0,0,0,0.3);
}
/* C8-M-36: selection must read on the whole card, not only the 22px
   checkbox (the JS toggles .selected with the checkbox state). */
.playlist-card.selected {
    border-color: var(--accent-purple);
    background: rgba(139,92,246,0.08);
    box-shadow: 0 0 0 2px rgba(139,92,246,0.35);
}
/* C8-M-37: keyboard focus on a card is visible (cards are tabindex=0). */
.playlist-card:focus-visible {
    outline: 2px solid var(--accent-purple);
    outline-offset: 2px;
}
.playlist-card img { width: 100%; aspect-ratio: 16/9; object-fit: cover; }
.playlist-card-body { padding: 10px; }
.playlist-card-title {
    font-size: 0.8rem; font-weight: 600; line-height: 1.3; margin-bottom: 4px;
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
    unicode-bidi: plaintext;
}
.playlist-card-cb {
    position: absolute; top: 8px; left: 8px;
    width: 22px; height: 22px;
    accent-color: var(--accent-purple); cursor: pointer;
}

/* (The duplicate "better clip card" rules that used to sit here were
   merged into the single clip-card rule above — audit53 UX-08.) */

/* Video grid for library */
.video-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
    gap: 16px;
    margin-top: 1.5rem;
}

/* (.video-card lives in the single merged rule near the top of this
   file — audit53 UX-08.) */

/* Badge styles */
.badge {
    font-size: 0.7rem;
    padding: 2px 8px;
    border-radius: 20px;
    font-weight: 600;
    display: inline-flex;
    align-items: center;
    gap: 3px;
    text-transform: uppercase;
    letter-spacing: 0.5px;
}
.badge-success { background: rgba(52, 211, 153, 0.15); color: var(--accent-green); }
.badge-error { background: rgba(248, 113, 113, 0.15); color: var(--accent-red); }
/* v2.13.0 (UX2-04): text is the light token (8.41:1 on the base
   tint) and the pulse animates the TINT, not the opacity — the old
   opacity pulse blended text toward the card at 50% (≈2.24:1).
   Worst point is now the tint peak (0.30 alpha): 6.84:1. */
.badge-processing { background: rgba(139, 92, 246, 0.15); color: var(--accent-purple-light); animation: pulse-badge 2s ease-in-out infinite; }
.badge-suggested { background: rgba(251, 191, 36, 0.15); color: var(--accent-orange); }
/* v2.15.0 (DR-L14): the library "⏳ Ready to render" badge has its
   own amber class — it previously reused the green .badge-success,
   whose colour contradicted the message (analysis done, nothing
   rendered yet). Same amber pairing as .badge-suggested:
   --accent-orange (#FBBF24) text on the 0.15 amber tint computes
   to 7.87:1 composited over the glass-card surface (worst case:
   the lightest --gradient-glass stop over --bg-primary) — above
   the 4.5:1 AA floor. */
.badge-ready { background: rgba(251, 191, 36, 0.15); color: var(--accent-orange); }

@keyframes pulse-badge {
    0%, 100% { background: rgba(139, 92, 246, 0.15); }
    50% { background: rgba(139, 92, 246, 0.30); }
}

/* Mini progress bar for library cards */
.mini-progress {
    margin-top: 8px;
    height: 3px;
    background: var(--border-subtle);
    border-radius: 2px;
    overflow: hidden;
}
.mini-progress-bar {
    height: 100%;
    width: 40%;
    background: var(--gradient-primary);
    border-radius: 2px;
    animation: mini-progress-anim 2s ease-in-out infinite;
}
@keyframes mini-progress-anim {
    0% { width: 10%; margin-left: 0; }
    50% { width: 40%; margin-left: 30%; }
    100% { width: 10%; margin-left: 90%; }
}

/* Settings grid override for analysis panel */
.settings-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
    gap: 12px;
}

/* =============================================================================
   Fix: Select/Dropdown dark theme visibility
   ============================================================================= */
/* audit53 UX-08 + R10-04 + A11Y-04: the !important armour is gone.
   For a select.input element this rule's specificity is (0,1,1),
   which already beats .input (0,1,0) — including its background
   shorthand — so the dark fill and chevron survive without
   !important, and future contextual select styling is possible
   again. font-size is 1rem: at 0.85rem (13.6px) EVERY select fell
   under iOS Safari's 16px focus-zoom threshold, defeating the
   protection .input deliberately carries. Border uses the A11Y-04
   input token (≥3:1). The two video-overlay speed selects
   (.fp-speed / #te-speed) carry full inline styling of their own
   and now simply keep it, as they did before this block existed. */
select, select.input {
    background: var(--bg-tertiary);
    color: var(--text-primary);
    border: 1px solid var(--border-input);
    border-radius: 8px;
    padding: 10px 14px;
    font-size: 1rem;
    width: 100%;
    appearance: none;
    -webkit-appearance: none;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%23a0a0b8' stroke-width='2'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 12px center;
    cursor: pointer;
    transition: all 0.2s;
}
select:focus, select.input:focus {
    outline: none;
    border-color: var(--accent-purple);
    box-shadow: 0 0 0 3px rgba(139, 92, 246, 0.15);
}
select option {
    background: #1a1a2e;
    color: #f0f0f8;
    padding: 8px;
}

/* =============================================================================
   Fix: Copy button visibility in clip cards
   audit53 UX-08 (completed in Wave 3a): the selector is class-based
   (.social-copy-btn, emitted by the copy button in video-list.js)
   instead of the old [onclick*=] coupling to JS wiring.
   ============================================================================= */
.clip-card .btn.btn-sm.social-copy-btn,
.clip-card div[style*="bg-tertiary"] .btn {
    background: rgba(139, 92, 246, 0.2) !important;
    /* v2.13.0 (UX2-05): light text token — was 3.44:1 base /
       2.54:1 hover with --accent-purple; now 7.87:1 / 5.85:1. */
    color: var(--accent-purple-light) !important;
    border: 1px solid rgba(139, 92, 246, 0.3);
    font-weight: 600;
}
.clip-card div[style*="bg-tertiary"] .btn:hover {
    background: rgba(139, 92, 246, 0.4) !important;
}

/* Video modal player */
/* v2.3.0 (R8-07): column direction — playClipFullscreen appends the
   <video>, the .fp-controls row and the error box as siblings; in
   the default row direction the controls sat BESIDE the video
   instead of under it. */
.video-player-modal {
    position: fixed;
    inset: 0;
    background: rgba(0,0,0,0.9);
    z-index: 200;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    cursor: pointer;
}
.video-player-modal video {
    max-width: 90vw;
    max-height: 85vh;
    max-height: 85dvh; /* audit53 R10-08 */
    border-radius: 12px;
    box-shadow: 0 0 60px rgba(0,0,0,0.5);
}
.video-player-close {
    position: fixed;
    top: 20px;
    top: calc(20px + env(safe-area-inset-top)); /* audit53 R10-09 */
    right: 24px;
    right: calc(24px + env(safe-area-inset-right)); /* audit53 R10-09 */
    color: white;
    font-size: 2rem;
    cursor: pointer;
    z-index: 201;
    background: rgba(255,255,255,0.1);
    width: 44px;
    height: 44px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: background 0.2s;
}
.video-player-close:hover {
    background: rgba(255,255,255,0.2);
}

/* =============================================================================
   Responsive: Subtitle editor (the clip editor's caption layout)
   R8-10 (v2.8.0): scoped to the clip editor's own content element —
   the old `.modal-content > …` form matched EVERY modal carrying
   that class (the API-key gate included, plus any future modal),
   force-stacking header/action rows that were never meant to stack
   on phone-width viewports.
   ============================================================================= */
@media (max-width: 700px) {
    /* audit53 R10-07: the two selectors this block used to carry
       were dead — no markup anywhere contains "width:300px", and
       the clip editor's direct children under #clip-modal-content
       are .modal-header / .modal-body, neither with an inline
       display:flex — so the phone layout it promised did not exist.
       Repointed at the editor's one live flex row (the transport
       controls), keeping the #clip-modal-content scoping the R8-10
       note above exists to protect: the row wraps on narrow screens
       instead of squeezing. */
    #clip-modal-content .clip-editor-transport {
        flex-wrap: wrap;
    }
}

/* === PREMIUM SETTINGS PAGE === */
.settings-page { max-width: 900px; margin: 0 auto; }
.settings-page .section-title {
    font-size: 1.6rem;
    /* audit53 UX-06: the brand gradient token, not a one-off pair. */
    background: var(--gradient-primary);
    -webkit-background-clip: text;
    -webkit-text-fill-color: transparent;
}
.settings-section { margin-bottom: 2rem; }
.settings-section-title {
    font-size: 0.82rem; font-weight: 700;
    text-transform: uppercase; letter-spacing: 0.08em;
    color: var(--text-muted); margin-bottom: 1rem; padding-left: 2px;
}
.settings-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(360px, 100%), 1fr)); gap: 1rem; }
.setting-card {
    background: var(--bg-secondary);
    border: 1px solid var(--border-subtle);
    border-radius: 14px;
    padding: 1.25rem;
    transition: border-color 0.2s, box-shadow 0.2s;
    position: relative;
    overflow: hidden;
}
.setting-card::before {
    content: '';
    position: absolute;
    top: 0; left: 0; right: 0;
    height: 3px;
    border-radius: 14px 14px 0 0;
}
.setting-card:hover { border-color: rgba(139,92,246,0.3); box-shadow: 0 4px 20px rgba(0,0,0,0.15); }
.setting-card.accent-cyan::before { background: linear-gradient(90deg, var(--accent-cyan), #0ea5e9); }
.setting-card.accent-purple::before { background: linear-gradient(90deg, #8b5cf6, #a78bfa); }
.setting-card.accent-blue::before { background: linear-gradient(90deg, #3b82f6, #60a5fa); }
.setting-card.accent-green::before { background: linear-gradient(90deg, #34d399, #10b981); }
.setting-card.accent-orange::before { background: linear-gradient(90deg, #f59e0b, #fb923c); }
.setting-card-header { display: flex; align-items: center; gap: 12px; margin-bottom: 1rem; }
.setting-card-icon {
    width: 40px; height: 40px;
    border-radius: 10px;
    display: flex; align-items: center; justify-content: center;
    font-size: 1.15rem; flex-shrink: 0;
}
.setting-card-title { font-size: 0.95rem; font-weight: 700; margin: 0; }
.setting-card-desc { font-size: 0.8rem; color: var(--text-muted); margin-top: 2px; } /* audit53 UX-07: 0.72rem was below the type-scale floor */
/* audit53 UX-07: the one class for Settings instructional copy —
   replaces the repeated inline font-size:0.68rem paragraphs. */
.settings-help { font-size: 0.8rem; color: var(--text-muted); line-height: 1.5; }
.key-badge {
    display: inline-flex; align-items: center; gap: 4px;
    padding: 3px 10px; border-radius: 20px;
    font-size: 0.7rem; font-weight: 700;
}
.key-badge.cyan { background: rgba(0,206,209,0.12); color: var(--accent-cyan); }
.key-badge.purple { background: rgba(139,92,246,0.12); color: #a78bfa; }
.health-row {
    display: flex; align-items: center; gap: 10px;
    padding: 8px 0;
    border-bottom: 1px solid rgba(255,255,255,0.04);
    font-size: 0.85rem;
}
.health-row:last-child { border-bottom: none; }
.health-dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; }
.health-dot.ok { background: #34d399; box-shadow: 0 0 6px rgba(52,211,153,0.5); }
.health-dot.warn { background: #f59e0b; box-shadow: 0 0 6px rgba(245,158,11,0.5); }
.health-dot.err { background: #ef4444; box-shadow: 0 0 6px rgba(239,68,68,0.5); }
.save-settings-btn {
    width: 100%; padding: 14px; font-size: 1rem; font-weight: 700;
    /* audit53 A11Y-01 (same defect pattern): white on the old ends
       computed to 4.23:1 (#8b5cf6) and 4.47:1 (#6366f1) — both
       under 4.5:1. These ends compute to 4.69:1 and 4.67:1. */
    background: linear-gradient(135deg, #8355ec, #6063ec);
    border: none; border-radius: 12px; color: #fff;
    cursor: pointer; transition: opacity 0.2s, transform 0.1s;
    box-shadow: 0 4px 15px rgba(139,92,246,0.3);
}
.save-settings-btn:hover { opacity: 0.9; transform: translateY(-1px); }
.save-settings-btn:disabled { opacity: 0.5; cursor: not-allowed; transform: none; }

/* Keyboard focus visibility: buttons, links, chips and disclosure
   summaries get a clear outline when reached by keyboard — mouse
   styling is unchanged (:focus-visible only matches keyboard focus). */
button:focus-visible, a:focus-visible, summary:focus-visible,
input:focus-visible, select:focus-visible, textarea:focus-visible,
.btn:focus-visible, .filter-btn:focus-visible, .sort-btn:focus-visible,
.nav-link:focus-visible {
    outline: 2px solid var(--accent-purple);
    outline-offset: 2px;
}

@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation: none !important;
        transition: none !important;
        scroll-behavior: auto !important;
    }
}

/* =============================================================================
   W5D (D-W7): form groups, text wrapping/clamp utilities, small-screen guards
   ============================================================================= */

/* C10-26 / C7-72: .form-group was used ~18x (render-setup + Settings) with
   zero CSS rules; every usage is label-above-control (verified in app.js +
   video-list.js templates — no horizontal usage), so a column layout is
   safe. .input inside a form group spans the card instead of rendering at
   intrinsic width. */
.form-group { display: flex; flex-direction: column; gap: 6px; margin-bottom: 12px; }
.form-group label { font-size: 0.8rem; font-weight: 600; color: var(--text-secondary); }
.form-group .input { width: 100%; }

/* C10-35: reusable 2-line clamp utility (none existed). Long unbroken
   strings on log/progress/error surfaces get overflow-wrap:anywhere via
   .log-text / .progress-message / .toast / .glass-card above. */
.text-clamp-2 {
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
    overflow: hidden; overflow-wrap: anywhere; unicode-bidi: plaintext;
}

/* C10-36 (leader reconciliation): the clip-card markup carries clamp-2 /
   clamp-3 classes (Squad-B pass); define them so they are real rules,
   not dangling names. The card description additionally keeps its
   inline -webkit-line-clamp:2 from that pass, which wins over .clamp-3. */
.clamp-2 {
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
    overflow: hidden;
}
.clamp-3 {
    display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical;
    overflow: hidden;
}

/* C6-57 (CSS half): render-setup clip-block summaries clamp to one line
   instead of wrapping unbounded (markup title attr is D-W3's half). */
.rs-clip-block summary {
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
    overflow-wrap: anywhere;
}

/* C10-76: social-text previews are inline-styled max-height:60px +
   overflow:hidden divs (id="social-text-<id>", no class hook — the old
   .social-text-preview class no longer exists); fade the clipped bottom
   edge via CSS only. */
div[id^="social-text-"] {
    -webkit-mask-image: linear-gradient(to bottom, #000 55%, transparent 100%);
    mask-image: linear-gradient(to bottom, #000 55%, transparent 100%);
}

/* C10-79: thumbnail placeholder — a failed/hidden <img> (onerror sets
   display:none) must not leave a transparent hole; the classed wrappers
   and card images keep the tertiary placeholder background.
   (.clip-thumb-container already carried background:var(--bg-tertiary).) */
.video-card img, .playlist-card img { background: var(--bg-tertiary); }

/* (the max-width:600px rules that used to sit here moved into the
   single merged 600px block in the Responsive section — UX-13.) */

/* =============================================================================
   audit53 W1c — first-run banner (UX-05), Render Setup row wrap
   (R10-03), video-detail header stack (R10-06)
   ============================================================================= */

/* UX-05: shown by video-form.js above the Home form ONLY when no
   usable Groq/Gemini keys are configured; dismissible, non-blocking,
   and absent entirely once keys exist. */
/* v2.13.0 (UX2-12): the slot is a zero-height anchor and the
   banner OVERLAYS the hero's subtitle area — it used to be
   injected in-flow when the async probe resolved, pushing the
   URL form down mid-reach. It now never moves layout; the area
   it covers is non-interactive text, and it dismisses. */
#firstrun-key-banner { position: relative; height: 0; }
.firstrun-banner {
    position: absolute; left: 0; right: 0; bottom: 10px; z-index: 40;
    display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
    max-width: 680px; margin: 0 auto; text-align: left;
    padding: 12px 16px; border-radius: var(--radius-md);
    background: rgba(43, 33, 8, 0.97);
    border: 1px solid rgba(251,191,36,0.35);
    box-shadow: 0 10px 32px rgba(0,0,0,0.5);
    font-size: 0.9rem; color: var(--text-primary);
}
.firstrun-banner-text { flex: 1; min-width: 220px; }
.firstrun-banner-close {
    color: var(--text-muted); font-size: 1rem; line-height: 1;
    padding: 6px 8px; border-radius: 6px;
}
.firstrun-banner-close:hover { color: var(--text-primary); background: rgba(255,255,255,0.06); }

/* R10-03: the Render Setup AI-Director row and sticky footer row
   are inline-styled display:flex with no wrap; at phone widths
   their ~400px of buttons overflow the ~300px panel and push the
   primary "Render N clips" button out. The named classes are the
   durable hook — video-list.js adds them to the two rows (routed
   to the parent programme; the markup is outside this wave's file
   scope). Until that lands, the scoped bridge rule below reaches
   the same two rows inside #render-setup-modal by their inline
   justify-content (flex-wrap is never set inline, so no
   !important is needed and desktop layout is untouched — the
   bridge sits inside the ≤600px query with the classes' stacking
   companions). */
.rs-actions-row { flex-wrap: wrap; }
.rs-footer-row { flex-wrap: wrap; }

/* (this block's max-width:600px rules moved into the single merged
   600px block in the Responsive section — audit53 UX-13.) */
