* { margin: 0; padding: 0; box-sizing: border-box; }

:root {
    --sidebar-width: 340px;
    --rail-width: 44px;
    --bg-void: #05080f;
    --bg-panel: #0a0e1a;
    --bg-control: #0c1225;
    --bg-card: #0a1020;
    --border-dim: #0f2040;
    --border-mid: #153060;
    --border-glow: #00d4ff;
    --text-primary: #d0e8ff;
    --text-secondary: #5a7a9a;
    --text-muted: #3a5a7a;
    --accent: #00d4ff;
    --accent-dim: #00d4ff30;
    --accent-glow: 0 0 12px #00d4ff40, 0 0 4px #00d4ff20;
    --accent-magenta: #ff00ff;
}

body {
    overflow: hidden;
    font-family: 'Segoe UI', 'Inter', system-ui, -apple-system, sans-serif;
    background: #05080f;
    color: var(--text-primary);
}

/* ============================
   Sidebar — icon rail + content panel
   ============================ */
#sidebar {
    position: fixed; left: 0; top: 0;
    width: var(--sidebar-width); height: 100%;
    display: flex; flex-direction: row;
    z-index: 10;
    transition: width 0.25s cubic-bezier(0.4, 0, 0.2, 1);
    background: var(--bg-void);
}

/* Scanline overlay */
#sidebar::after {
    content: ''; position: absolute; inset: 0;
    background: repeating-linear-gradient(
        0deg,
        transparent, transparent 2px,
        rgba(0,212,255,0.015) 2px, rgba(0,212,255,0.015) 4px
    );
    pointer-events: none; z-index: 1;
}

#canvas-container {
    margin-left: var(--sidebar-width);
    width: calc(100% - var(--sidebar-width));
    height: 100vh;
    transition: margin-left 0.25s cubic-bezier(0.4, 0, 0.2, 1),
                width 0.25s cubic-bezier(0.4, 0, 0.2, 1);
    /* v11.2: OrbitControls sets touch-action:none on the canvas itself, but
       gaps around it could still trigger browser page-zoom/scroll on mobile */
    touch-action: none;
}

/* ============================
   Icon Rail — vertical nav
   ============================ */
.sidebar-icon-rail {
    width: var(--rail-width); flex-shrink: 0;
    background: var(--bg-void);
    border-right: 1px solid var(--border-dim);
    display: flex; flex-direction: column;
    align-items: center;
    padding: 8px 0;
    position: relative; z-index: 2;
}

.sidebar-logo {
    width: 32px; height: 32px;
    display: flex; align-items: center; justify-content: center;
    color: var(--accent);
    margin-bottom: 12px;
    filter: drop-shadow(0 0 6px var(--accent-dim));
}

.icon-rail-tabs {
    display: flex; flex-direction: column;
    gap: 2px; flex: 1;
    /* v14: 8 tabs + GPU button — allow scroll on very short windows */
    min-height: 0; overflow-y: auto;
    scrollbar-width: none;
}
.icon-rail-tabs::-webkit-scrollbar { display: none; }
.icon-rail-tabs .icon-btn { flex-shrink: 0; }

.icon-btn {
    width: 36px; height: 36px; border-radius: 6px;
    display: flex; align-items: center; justify-content: center;
    color: var(--text-secondary); cursor: pointer;
    border: 1px solid transparent;
    background: transparent;
    transition: all 0.2s;
    position: relative;
}
.icon-btn:hover {
    color: var(--accent);
    border-color: var(--border-dim);
    background: rgba(0,212,255,0.04);
}
.icon-btn.active {
    color: var(--accent);
    border-color: var(--accent);
    box-shadow: var(--accent-glow);
    background: rgba(0,212,255,0.06);
}

/* Tooltip */
.icon-btn[data-tooltip]::after {
    content: attr(data-tooltip);
    position: absolute; left: 44px; top: 50%; transform: translateY(-50%);
    background: var(--bg-panel); color: var(--accent); padding: 4px 10px;
    border-radius: 4px; font-size: 0.72em; white-space: nowrap;
    border: 1px solid var(--border-dim);
    box-shadow: var(--accent-glow);
    opacity: 0; pointer-events: none;
    transition: opacity 0.15s;
    z-index: 100;
}
.icon-btn[data-tooltip]:hover::after { opacity: 1; }

/* Collapse button */
.sidebar-collapse-btn {
    margin-top: auto;
    transition: all 0.2s;
}
.sidebar-collapse-btn svg {
    transition: transform 0.25s;
}
#sidebar.collapsed .sidebar-collapse-btn svg {
    transform: rotate(180deg);
}

/* ============================
   Sidebar Content Panel
   ============================ */
.sidebar-content {
    flex: 1; min-width: 0;
    display: flex; flex-direction: column;
    overflow: hidden;
    background: var(--bg-panel);
    border-right: 1px solid var(--border-dim);
    position: relative;
}

.sidebar-header {
    padding: 14px 16px 10px;
    flex-shrink: 0;
    border-bottom: 1px solid var(--border-dim);
    position: relative;
}

h1 {
    font-size: 0.95em; color: var(--accent);
    letter-spacing: 1px; font-weight: 600;
    text-transform: uppercase;
    text-shadow: 0 0 12px var(--accent-dim);
    margin: 0;
}

.sidebar-tab-label {
    display: block;
    font-size: 0.68em;
    color: var(--text-secondary);
    text-transform: uppercase;
    letter-spacing: 2px;
    margin-top: 4px;
}

/* ============================
   Collapsed State
   ============================ */
#sidebar.collapsed {
    width: calc(var(--rail-width) + 2px);
}
#sidebar.collapsed .sidebar-content {
    display: none;
}
#sidebar.collapsed .sidebar-resize-handle {
    display: none;
}
#sidebar.collapsed ~ #canvas-container {
    margin-left: calc(var(--rail-width) + 2px);
    width: calc(100% - var(--rail-width) - 2px);
}

/* ============================
   Resize Handle
   ============================ */
.sidebar-resize-handle {
    position: absolute; right: -3px; top: 0;
    width: 6px; height: 100%;
    cursor: col-resize; z-index: 12;
    background: transparent;
    transition: background 0.2s;
}
.sidebar-resize-handle:hover,
.sidebar-resize-handle.dragging {
    background: var(--accent);
    box-shadow: 0 0 12px var(--accent), 0 0 4px var(--accent);
}

/* ============================
   Tab Panels — scrollable content
   ============================ */
.tab-panel {
    display: none;
    flex: 1; min-height: 0;
    overflow-y: auto; overflow-x: hidden;
    padding: 12px 14px 20px;
}
.tab-panel.active { display: block; }

/* Custom scrollbar — neon */
.tab-panel::-webkit-scrollbar { width: 4px; }
.tab-panel::-webkit-scrollbar-track { background: transparent; }
.tab-panel::-webkit-scrollbar-thumb { background: var(--accent-dim); border-radius: 2px; }
.tab-panel::-webkit-scrollbar-thumb:hover { background: rgba(0,212,255,0.5); }
.tab-panel { scrollbar-width: thin; scrollbar-color: var(--accent-dim) transparent; }

/* ============================
   Loading / Error — pinned
   ============================ */
#loading-indicator, #error-message { flex-shrink: 0; }

/* ============================
   Controls — Neon Style
   ============================ */
.control-group { margin-bottom: 12px; }

.control-group > label {
    display: block; font-size: 0.72em; margin-bottom: 4px;
    color: var(--text-secondary);
    text-transform: uppercase; letter-spacing: 0.06em;
    font-weight: 500;
}

/* Select */
select {
    width: 100%; background: var(--bg-control); color: var(--text-primary);
    border: 1px solid var(--border-dim); padding: 7px 8px; border-radius: 4px;
    font-size: 0.85em; cursor: pointer; outline: none;
    transition: all 0.2s;
    font-family: inherit;
}
select:hover { border-color: var(--accent-dim); }
select:focus {
    border-color: var(--accent);
    box-shadow: 0 0 0 1px var(--accent-dim), var(--accent-glow);
}

optgroup { background: var(--bg-control); color: var(--text-secondary); font-style: normal; }

/* Range sliders — custom neon */
input[type=range] {
    width: 100%; cursor: pointer;
    -webkit-appearance: none; appearance: none;
    height: 20px; background: transparent;
}
input[type=range]::-webkit-slider-runnable-track {
    height: 3px; background: var(--border-dim); border-radius: 2px;
}
input[type=range]::-webkit-slider-thumb {
    -webkit-appearance: none;
    width: 14px; height: 14px; border-radius: 50%;
    background: var(--accent);
    box-shadow: 0 0 8px var(--accent-dim);
    margin-top: -5.5px;
    cursor: pointer;
    transition: box-shadow 0.15s;
}
input[type=range]::-webkit-slider-thumb:hover {
    box-shadow: 0 0 14px var(--accent);
}
input[type=range]::-moz-range-track {
    height: 3px; background: var(--border-dim); border-radius: 2px;
}
input[type=range]::-moz-range-thumb {
    width: 14px; height: 14px; border-radius: 50%;
    background: var(--accent); border: none;
    box-shadow: 0 0 8px var(--accent-dim);
    cursor: pointer;
}

.range-value {
    font-size: 0.85em; color: var(--accent); float: right;
    font-variant-numeric: tabular-nums;
    text-shadow: 0 0 6px var(--accent-dim);
}

/* v13.6.1: theme every radio globally — native OS radios (white circle /
   blue dot) clashed with the neon theme on bare <label><input> controls
   that sit outside .radio-group. */
input[type=radio] {
    accent-color: var(--accent);
    width: 15px; height: 15px;
    vertical-align: middle;
    cursor: pointer;
}

/* v13.7: fully custom checkbox (native accent-color still left an OS-drawn
   white well when unchecked — foreign against the neon theme on
   hdr-toggle, toggle-label and bare <label><input> controls outside
   .checkbox-group: Auto-Rotate, Audio Reactive, Soft Shadows…). One global
   rule; .checkbox-group/mobile overrides below only resize it. */
input[type=checkbox] {
    appearance: none; -webkit-appearance: none;
    width: 16px; height: 16px; flex-shrink: 0;
    border-radius: 3px;
    background: var(--bg-void);
    border: 1px solid var(--border-mid);
    vertical-align: middle;
    cursor: pointer;
    position: relative;
    transition: background 0.15s, border-color 0.15s, box-shadow 0.15s;
}
input[type=checkbox]:hover { border-color: var(--accent-dim); }
input[type=checkbox]:checked {
    background: var(--accent); border-color: var(--accent);
    box-shadow: 0 0 6px var(--accent-dim);
}
input[type=checkbox]:checked::after {
    content: '';
    position: absolute; left: 4px; top: 1px;
    width: 4px; height: 8px;
    border: solid var(--bg-void);
    border-width: 0 2px 2px 0;
    transform: rotate(45deg);
}
input[type=checkbox]:focus-visible {
    outline: none;
    box-shadow: 0 0 0 2px var(--bg-void), 0 0 0 4px var(--accent-dim);
}

.radio-group { display: flex; flex-direction: column; gap: 4px; }
.radio-group label {
    display: flex; align-items: center; gap: 8px;
    font-size: 0.85em; color: var(--text-primary); cursor: pointer;
    text-transform: none; letter-spacing: 0;
}
.radio-group input[type=radio] { accent-color: var(--accent); }

.checkbox-group label {
    display: flex; align-items: center; gap: 8px;
    font-size: 0.85em; color: var(--text-primary); cursor: pointer;
    text-transform: none; letter-spacing: 0;
}

#param-sliders { margin-top: 2px; }
#param-sliders .control-group { margin-bottom: 8px; }
#param-sliders label { font-size: 0.75em; color: var(--text-secondary); text-transform: none; }

/* Info Panel — neon card */
#info-panel {
    margin-top: 10px; padding: 10px;
    background: var(--bg-card); border-radius: 5px;
    border: 1px solid var(--border-dim);
    position: relative;
}
#info-panel::before {
    content: ''; position: absolute; top: -1px; left: 8px; width: 40px; height: 1px;
    background: var(--accent); box-shadow: 0 0 6px var(--accent);
}

#info-panel h3 {
    font-size: 0.8em; color: var(--accent); margin-bottom: 6px;
    text-shadow: 0 0 8px var(--accent-dim);
}

.info-row {
    display: flex; justify-content: space-between;
    font-size: 0.78em; padding: 2px 0; color: var(--text-secondary);
}
.info-row .info-label { color: var(--text-muted); }

/* Divider — neon gradient */
.divider {
    border: none; height: 1px; margin: 10px 0;
    background: linear-gradient(90deg, transparent, var(--accent-dim), transparent);
}

/* Button row */
.btn-row { display: flex; gap: 6px; margin-top: 4px; }

/* Buttons — neon outline */
.btn {
    flex: 1; padding: 6px 8px;
    border: 1px solid var(--border-dim);
    background: var(--bg-control); color: var(--text-secondary);
    border-radius: 4px; font-size: 0.78em;
    cursor: pointer; text-align: center;
    transition: all 0.15s; font-family: inherit;
}
.btn:hover {
    border-color: var(--accent-dim);
    color: var(--text-primary);
    box-shadow: 0 0 8px rgba(0,212,255,0.1);
}
.btn.active {
    background: var(--accent); color: var(--bg-void);
    border-color: var(--accent); font-weight: 600;
    box-shadow: 0 0 12px var(--accent-dim);
}

#error-message {
    display: none; padding: 8px 16px; margin: 0;
    background: #1a0510; border-top: 1px solid #ff444430;
    color: #ff6666; font-size: 0.78em;
}

#loading-indicator {
    display: none; text-align: center; padding: 8px 16px;
    color: var(--accent); font-size: 0.8em;
    text-shadow: 0 0 8px var(--accent-dim);
}

#loading-indicator .spinner {
    display: inline-block; width: 12px; height: 12px;
    border: 2px solid var(--border-dim); border-top-color: var(--accent);
    border-radius: 50%; animation: spin 0.8s linear infinite;
    vertical-align: middle; margin-right: 6px;
}

@keyframes spin { to { transform: rotate(360deg); } }

.tag {
    display: inline-block; font-size: 0.7em; padding: 1px 5px;
    border-radius: 3px; margin-left: 4px;
}
.tag-yes { background: #0a2a15; color: #4ade80; border: 1px solid #4ade8030; }
.tag-no { background: #2a0510; color: #f87171; border: 1px solid #f8717130; }
.tag-na { background: #111; color: var(--text-muted); }

/* ============================
   Effects Section
   ============================ */
.effect-row {
    display: flex; align-items: center; justify-content: space-between;
    padding: 4px 0; border-bottom: 1px solid rgba(15,32,64,0.3);
}
.effect-row label { font-size: 0.82em; color: var(--text-secondary); cursor: pointer; display: flex; align-items: center; gap: 6px; }
.effect-slider { margin-top: 2px; }

/* ============================
   Palette Grid
   ============================ */
.palette-grid {
    display: flex; flex-direction: column; gap: 4px; margin-top: 6px;
    /* v13.6.1: 280 -> 380 on desktop so more categories are visible before
       scrolling; mobile drops the inner scroll entirely (see media query). */
    max-height: 380px; overflow-y: auto; padding-right: 2px;
}

/* v13.6.1: generic paired row — a dial (or any control-group) beside a
   checkbox/select, used where a lone dial left dead space (Music tab
   Sensitivity + Audio Reactive; Motion tab Duration + Loop). */
.control-row {
    display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
}
.control-row > .checkbox-group,
.control-row > .control-group:not(:has(.dial-cell)) {
    flex: 1; min-width: 120px; margin-bottom: 12px;
}

/* v13.6.1: duplicates the Projection <select>'s own visible value in
   accent-colored caps beside the label — kept wired (index.html inline
   script + presets.js both write to it) but not shown. */
#projection-val { display: none; }

/* v13.6.1: dissolves the GPU-only wrapper from the box tree so its dials
   (Focal Length…) flow inline alongside sibling dials in the Camera card
   (Fly Speed) instead of starting a forced new "line". The wrapper's own
   .gpu-fractal-only class carries no visual rule to lose — since v13.6
   removed mesh mode every manifold is a GPU fractal, so nothing ever hides
   this div; display:contents only changes how its children lay out. */
#motion-gpu-controls { display: contents; }
.palette-category-label {
    font-size: 0.6em; text-transform: uppercase; letter-spacing: 0.08em;
    color: var(--text-dim); padding: 4px 0 1px; margin-top: 2px;
}
.palette-category-row {
    display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 4px;
}
.palette-swatch {
    padding: 4px 3px; border-radius: 4px; cursor: pointer; font-size: 0.6em;
    text-align: center; border: 2px solid transparent; transition: all 0.2s;
    text-shadow: 0 1px 3px #000; overflow: hidden; white-space: nowrap; text-overflow: ellipsis;
}
.palette-swatch:hover { border-color: rgba(255,255,255,0.15); }
.palette-swatch.active {
    border-color: var(--accent);
    box-shadow: 0 0 8px var(--accent-dim);
}

/* Palette editor */
.palette-editor-preview {
    height: 20px; border-radius: 4px; margin-bottom: 6px;
    background: linear-gradient(90deg, #f0f, #0ff, #ff0, #f0f);
}
.palette-editor-rows { display: flex; flex-direction: column; gap: 3px; }
.palette-editor-row {
    display: grid; grid-template-columns: 32px 1fr 1fr 1fr; gap: 4px; align-items: center;
}
.pe-label { font-size: 0.6em; color: var(--text-dim); text-transform: uppercase; }
.pe-slider { width: 100%; height: 4px; }
.pe-slider[data-ch="0"] { accent-color: #ff4444; }
.pe-slider[data-ch="1"] { accent-color: #44ff44; }
.pe-slider[data-ch="2"] { accent-color: #4488ff; }

/* ============================
   Background Grid
   ============================ */
.background-grid {
    display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 5px; margin-top: 6px;
}
.bg-swatch {
    cursor: pointer; border: 2px solid transparent; border-radius: 4px;
    text-align: center; transition: all 0.2s; padding: 3px;
}
.bg-swatch:hover { border-color: rgba(255,255,255,0.15); }
.bg-swatch.active {
    border-color: var(--accent);
    box-shadow: 0 0 8px var(--accent-dim);
}
.bg-swatch span {
    display: block; font-size: 0.65em; color: var(--text-secondary); margin-top: 2px;
    text-transform: uppercase; letter-spacing: 0.3px;
}
.bg-preview { height: 32px; border-radius: 3px; }
.bg-preview-black { background: #000; }
.bg-preview-mirror { background: radial-gradient(ellipse at 40% 45%, #1a2030 0%, #0a0e1a 60%, #060810); }
.bg-preview-void { background: linear-gradient(180deg, #020204, #0a0a14); }
.bg-preview-nebula { background: radial-gradient(ellipse at 40% 50%, #2a1845, #0f1028 60%, #080812); }
.bg-preview-starfield { background: radial-gradient(circle at 60% 40%, #0a0a1a 0%, #050510 100%); position: relative; overflow: hidden; }
.bg-preview-starfield::after {
    content: ''; position: absolute; inset: 0;
    background: radial-gradient(1px 1px at 20% 30%, #fff 50%, transparent 100%),
                radial-gradient(1px 1px at 60% 70%, #cce 50%, transparent 100%),
                radial-gradient(1px 1px at 80% 20%, #eef 50%, transparent 100%),
                radial-gradient(1px 1px at 40% 80%, #ddf 50%, transparent 100%),
                radial-gradient(1px 1px at 10% 60%, #ccf 50%, transparent 100%);
}
.bg-preview-plasma { background: linear-gradient(135deg, #1a0030 0%, #0a1040 40%, #002030 100%); }
.bg-preview-echo { background: radial-gradient(circle at 50% 50%, #102030 0%, #0a0a18 50%, #050510 100%); }
.bg-preview-deep { background: radial-gradient(ellipse at 35% 45%, #1a1030 0%, #0a0818 40%, #040410 100%); }
.bg-preview-grid {
    background: linear-gradient(180deg, #050510 0%, #050510 45%, #0a0a20 50%, #080820 100%);
    position: relative; overflow: hidden;
}
.bg-preview-grid::after {
    content: ''; position: absolute; inset: 0;
    background:
        linear-gradient(90deg, transparent 48%, #00d4ff18 49%, #00d4ff18 51%, transparent 52%),
        linear-gradient(180deg, transparent 40%, #00d4ff10 50%, #00d4ff18 60%, #00d4ff10 80%, transparent 100%);
}
.bg-preview-warp {
    background: radial-gradient(circle at 50% 50%, #0a1020 0%, #060812 40%, #030408 100%);
    position: relative; overflow: hidden;
}
.bg-preview-warp::after {
    content: ''; position: absolute; inset: 0;
    background:
        conic-gradient(from 0deg at 50% 50%, transparent 0deg, #88aaff08 10deg, transparent 20deg,
            transparent 60deg, #aaccff08 70deg, transparent 80deg,
            transparent 120deg, #99bbff08 130deg, transparent 140deg,
            transparent 200deg, #bbddff08 210deg, transparent 220deg,
            transparent 280deg, #aabbee08 290deg, transparent 300deg, transparent 360deg);
}

/* ============================
   Recording Indicator — v13.7: same .canvas-pill language as the render
   and zoom badges, top-center so it never collides with the top-right
   render badge or the top-left FAB stack at any viewport width.
   ============================ */
#recording-indicator {
    display: none; position: fixed; top: 10px; left: 50%;
    transform: translateX(-50%); z-index: 65;
    background: rgba(200,20,20,0.88); color: #fff;
    border-color: rgba(255,255,255,0.3);
    animation: pulse-red 1s ease infinite;
    box-shadow: 0 0 16px #ff000040;
}
#recording-indicator::before {
    content: ''; width: 7px; height: 7px; border-radius: 50%;
    background: #fff; flex-shrink: 0;
}
@keyframes pulse-red { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } }

/* HQ Offline Render */
body.hq-rendering #sidebar,
body.hq-rendering #fab-stack,
body.hq-rendering .bg-popover,
body.hq-rendering .nav-popover,
body.hq-rendering .zoom-depth-badge,
body.hq-rendering #recording-indicator,
body.hq-rendering .fullscreen-controls,
body.hq-rendering #gpu-stream-badge { display: none !important; }
body.hq-rendering #canvas-container { margin-left: 0; width: 100%; }
#hq-render-overlay {
    display: none; position: fixed; inset: 0; z-index: 9999;
    flex-direction: column; align-items: center; justify-content: center;
    background: rgba(5,8,15,0.85); color: var(--accent, #00d4ff);
    font-family: monospace; gap: 12px; pointer-events: none;
}
.hq-render-text { font-size: 1.1em; }
.hq-render-bar {
    width: min(400px, 80vw); height: 6px; background: rgba(255,255,255,0.1);
    border-radius: 3px; overflow: hidden;
}
.hq-render-fill {
    height: 100%; width: 0; background: var(--accent, #00d4ff);
    transition: width 0.15s ease;
}

/* ============================
   Capture tab layout
   ============================ */
.capture-settings-grid {
    display: grid; grid-template-columns: 1fr 1fr; gap: 6px;
}
.capture-settings-grid:has(.capture-setting:nth-child(3)) {
    grid-template-columns: 1fr 1fr 1fr;
}
.capture-setting {
    display: flex; flex-direction: column; gap: 2px;
}
.capture-setting-label {
    font-size: 0.68em; color: var(--text-muted);
    text-transform: uppercase; letter-spacing: 0.06em;
}
.capture-setting select {
    width: 100%; font-size: 0.82em; padding: 3px 4px;
}
.capture-hint {
    font-size: 0.68em; color: var(--text-muted); margin-top: 4px;
    line-height: 1.3;
}
.capture-primary-btn {
    flex: 1; font-size: 0.85em !important; padding: 6px 10px !important;
}
.capture-stop-btn {
    background: rgba(255,60,60,0.15) !important;
    border-color: #ff3c3c !important; color: #ff6666 !important;
}
.capture-stop-btn:hover {
    background: rgba(255,60,60,0.3) !important;
    box-shadow: 0 0 8px rgba(255,60,60,0.3) !important;
}
.capture-countdown {
    font-size: 0.78em; color: var(--accent);
    margin-top: 4px; font-variant-numeric: tabular-nums;
}

/* ============================
   Fullscreen Mode
   ============================ */
body.fullscreen #sidebar { transform: translateX(-100%); }
body.fullscreen #canvas-container { margin-left: 0; width: 100%; }
.fullscreen-controls {
    display: none; position: fixed; bottom: 20px; left: 50%;
    transform: translateX(-50%); z-index: 20;
    background: rgba(10,14,26,0.9); padding: 8px 16px; border-radius: 20px;
    border: 1px solid var(--border-dim);
    transition: opacity 0.3s;
}
body.fullscreen .fullscreen-controls { display: flex; gap: 12px; }
.fullscreen-controls .btn { padding: 5px 12px; font-size: 0.8em; }

/* ============================
   Keyboard Shortcuts Overlay
   ============================ */
#shortcuts-overlay {
    display: none; position: fixed; inset: 0; z-index: 200;
    background: rgba(0,0,0,0.85); justify-content: center; align-items: center;
}
#shortcuts-overlay.visible { display: flex; }
#shortcuts-panel {
    background: var(--bg-panel); border: 1px solid var(--border-dim); border-radius: 8px;
    padding: 24px; max-width: 500px; width: 90%;
    box-shadow: 0 0 40px rgba(0,212,255,0.1);
    position: relative;
}
#shortcuts-panel::before {
    content: ''; position: absolute; top: -1px; left: 20px; right: 20px; height: 1px;
    background: linear-gradient(90deg, transparent, var(--accent), transparent);
}
#shortcuts-panel h2 { color: var(--accent); margin-bottom: 16px; font-size: 1.1em; text-shadow: 0 0 10px var(--accent-dim); }
.shortcut-row { display: flex; justify-content: space-between; padding: 4px 0; font-size: 0.85em; color: var(--text-secondary); }
.shortcut-key {
    background: var(--bg-control); padding: 2px 8px; border-radius: 3px;
    font-family: monospace; color: var(--accent); font-size: 0.9em;
    border: 1px solid var(--border-dim);
}

/* ============================
   Spectrum Canvas
   ============================ */
#spectrum-canvas {
    width: 100%; height: 90px; border-radius: 4px;
    background: var(--bg-void); border: 1px solid var(--border-dim);
}

/* ============================
   Node Graph — Cable Visualization
   ============================ */
#react-node-graph {
    position: relative;
    min-height: 120px;
    margin-bottom: 8px;
}

#react-cables-svg {
    position: absolute; inset: 0;
    width: 100%; height: 100%;
    pointer-events: none;
    z-index: 1;
}

.node-graph-columns {
    display: flex; justify-content: space-between;
    position: relative; z-index: 2;
    gap: 8px;
}

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

#react-sources { align-items: flex-start; }
#react-targets { align-items: flex-end; }

.node-port {
    display: flex; align-items: center; gap: 5px;
    padding: 3px 8px; border-radius: 4px;
    font-size: 0.68em; cursor: pointer;
    border: 1px solid var(--border-dim);
    background: var(--bg-card);
    color: var(--text-secondary);
    transition: all 0.15s;
    white-space: nowrap;
    position: relative;
}
.node-port:hover {
    border-color: var(--accent-dim);
    color: var(--text-primary);
}
.node-port.connected {
    border-color: var(--accent);
    color: var(--accent);
    box-shadow: 0 0 6px var(--accent-dim);
}
.node-port.selecting {
    border-color: var(--accent-magenta);
    box-shadow: 0 0 8px rgba(255,0,255,0.3);
    animation: port-pulse 1s ease infinite;
}

@keyframes port-pulse {
    0%, 100% { box-shadow: 0 0 8px rgba(255,0,255,0.3); }
    50% { box-shadow: 0 0 16px rgba(255,0,255,0.5); }
}

.node-dot {
    width: 7px; height: 7px; border-radius: 50%;
    background: var(--text-muted);
    flex-shrink: 0;
    transition: all 0.2s;
}
.node-port.connected .node-dot {
    background: var(--accent);
    box-shadow: 0 0 6px var(--accent);
}

/* Cable animation */
.cable-path {
    fill: none;
    stroke: var(--accent);
    stroke-width: 1.5;
    opacity: 0.5;
    transition: opacity 0.3s;
}
.cable-path.active {
    opacity: 0.9;
    filter: drop-shadow(0 0 3px var(--accent));
    stroke-dasharray: 8 4;
    animation: cable-flow 1s linear infinite;
}

@keyframes cable-flow {
    to { stroke-dashoffset: -12; }
}

/* ============================
   Reactivity Bindings — Neon Cards
   ============================ */
.binding-card {
    background: var(--bg-card); border-radius: 5px; padding: 8px;
    margin-bottom: 6px;
    border: 1px solid var(--border-dim);
    position: relative;
    transition: border-color 0.2s;
}
.binding-card:hover {
    border-color: var(--border-mid);
}
.binding-card::before {
    content: ''; position: absolute; top: -1px; left: 6px; width: 24px; height: 1px;
    background: var(--accent); opacity: 0.5;
}
.binding-card .binding-header {
    display: flex; justify-content: space-between; align-items: center;
    margin-bottom: 4px;
}
.binding-card .binding-header span {
    font-size: 0.7em; color: var(--accent); text-transform: uppercase; letter-spacing: 0.5px;
    text-shadow: 0 0 6px var(--accent-dim);
}
.binding-card .binding-remove {
    background: none; border: none; color: #f87171; cursor: pointer;
    font-size: 0.85em; padding: 2px 6px;
    transition: all 0.15s;
}
.binding-card .binding-remove:hover { color: #ff4444; text-shadow: 0 0 6px #ff444460; }
.binding-card select, .binding-card input[type=range] {
    width: 100%; margin-top: 3px;
}
.binding-card label {
    font-size: 0.68em; color: var(--text-muted); text-transform: none; display: block; margin-top: 4px;
}
.binding-row {
    display: flex; gap: 6px; align-items: center;
}
.binding-row select { flex: 1; font-size: 0.78em; padding: 4px; }

/* ============================
   Live Performance Buttons
   ============================ */
.live-trigger, .live-bass, .live-fx {
    transition: all 0.1s;
}
.live-trigger:active, .live-bass:active, .live-fx:active {
    background: var(--accent); color: var(--bg-void); transform: scale(0.95);
    box-shadow: 0 0 12px var(--accent-dim);
}
.live-trigger.flash {
    background: var(--accent); color: var(--bg-void);
    box-shadow: 0 0 12px var(--accent-dim);
}

/* ============================
   Now Playing Panel
   ============================ */
#now-playing-panel {
    background: var(--bg-card); border-radius: 5px; padding: 6px 8px;
    margin: 6px 0; border: 1px solid var(--border-dim);
}
#now-playing-panel .info-label { color: var(--accent); }

/* ============================
   Journey Panel
   ============================ */
.mood-indicator {
    display: flex; align-items: center; gap: 8px; padding: 5px 8px;
    background: var(--bg-card); border-radius: 5px; border: 1px solid var(--border-dim);
}
.mood-dot {
    display: inline-block; width: 9px; height: 9px; border-radius: 50%;
    background: #44ff88; transition: background 0.5s;
}
.mood-dot[data-mood="drop"] { background: #ff4444; box-shadow: 0 0 8px #ff444460; }
.mood-dot[data-mood="buildup"] { background: #ffaa00; box-shadow: 0 0 8px #ffaa0060; }
.mood-dot[data-mood="breakdown"] { background: #4488ff; box-shadow: 0 0 8px #4488ff60; }
.mood-dot[data-mood="sustain"] { background: #44ff88; box-shadow: 0 0 8px #44ff8860; }
#journey-mood-text {
    font-size: 0.78em; color: var(--text-secondary); text-transform: capitalize;
}

.scene-dots {
    display: flex; gap: 8px; padding: 4px 0;
}
.scene-dot {
    width: 12px; height: 12px; border-radius: 50%; cursor: pointer;
    background: var(--bg-card); border: 2px solid var(--border-dim); transition: all 0.3s;
}
.scene-dot:hover { border-color: var(--accent); }
.scene-dot.active {
    background: var(--accent); border-color: var(--accent);
    box-shadow: 0 0 8px var(--accent-dim);
}

.lfo-card {
    background: var(--bg-card); border-radius: 5px; padding: 8px;
    margin-bottom: 6px; border: 1px solid var(--border-dim);
    position: relative;
}
.lfo-card::before {
    content: ''; position: absolute; top: -1px; left: 6px; width: 24px; height: 1px;
    background: var(--accent-magenta); opacity: 0.4;
}
.lfo-card .binding-header {
    display: flex; justify-content: space-between; align-items: center;
    margin-bottom: 4px;
}
.lfo-card .binding-header span {
    font-size: 0.7em; color: var(--accent); text-transform: uppercase; letter-spacing: 0.5px;
}
.lfo-card select, .lfo-card input[type=range] {
    width: 100%; margin-top: 3px;
}
.lfo-card label {
    font-size: 0.68em; color: var(--text-muted); text-transform: none; display: block; margin-top: 4px;
}

#energy-canvas {
    width: 100%; height: 50px; border-radius: 4px;
    background: var(--bg-void); border: 1px solid var(--border-dim);
}

/* ============================
   Pattern Controls
   ============================ */
#pattern-start-btn.active {
    background: var(--accent); color: var(--bg-void);
    box-shadow: 0 0 8px var(--accent-dim);
}

/* ============================
   v12.0 Dial control
   ============================ */
.dial-hidden-input { display: none !important; }
label.dial-src-label { display: none !important; }

.dial-cell {
    display: flex; flex-direction: column; align-items: center;
    width: 76px; padding: 4px 2px;
    user-select: none; -webkit-user-select: none;
}
.dial-knob {
    position: relative; width: 56px; height: 56px;
    cursor: ns-resize;
    touch-action: none;
}
.dial-svg { width: 100%; height: 100%; display: block; }
.dial-track {
    fill: none; stroke: var(--border-mid); stroke-width: 3;
    stroke-linecap: round;
}
.dial-fill {
    fill: none; stroke: var(--accent); stroke-width: 3;
    stroke-linecap: round;
    filter: drop-shadow(0 0 3px var(--accent-dim));
    transition: stroke-dashoffset 0.05s linear;
}
.dial-needle {
    position: absolute; inset: 0;
    transform: rotate(135deg);
    pointer-events: none;
}
.dial-needle span {
    position: absolute; left: 50%; top: 50%;
    width: 10px; height: 2px;
    margin-top: -1px;
    transform: translateX(9px);
    background: var(--text-primary);
    border-radius: 1px;
}
.dial-value {
    position: absolute; inset: 0;
    display: flex; align-items: center; justify-content: center;
    font-size: 0.62em; color: var(--text-primary);
    pointer-events: none;
    font-variant-numeric: tabular-nums;
}
.dial-label {
    font-size: 0.6em; color: var(--text-secondary);
    text-align: center; line-height: 1.15; margin-top: 1px;
    /* v13.6.1: relative, not a fixed 74px — tracks whatever width the
       .dial-cell parent actually has (76px default, 54px param-row, 48px
       narrow-mobile) instead of silently overflowing it and ellipsizing
       mid-word ("Color Cycle...", "Fly Speed..") once the cell shrank but
       this didn't. 3-line clamp gives 3-word labels ("Color Cycle Speed")
       room to wrap instead of losing their last word. */
    max-width: 100%;
    overflow: hidden;
    display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical;
}
.dial-knob.dragging .dial-fill { stroke-width: 4; }
.dial-reset-flash .dial-fill { stroke: var(--accent-magenta); }

/* Dialized control groups flow inline so adjacent dials form rows */
.control-group:has(> .dial-cell),
.control-group:has(> .param-slider-row .dial-cell) {
    display: inline-flex; vertical-align: top;
    width: auto; margin: 0 2px 4px 0;
}
.param-slider-row:has(.dial-cell) {
    display: inline-flex; align-items: center; gap: 1px;
}
/* v12.0.1: compact param rows so two manifold-param dials fit per row at
   the default sidebar width (dial + [ ] ▶ sweep buttons) */
.param-slider-row .dial-cell { width: 54px; padding: 2px 0; }
.param-slider-row .dial-knob { width: 44px; height: 44px; }
.param-slider-row .dial-label { max-width: 54px; font-size: 0.56em; }
.param-slider-row .dial-value { font-size: 0.56em; }
.param-slider-row:has(.dial-cell) {
    gap: 0;
}
/* v13.6.1: the [ ] bound buttons + ▶ sweep button read as cryptic clutter
   around the dial they belong to — ghost them down to near-invisible until
   hover/focus so the dial reads as the primary control and the cluster
   reads as one unit, not four. Functionality/ids/listeners untouched. */
.param-slider-row:has(.dial-cell) .param-bound-btn,
.param-slider-row:has(.dial-cell) .param-play-btn {
    width: 20px; height: 26px;
    padding: 0; font-size: 0.62em;
    opacity: 0.4;
    background: transparent;
    border-color: transparent;
    box-shadow: none;
}
.param-slider-row:has(.dial-cell) .param-bound-btn:hover,
.param-slider-row:has(.dial-cell) .param-bound-btn:focus-visible,
.param-slider-row:has(.dial-cell) .param-play-btn:hover,
.param-slider-row:has(.dial-cell) .param-play-btn:focus-visible {
    opacity: 1;
    background: rgba(12,18,37,0.6);
    border-color: var(--border-mid);
}
/* Binding/LFO card dials sit inline in a compact row */
.binding-card .dial-cell, .lfo-card .dial-cell {
    display: inline-flex; width: 68px;
}

/* ============================
   v12.0 Control cards
   ============================ */
.ctl-card {
    background: var(--bg-card);
    border: 1px solid var(--border-dim);
    border-radius: 8px;
    margin: 8px 0;
    position: relative;
    overflow: hidden;
}
.ctl-card::before {
    content: ''; position: absolute; left: 0; top: 0; bottom: 0;
    width: 2px; background: var(--accent); opacity: 0.5;
}
.ctl-card-header {
    display: flex; align-items: center; justify-content: space-between;
    padding: 8px 10px;
    cursor: pointer;
    user-select: none; -webkit-user-select: none;
}
.ctl-card-header:hover { background: rgba(0,212,255,0.04); }
.ctl-card-title {
    font-size: 0.72em; color: var(--text-primary);
    text-transform: uppercase; letter-spacing: 0.06em;
}
.ctl-card-header .hdr-toggle {
    display: flex; align-items: center; gap: 6px;
    font-size: 0.72em; color: var(--text-primary);
    text-transform: uppercase; letter-spacing: 0.06em;
    cursor: pointer; margin: 0;
}
.ctl-card-chevron::after {
    content: '\25BE';
    color: var(--text-secondary); font-size: 0.8em;
    display: inline-block;
    transition: transform 0.2s;
}
.ctl-card.collapsed .ctl-card-chevron::after { transform: rotate(-90deg); }
.ctl-card-body { padding: 2px 10px 8px; }
.ctl-card.collapsed .ctl-card-body { display: none; }

/* ============================
   v12.0 Preset chips
   ============================ */
/* v13.7: was inline text after "Preset Slots" inside the same uppercase
   label — at sidebar/mobile-sheet widths that one long run wrapped mid
   sentence. display:block drops it to its own row; nowrap+ellipsis keeps
   that row a single tightened line instead of wrapping again. */
.chip-hint {
    display: block; margin-top: 1px;
    font-size: 0.68em; color: var(--text-muted); text-transform: none;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.preset-chips {
    display: flex; gap: 5px; flex-wrap: wrap; margin-top: 4px;
}
.preset-chip {
    width: 32px; height: 32px;
    border-radius: 50%;
    border: 1px solid var(--border-mid);
    background: var(--bg-control);
    color: var(--text-secondary);
    font-size: 0.8em; cursor: pointer;
    display: flex; align-items: center; justify-content: center;
    transition: all 0.15s;
    touch-action: manipulation;
    position: relative;
}
.preset-chip.filled {
    border-color: var(--accent);
    color: var(--accent);
    box-shadow: 0 0 6px var(--accent-dim);
}
.preset-chip.holding { border-color: var(--accent-magenta); color: var(--accent-magenta); }
.preset-chip.armed {
    background: var(--accent-magenta); color: var(--bg-void);
    box-shadow: 0 0 10px var(--accent-magenta);
}
.preset-chip.saving { opacity: 0.6; }
.preset-chip.flash {
    background: var(--accent); color: var(--bg-void);
    box-shadow: var(--accent-glow);
}
.preset-chips-peek { display: none; }

/* v12.2: Live-tab caption row (hint text + Manage toggle) */
.preset-slots-caption-row {
    display: flex; align-items: center; justify-content: space-between; gap: 8px;
}
.preset-slots-caption-row > label { margin-bottom: 0; flex: 1; min-width: 0; }
.chip-manage-btn {
    flex-shrink: 0;
    padding: 3px 9px;
    border: 1px solid var(--border-mid);
    background: var(--bg-control);
    color: var(--text-secondary);
    border-radius: 4px;
    font-size: 0.68em;
    text-transform: uppercase; letter-spacing: 0.06em;
    cursor: pointer; font-family: inherit;
    transition: all 0.15s;
}
.chip-manage-btn:hover { border-color: var(--accent-dim); color: var(--text-primary); }
.chip-manage-btn.active {
    border-color: var(--accent-magenta); color: var(--accent-magenta);
    box-shadow: 0 0 6px var(--accent-magenta);
}

/* v12.2: manage mode — delete badge on Live-tab chips only (peek strip is
   untouched, see review note in ui.js) */
#preset-chips-live.manage-mode .preset-chip { border-color: var(--accent-magenta); }
#preset-chips-live.manage-mode .preset-chip::after {
    content: '\00d7';
    position: absolute; top: -5px; right: -5px;
    width: 15px; height: 15px; border-radius: 50%;
    background: var(--accent-magenta); color: var(--bg-void);
    font-size: 0.75em; line-height: 15px; text-align: center;
    box-shadow: 0 0 4px var(--accent-magenta);
}
#preset-chips-live.manage-mode .preset-chip:not(.filled)::after { opacity: 0.4; }

/* ============================
   v12.2 Contextual fractal hints (Shapes tab, Suggested row)
   ============================ */
.hint-chips {
    display: flex; flex-wrap: wrap; gap: 5px; margin-top: 4px;
}
.hint-chip {
    flex: 0 1 auto; min-width: 0; max-width: 46%;
    padding: 4px 9px;
    border-radius: 12px;
    border: 1px solid var(--border-mid);
    background: var(--bg-control);
    color: var(--text-secondary);
    font-size: 0.7em;
    cursor: pointer; font-family: inherit;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
    transition: all 0.15s;
}
.hint-chip:hover { border-color: var(--accent-dim); color: var(--text-primary); }
.hint-chip:active { transform: scale(0.96); }
.hint-chip-all {
    flex-basis: 100%; max-width: 100%; text-align: center;
    border-color: var(--accent); color: var(--accent);
}
.hint-chip-all:hover { box-shadow: var(--accent-glow); }

/* ============================
   v12.0 Background FAB + popover
   v13.7: FABs live in #fab-stack, a flex column — slots auto-flow as
   fly-fab (coarse pointer only) / audio-fab (active playback only) come
   and go, so no button carries its own fixed top offset any more.
   ============================ */
#canvas-container { position: relative; }
#fab-stack {
    position: absolute; top: 12px; left: 12px; z-index: 6;
    display: flex; flex-direction: column; align-items: flex-start; gap: 12px;
}
body.fullscreen #fab-stack,
body.fullscreen .bg-popover,
body.fullscreen .nav-popover { display: none; }
.bg-fab {
    width: 40px; height: 40px; border-radius: 50%; flex-shrink: 0;
    background: rgba(10, 14, 26, 0.82);
    border: 1px solid var(--border-mid);
    color: var(--text-secondary);
    display: flex; align-items: center; justify-content: center;
    cursor: pointer;
    backdrop-filter: blur(4px);
    transition: all 0.2s;
}
.bg-fab:hover, .bg-fab.open {
    color: var(--accent); border-color: var(--accent);
    box-shadow: var(--accent-glow);
}
.bg-popover {
    position: absolute; top: 60px; left: 12px; z-index: 6;
    width: 240px;
    background: rgba(10, 14, 26, 0.94);
    border: 1px solid var(--border-mid);
    border-radius: 10px;
    padding: 10px;
    backdrop-filter: blur(6px);
    box-shadow: 0 4px 24px rgba(0,0,0,0.5), var(--accent-glow);
}
.bg-popover-title {
    font-size: 0.72em; color: var(--text-secondary);
    text-transform: uppercase; letter-spacing: 0.06em;
    margin-bottom: 8px;
}

/* ============================
   Mobile Responsive (< 768px)
   ============================ */
@media (max-width: 768px) {
    /* v12.0: always-visible bottom sheet with three detents (peek/half/full).
       Height-driven — the peek strip (tabs + preset chips) never hides. */
    #sidebar {
        position: fixed; left: 0; bottom: 0; top: auto;
        width: 100% !important;
        height: 104px; /* peek */
        flex-direction: column;
        border-right: none;
        border-top: 1px solid var(--accent-dim);
        border-radius: 16px 16px 0 0;
        padding: 0;
        overflow: hidden;
        transition: height 0.3s cubic-bezier(0.4, 0, 0.2, 1);
    }
    #sidebar.sheet-half { height: 48dvh; }
    #sidebar.sheet-full { height: 88dvh; }

    /* v12.1.1: the sheet chrome is a drag surface — without touch-action
       the browser can claim a vertical pan and pointercancel the drag,
       leaving the sheet stuck at its current detent */
    .sidebar-icon-rail { touch-action: none; }

    /* Peek preset chips row (pan-x keeps horizontal chip overflow scroll,
       vertical drags stay ours for the sheet) */
    .preset-chips-peek {
        display: flex; order: -1;
        justify-content: center;
        padding: 2px 8px 6px;
        flex-wrap: nowrap; overflow-x: auto;
        flex-shrink: 0;
        touch-action: pan-x;
        /* Edge fade signals the row scrolls when chips overflow narrow
           viewports (9 chips fit exactly at 375px, not below) */
        -webkit-mask-image: linear-gradient(to right, transparent, black 16px, black calc(100% - 16px), transparent);
        mask-image: linear-gradient(to right, transparent, black 16px, black calc(100% - 16px), transparent);
    }
    .preset-chips-peek .preset-chip { width: 30px; height: 30px; flex-shrink: 0; }

    /* Icon rail becomes horizontal bottom bar */
    .sidebar-icon-rail {
        width: 100%; height: auto;
        flex-direction: row;
        border-right: none;
        border-bottom: 1px solid var(--border-dim);
        padding: 4px 8px;
        order: -1;
        gap: 2px;
        justify-content: center;
    }

    .sidebar-logo { display: none; }
    .sidebar-collapse-btn { display: none; }
    .sidebar-resize-handle { display: none; }

    .icon-rail-tabs {
        flex-direction: row;
        gap: 4px;
        /* v14: 9 buttons (8 tabs + GPU) at 36px each — fits 375px */
        flex-wrap: nowrap;
        overflow-y: visible; overflow-x: visible;
    }

    .icon-btn[data-tooltip]::after { display: none; }

    /* Drag handle */
    #sidebar::before {
        content: ''; display: block;
        width: 40px; height: 4px; background: var(--text-muted);
        border-radius: 2px; margin: 6px auto 2px;
        flex-shrink: 0;
        order: -2;
    }

    /* Hide scanline on mobile */
    #sidebar::after { display: none; }

    .sidebar-content {
        flex: 1; min-height: 0;
        border-right: none;
    }

    #canvas-container {
        margin-left: 0 !important; width: 100% !important; height: 100vh;
    }

    /* v13.6.1: the wordmark row cost ~50px of sheet height on every tab at
       390px for a label the user already knows (they're looking at the
       app). Desktop keeps it — .sidebar-tab-label is already hidden below. */
    .sidebar-header { display: none; }

    /* Touch-friendly controls */
    select { padding: 12px 10px; font-size: 1em; }

    input[type=range] { height: 36px; }
    input[type=range]::-webkit-slider-thumb {
        width: 24px; height: 24px; margin-top: -10.5px;
    }
    input[type=range]::-moz-range-thumb { width: 24px; height: 24px; }

    .radio-group label, .checkbox-group label {
        font-size: 1em; padding: 4px 0; min-height: 36px;
    }

    .radio-group input[type=radio],
    input[type=checkbox] {
        width: 20px; height: 20px;
    }
    input[type=checkbox]:checked::after {
        left: 6px; top: 2px; width: 5px; height: 10px;
    }

    .btn { padding: 12px 14px; font-size: 0.9em; min-height: 44px; }

    .palette-category-row { grid-template-columns: 1fr 1fr; gap: 6px; }
    .palette-swatch { padding: 8px; font-size: 0.75em; }

    /* v13.6.1: the grid's own 280px scroll-inside-scroll clipped categories
       mid-row inside the bottom sheet. Let the sheet itself scroll instead —
       desktop's 380px cap (below) stays put. */
    .palette-grid { max-height: none; overflow-y: visible; padding-right: 0; }

    /* v13.6.1: Perform tab already repeats these 9 chips in the peek strip
       right above the sheet body — keep the hint + Manage row, drop the
       redundant tap targets. */
    #preset-chips-live { display: none; }

    #info-panel { padding: 8px; }
    .info-row { font-size: 0.85em; padding: 3px 0; }

    body.fullscreen #sidebar { display: none; }

    #shortcuts-panel { padding: 16px; max-width: 95%; }
    .shortcut-row { font-size: 0.8em; }

    .fullscreen-controls { bottom: 12px; }

    /* v11.4: hamburger is top-left and the drawer lives at the bottom on
       mobile, so top-right stays clear — just shrink the badge a touch. */
    .gpu-render-badge { top: 8px; right: 8px; font-size: 10px; padding: 4px 8px 4px 6px; }
}

/* ============================
   v13.7: Canvas Pill — shared language for every canvas-overlay pill
   (render badge, zoom badge, recording indicator): same font/padding/
   radius/background alpha/letter-spacing. Each pill keeps its own id +
   modifier classes for position and state color.
   ============================ */
.canvas-pill {
    display: flex; align-items: center; gap: 6px;
    background: rgba(5,8,15,0.8);
    border: 1px solid var(--border-dim);
    border-radius: 18px;
    padding: 5px 10px 5px 8px;
    font-family: monospace;
    font-size: 11px;
    letter-spacing: 0.3px;
    color: var(--text-secondary);
    pointer-events: none;
    user-select: none;
}

/* ============================
   Render-Source Badge (v11.4) — always-visible pill over the canvas
   telling the user whether they're watching remote GH200 frames or the
   local fallback, since #gpu-status-panel is hidden until clicked open.
   ============================ */
.gpu-render-badge {
    position: fixed; top: 10px; right: 10px; z-index: 60;
    transition: box-shadow 0.3s, border-color 0.3s;
}
.gpu-render-badge .grb-dot {
    width: 7px; height: 7px; border-radius: 50%;
    background: #555; flex-shrink: 0;
    transition: background 0.3s, box-shadow 0.3s;
}
.gpu-render-badge.streaming {
    border-color: var(--accent-dim);
    box-shadow: 0 0 10px var(--accent-dim);
    color: var(--text-primary);
}
.gpu-render-badge.streaming .grb-dot { background: var(--accent); box-shadow: 0 0 6px var(--accent); }
.gpu-render-badge.local .grb-dot { background: #666; }
.gpu-render-badge.reconnecting { border-color: #ffaa0060; }
.gpu-render-badge.reconnecting .grb-dot { background: #ffaa00; box-shadow: 0 0 6px #ffaa0060; }

/* ============================
   Zoom Depth Badge (v12.8)
   ============================ */
.zoom-depth-badge {
    position: fixed; bottom: 10px; left: 10px; z-index: 60;
    opacity: 0;
    transition: opacity 0.3s, border-color 0.4s, box-shadow 0.4s, color 0.4s;
}
.zoom-depth-badge .zdb-icon { font-size: 11px; }
.zoom-depth-badge .zdb-label { min-width: 28px; text-align: right; }
.zoom-depth-badge.deep {
    border-color: var(--accent-dim);
    color: var(--accent);
    box-shadow: 0 0 8px var(--accent-dim);
}
@media (max-width: 768px) {
    .zoom-depth-badge { bottom: 112px; left: 8px; font-size: 10px; padding: 4px 8px 4px 6px; }
}
body.fullscreen .zoom-depth-badge { display: none; }

/* ============================
   Performance Overlay
   ============================ */
#perf-overlay {
    position: fixed; top: 10px; right: 10px; z-index: 50;
    background: rgba(5,8,15,0.9); color: #00ff88; padding: 8px 12px;
    border-radius: 6px; font-family: monospace; font-size: 0.75em;
    line-height: 1.6; pointer-events: none; min-width: 140px;
    border: 1px solid #00ff8830;
    box-shadow: 0 0 10px rgba(0,255,136,0.1);
}

/* Hidden cursor */
.cursor-hidden, .cursor-hidden * { cursor: none !important; }

/* ============================
   Param Slider Play Button
   ============================ */
.param-slider-row { display: flex; align-items: center; gap: 6px; }
.param-slider-row input[type=range] { flex: 1; }
.param-play-btn {
    background: rgba(12,18,37,0.6); border: 1px solid var(--border-mid); color: var(--accent);
    width: 28px; height: 28px; border-radius: 4px; cursor: pointer;
    font-size: 0.7em; display: flex; align-items: center; justify-content: center;
    padding: 0; flex-shrink: 0;
    transition: all 0.15s;
}
.param-play-btn:hover {
    background: var(--bg-control); border-color: var(--accent);
    box-shadow: 0 0 6px var(--accent-dim);
}
.param-bound-btn {
    background: rgba(12,18,37,0.6); border: 1px solid var(--border-mid); color: var(--text-muted);
    width: 22px; height: 28px; border-radius: 3px; cursor: pointer;
    font-size: 0.75em; display: flex; align-items: center; justify-content: center;
    padding: 0; flex-shrink: 0;
}
.param-bound-btn:hover { color: var(--accent); background: var(--bg-control); border-color: var(--accent); }
.param-bound-reset { width: 16px; height: 16px; font-size: 0.65em; border: none; color: var(--text-muted); margin-left: 4px; }
.param-bound-reset:hover { color: #f87171; }
.param-bounds {
    font-size: 0.68em; color: var(--accent); opacity: 0.7;
    text-align: center; margin-top: 1px;
    display: flex; align-items: center; justify-content: center; gap: 2px;
}
.param-bound-val { color: #7dd3fc; }

/* Hide mesh-only controls when GPU fractal is active */
#mesh-only-controls.hidden, #mesh-only-controls-2.hidden { display: none; }

/* ============================
   Sidebar drag prevention
   ============================ */
body.sidebar-resizing {
    cursor: col-resize !important;
    user-select: none !important;
}
body.sidebar-resizing * {
    cursor: col-resize !important;
}

/* ============================
   v7.0 Light Cards
   ============================ */
.light-card {
    background: var(--bg-card);
    border: 1px solid var(--border-dim);
    border-radius: 6px;
    padding: 8px 10px;
    margin-bottom: 6px;
    position: relative;
}
.light-card::before {
    content: '';
    position: absolute;
    top: 0; left: 0;
    width: 3px;
    height: 100%;
    background: var(--accent);
    border-radius: 3px 0 0 3px;
}
.light-card-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 4px;
}
.light-name {
    color: var(--text-primary);
    font-size: 11px;
    font-weight: 600;
}
.light-band {
    color: var(--text-muted);
    font-size: 10px;
    text-transform: uppercase;
}
.section-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 8px;
}
.section-header h4 {
    color: var(--text-secondary);
    font-size: 10px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    margin: 0;
}
.toggle-label {
    display: flex;
    align-items: center;
    gap: 5px;
    color: var(--text-secondary);
    font-size: 11px;
    cursor: pointer;
}
.control-section {
    margin-bottom: 12px;
    padding: 10px;
    background: var(--bg-control);
    border: 1px solid var(--border-dim);
    border-radius: 6px;
}

/* ============================
   v7.0 Convergence Bar (Path Tracing)
   ============================ */
.convergence-bar {
    background: var(--bg-control);
    border: 1px solid var(--border-dim);
    border-radius: 2px;
    height: 4px;
    overflow: hidden;
    margin-top: 4px;
    width: 100%;
}

/* ============================
   v7.0 Compositor Controls
   ============================ */
#compositor-controls .control-group {
    margin-bottom: 6px;
}

/* ============================
   v8.0 Remote GPU Button + Modal
   ============================ */

/* GPU connect button — pulse animation when connected */
.gpu-connect-btn {
    position: relative;
}
.gpu-connect-btn.connected {
    color: #00ff88;
    border-color: #00ff8840;
    animation: gpu-pulse 2s ease-in-out infinite;
}
.gpu-connect-btn.connecting {
    color: #ffaa00;
    animation: gpu-pulse 0.8s ease-in-out infinite;
}
@keyframes gpu-pulse {
    0%, 100% { box-shadow: none; }
    50% { box-shadow: 0 0 10px currentColor; }
}

/* v9.0: GPU status panel (inline, replaces modal) */
.gpu-status-panel {
    position: fixed;
    top: 50px;
    left: 60px;
    z-index: 150;
    background: var(--bg-panel);
    border: 1px solid var(--border-dim);
    border-radius: 8px;
    padding: 12px 16px;
    min-width: 220px;
    box-shadow: 0 0 20px rgba(0,212,255,0.1);
}
.gpu-status-header {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 0.82em;
    margin-bottom: 8px;
}
.gpu-status-dot {
    width: 10px;
    height: 10px;
    border-radius: 50%;
    background: #555;
    flex-shrink: 0;
    transition: background 0.3s, box-shadow 0.3s;
}
.gpu-status-dot.disconnected { background: #555; }
.gpu-status-dot.connecting { background: #ffaa00; box-shadow: 0 0 8px #ffaa0060; }
.gpu-status-dot.connected { background: #00ff88; box-shadow: 0 0 8px #00ff8860; }
.gpu-status-dot.error { background: #ff4444; box-shadow: 0 0 8px #ff444460; }
.gpu-stream-stats {
    font-family: monospace;
    font-size: 0.72em;
    color: var(--text-muted);
    margin: -2px 0 8px;
    white-space: nowrap;
    overflow-x: auto;
}
.gpu-server-info {
    margin-bottom: 10px;
    padding-bottom: 8px;
    border-bottom: 1px solid var(--border-dim);
}
.gpu-info-grid {
    display: grid;
    grid-template-columns: auto 1fr;
    gap: 3px 10px;
    font-size: 0.75em;
}
.gpu-info-label {
    color: var(--text-muted);
    text-transform: uppercase;
    font-size: 0.9em;
    letter-spacing: 0.3px;
}
.gpu-info-grid span:nth-child(even) {
    color: var(--text-primary);
    font-family: monospace;
}
.gpu-reconnect-btn {
    width: 100%;
    background: var(--bg-control);
    color: var(--accent);
    border: 1px solid var(--accent);
    padding: 6px 12px;
    border-radius: 4px;
    font-size: 0.78em;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    cursor: pointer;
    transition: all 0.2s;
}
.gpu-reconnect-btn:hover {
    background: rgba(0,212,255,0.1);
    box-shadow: 0 0 10px var(--accent-dim);
}
.gpu-host-row {
    display: flex;
    gap: 6px;
    margin-top: 8px;
}
.gpu-host-input {
    flex: 1;
    background: var(--bg-control);
    color: var(--text-primary);
    border: 1px solid var(--border-color);
    border-radius: 4px;
    padding: 5px 8px;
    font-family: monospace;
    font-size: 0.75em;
    outline: none;
    transition: border-color 0.2s;
}
.gpu-host-input:focus {
    border-color: var(--accent);
    box-shadow: 0 0 6px var(--accent-dim);
}
.gpu-host-input::placeholder {
    color: var(--text-muted, #555);
}
.gpu-connect-btn-inline {
    background: var(--bg-control);
    color: var(--accent);
    border: 1px solid var(--accent);
    padding: 5px 10px;
    border-radius: 4px;
    font-size: 0.75em;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    cursor: pointer;
    white-space: nowrap;
    transition: all 0.2s;
}
.gpu-connect-btn-inline:hover {
    background: rgba(0,212,255,0.1);
    box-shadow: 0 0 8px var(--accent-dim);
}
.gpu-connect-btn-inline:disabled {
    opacity: 0.4;
    cursor: not-allowed;
}
.gpu-host-status {
    font-size: 0.7em;
    margin-top: 4px;
    min-height: 1em;
    color: var(--text-secondary, #888);
    font-family: monospace;
}
.gpu-host-status.success { color: #00ffaa; }
.gpu-host-status.error { color: #ff4466; }
.gpu-current-ip {
    margin-left: auto;
    font-family: monospace;
    font-size: 0.85em;
    color: var(--text-muted);
    opacity: 0.7;
}
.gpu-config-section {
    margin-top: 8px;
    padding-top: 8px;
    border-top: 1px solid var(--border-dim);
}
.gpu-test-btn {
    background: var(--bg-control);
    color: #ffaa00;
    border: 1px solid #ffaa00;
    padding: 5px 10px;
    border-radius: 4px;
    font-size: 0.75em;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    cursor: pointer;
    white-space: nowrap;
    transition: all 0.2s;
}
.gpu-test-btn:hover {
    background: rgba(255,170,0,0.1);
    box-shadow: 0 0 8px rgba(255,170,0,0.3);
}
.gpu-test-btn:disabled {
    opacity: 0.4;
    cursor: not-allowed;
}
.gpu-probe-result {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 0.72em;
    font-family: monospace;
    margin-top: 6px;
    padding: 4px 8px;
    border-radius: 4px;
    background: rgba(0,0,0,0.2);
}
.gpu-probe-icon {
    font-size: 1.1em;
    flex-shrink: 0;
}
.gpu-probe-result.success { color: #00ffaa; border-left: 2px solid #00ffaa; }
.gpu-probe-result.error { color: #ff4466; border-left: 2px solid #ff4466; }
.gpu-probe-result.warn { color: #ffaa00; border-left: 2px solid #ffaa00; }
.gpu-probe-result.testing { color: var(--text-secondary); border-left: 2px solid var(--text-secondary); }

/* ============================
   HQ Render Engine (v10.0)
   ============================ */
.hq-btn {
    background: var(--bg-control);
    color: var(--accent);
    border: 1px solid var(--accent);
    font-weight: 600;
    letter-spacing: 0.3px;
    transition: all 0.2s;
}
.hq-btn:hover:not(:disabled) {
    background: rgba(0,212,255,0.1);
    box-shadow: 0 0 10px var(--accent-dim);
}
.hq-btn:disabled {
    opacity: 0.4;
    cursor: not-allowed;
}
.hq-progress-bar {
    width: 100%;
    height: 6px;
    background: var(--bg-control);
    border-radius: 3px;
    overflow: hidden;
    border: 1px solid var(--border-dim);
}
.hq-progress-fill {
    height: 100%;
    width: 0%;
    background: linear-gradient(90deg, var(--accent), #00ffaa);
    border-radius: 3px;
    transition: width 0.3s ease;
}
.hq-gallery-item {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 4px 0;
    border-bottom: 1px solid var(--border-dim);
}
.hq-gallery-item:last-child {
    border-bottom: none;
}
.hq-gallery-thumb {
    width: 48px;
    height: 36px;
    border-radius: 3px;
    object-fit: cover;
    background: var(--bg-control);
    flex-shrink: 0;
}
.hq-gallery-info {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
}
.hq-gallery-download {
    color: var(--accent);
    flex-shrink: 0;
    padding: 4px;
    transition: opacity 0.2s;
}
.hq-gallery-download:hover {
    opacity: 0.7;
}
.hq-gallery-badge {
    display: inline-block;
    font-size: 0.65em;
    padding: 1px 5px;
    border-radius: 3px;
    letter-spacing: 0.06em;
    font-weight: 600;
}
.hq-badge-video {
    background: rgba(0, 212, 255, 0.15);
    color: var(--accent);
    border: 1px solid rgba(0, 212, 255, 0.3);
}
.hq-badge-image {
    background: rgba(0, 255, 170, 0.15);
    color: #00ffaa;
    border: 1px solid rgba(0, 255, 170, 0.3);
}
.hq-badge-audio {
    color: #00ffaa;
    opacity: 0.8;
    line-height: 1;
}
/* Flash animation on Anim tab icon when render completes */
@keyframes hq-flash {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.3; box-shadow: 0 0 8px var(--accent); }
}
.hq-complete-flash {
    animation: hq-flash 0.6s ease 3;
}

@media (max-width: 768px) {
    /* v12.2.1: was `.gpu-connect-btn { display: none; }` — a stale pre-v12
       rule (predates the icon rail flowing into the sheet's horizontal tab
       bar) that hid the ONLY entry point to the GPU panel on mobile. Removed
       so the panel is reachable at all on phones. */
    .gpu-status-panel {
        left: 10px; right: 10px; min-width: auto;
        top: auto; bottom: calc(104px + 12px);
        max-height: 40vh; overflow-y: auto;
    }
    /* Readable single-column info grid (was auto/1fr at 0.75em — unreadable
       and wrapped awkwardly under 360px) */
    .gpu-info-grid {
        grid-template-columns: 1fr;
        gap: 2px 0;
        font-size: 0.85em;
    }
    .gpu-info-label { font-size: 0.95em; margin-top: 4px; }
    .gpu-info-grid span:nth-child(even) { padding-bottom: 2px; }
    /* These rules sit after the unconditional .gpu-host-input/.gpu-test-btn/
       .gpu-connect-btn-inline declarations above, so they win the cascade at
       equal specificity (source order) without needing !important. */
    .gpu-host-input {
        font-size: 16px; /* absolute, not em — kills iOS auto-zoom on focus
            regardless of the inherited ancestor font-size */
        min-height: 44px;
        padding: 8px 10px;
    }
    .gpu-test-btn, .gpu-connect-btn-inline {
        min-height: 44px;
        padding: 10px 14px;
        font-size: 0.85em;
    }
}

@media (max-width: 500px) {
    /* Input+Test+Apply left <70px for the IP field at 375px — stack input
       on its own line, Test/Apply share the second line 50/50. */
    .gpu-host-row { flex-wrap: wrap; }
    .gpu-host-input { flex: 1 1 100%; }
    .gpu-test-btn, .gpu-connect-btn-inline { flex: 1 1 0; }
}

@media (max-width: 400px) {
    /* v13.6.1: param-row dials (two-per-row density, sweep buttons already
       compact) stay tight at 48px; standalone dials (Fly Speed, Color Cycle
       Speed, Play Speed…) get a bit more room since they aren't paired —
       narrower than that clipped multi-word labels even with the relative
       max-width fix above. */
    .param-slider-row .dial-cell { width: 48px; }
    .dial-cell { width: 58px; }
}

@media (max-width: 360px) {
    /* v14: keep all 9 rail buttons visible on very narrow phones
       (9×32px + 8×2px gap = 304px — fits a 320px viewport) */
    .icon-rail-tabs { gap: 2px; }
    .icon-rail-tabs .icon-btn { width: 32px; height: 32px; }
}

/* ============================
   v12.5 Collapsible binding/LFO cards (Music tab UX)
   ============================ */
.binding-card .binding-header,
.lfo-card .binding-header {
    display: flex; justify-content: flex-start; align-items: center;
    gap: 6px; margin-bottom: 4px; cursor: pointer;
    min-height: 24px;
}
.binding-card.collapsed .binding-body,
.lfo-card.collapsed .binding-body { display: none; }
.binding-card.collapsed .ctl-card-chevron::after,
.lfo-card.collapsed .ctl-card-chevron::after { transform: rotate(-90deg); }
.binding-card.collapsed .binding-header,
.lfo-card.collapsed .binding-header { margin-bottom: 0; }

.binding-dot {
    width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0;
    box-shadow: 0 0 6px currentColor;
}
/* magenta = LFO accent (matches .lfo-card::before) */
.lfo-dot { background: #ff00ff; color: #ff00ff; }

.binding-card .binding-header .binding-summary,
.lfo-card .binding-header .binding-summary {
    flex: 1; min-width: 0;
    font-size: 0.72em; color: var(--text-primary);
    text-transform: none; letter-spacing: 0;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
    text-shadow: none;
}

.binding-meter {
    width: 34px; height: 4px; flex-shrink: 0;
    background: rgba(255,255,255,0.08); border-radius: 2px; overflow: hidden;
}
.binding-meter-fill {
    display: block; height: 100%; width: 0;
    background: var(--accent); border-radius: 2px;
}

/* The legacy `.binding-card .binding-header span` rule (accent glow, 0.7em,
   uppercase) was written for the old "#N" label — keep it off the chevron
   host span so Music-tab chevrons match every other card's (v12.5.1) */
.binding-card .binding-header .ctl-card-chevron,
.lfo-card .binding-header .ctl-card-chevron {
    text-shadow: none; font-size: 1em; letter-spacing: 0;
}

.bind-mute {
    background: none; border: none; cursor: pointer;
    font-size: 0.8em; padding: 2px 4px; line-height: 1;
    filter: grayscale(0.3); opacity: 0.85; flex-shrink: 0;
}
.bind-mute:hover { opacity: 1; }
.binding-card.muted { opacity: 0.5; }
.binding-card.muted .binding-meter-fill { background: var(--text-muted); }

/* ============================
   v12.6 Fly-mode FAB (touch devices only) — stack item, hidden by default
   so it takes no space in #fab-stack's flex flow except on coarse pointers
   ============================ */
.fly-fab {
    display: none;
}
@media (pointer: coarse) {
    .fly-fab { display: flex; }
}
.fly-fab.active {
    color: var(--accent); border-color: var(--accent);
    box-shadow: var(--accent-glow);
    background: rgba(0, 212, 255, 0.08);
}

/* ============================
   v13.5: Auto-rotate FAB
   ============================ */
.rotate-fab.active {
    color: var(--accent); border-color: var(--accent);
    box-shadow: var(--accent-glow);
    background: rgba(0, 212, 255, 0.08);
}

/* ============================
   v13.5: Play/pause FAB for file/radio audio playback — hidden (and takes
   no stack space) until a file/radio source is active (ui.js toggles the
   .visible class)
   ============================ */
.audio-fab {
    display: none;
}
.audio-fab.visible { display: flex; }

/* v13.8b: preset-next FAB (mirrors audio-fab visibility pattern) — visible
   only while audio is active AND audio-reactive is on. */
.preset-fab {
    display: none;
}
.preset-fab.visible { display: flex; }
/* Transient name flash pinned near the FAB stack; fades in/out via .visible */
.preset-fab-flash {
    position: fixed;
    top: 12px;
    left: 68px;       /* right edge of fab-stack col + gap */
    padding: 6px 10px;
    background: rgba(5, 8, 15, 0.85);
    border: 1px solid var(--accent, #00d4ff);
    border-radius: 4px;
    color: var(--text, #d7e6ee);
    font: 11px/1 system-ui, sans-serif;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    pointer-events: none;
    opacity: 0;
    transform: translateY(-4px);
    transition: opacity 180ms ease, transform 180ms ease;
    z-index: 12;
    white-space: nowrap;
    max-width: 60vw;
    overflow: hidden;
    text-overflow: ellipsis;
}
.preset-fab-flash.visible {
    opacity: 1;
    transform: translateY(0);
}
@media (max-width: 768px) {
    .preset-fab-flash { left: 60px; top: 8px; font-size: 10px; }
}

/* ============================
   v13.5: Navigation FAB + popover (look/move sensitivity, in-place zoom,
   GPU focal length, cinematic dive/pullback/center). v13.7: popover
   position is set inline by ui.js anchorPopoverBelowStack() — it always
   opens below the whole #fab-stack, so its CSS top/left below are only the
   pre-JS fallback.
   ============================ */
@media (max-width: 768px) {
    .nav-popover {
        width: calc(100vw - 24px);
        max-width: 280px;
        overflow-y: auto;
    }
}

/* Compact dials so Look/Move/Zoom/Focal fit the 240px popover width
   (mirrors the .param-slider-row 54px compaction) */
.nav-popover .dial-cell { width: 54px; padding: 2px 0; }
.nav-popover .dial-knob { width: 44px; height: 44px; }
.nav-popover .dial-label { max-width: 54px; font-size: 0.56em; }
.nav-popover .dial-value { font-size: 0.56em; }

/* 4 cinematic buttons wrap 2x2 — "Pull back" doesn't fit 4-across at 240px */
.nav-cine-row { flex-wrap: wrap; }
.nav-cine-row .btn { flex: 1 1 calc(50% - 3px); }

/* ============================
   v13.6.1: Audio diagnostics disclosure (Music tab)
   audio.js's diag line + test-tone button are load-bearing debug doctrine
   (see CLAUDE.md) — kept reachable, just tucked behind a collapsed-by-
   default row instead of shouting under Audio Source. IDs/1s-poll wiring
   are untouched; audio.js only changed WHERE it appends #audio-diag /
   #audio-test-tone (into #diag-disclosure-body instead of after
   #audio-status).
   ============================ */
.diag-disclosure { margin-top: 2px; }
.diag-toggle {
    display: flex; align-items: center; gap: 4px;
    background: none; border: none; padding: 2px 0;
    font-family: inherit; font-size: 0.68em;
    color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.06em;
    cursor: pointer;
}
.diag-toggle:hover { color: var(--text-secondary); }
.diag-toggle-chevron {
    display: inline-block; font-size: 0.9em;
    transition: transform 0.2s;
}
.diag-disclosure.expanded .diag-toggle-chevron { transform: rotate(90deg); }
.diag-body { margin-top: 2px; }

/* ============================
   v13.6.1: Journey tab empty states — muted rather than bare
   ============================ */
.journey-hint {
    font-size: 0.72em; color: var(--text-secondary); margin-top: 4px;
}
/* Placeholder option ("-- Select Journey --") reads muted; a real
   selection restores the select's normal text color. */
#journey-preset-select:has(option[value=""]:checked) { color: var(--text-muted); }

/* ============================
   v13.8: Per-section 🎲 randomize buttons + canvas FAB
   ============================ */
/* Sits in .ctl-card-header between title and chevron. margin-left:auto
   pushes it to the right so the chevron stays flush. Header click still
   toggles collapse — initCards() skips clicks on .randomize-btn (same
   guard as .hdr-toggle). */
.randomize-btn {
    background: transparent;
    border: 1px solid transparent;
    color: var(--accent);
    font-size: 14px; line-height: 1;
    cursor: pointer;
    opacity: 0.55;
    padding: 3px 6px;
    margin-left: auto; margin-right: 6px;
    border-radius: 4px;
    transition: opacity 0.15s ease, background 0.15s, filter 0.15s, transform 0.4s;
    user-select: none;
}
.randomize-btn:hover {
    opacity: 1;
    background: rgba(0, 212, 255, 0.08);
    filter: drop-shadow(0 0 4px var(--accent));
}
.randomize-btn:active {
    transform: scale(0.85);
    background: rgba(0, 212, 255, 0.15);
}
.randomize-btn.spinning { animation: dice-spin 0.5s cubic-bezier(0.15, 0.9, 0.3, 1); }
@keyframes dice-spin {
    0%   { transform: rotate(0deg) scale(1); }
    50%  { transform: rotate(360deg) scale(1.15); }
    100% { transform: rotate(720deg) scale(1); }
}
/* Inline randomize header row for Shape → Params (no ctl-card wrapper) */
.params-header {
    display: flex; align-items: center; justify-content: space-between;
    padding: 4px 0 2px;
    font-size: 0.72em; color: var(--text-primary);
    text-transform: uppercase; letter-spacing: 0.06em;
}
.params-header .randomize-btn {
    margin-left: auto; margin-right: 0;
}

/* Master canvas FAB — reuses .bg-fab dimensions but with an emoji glyph.
   .bg-fab already handles fullscreen/hq-render hide via #fab-stack rule. */
#randomize-fab {
    font-size: 20px; line-height: 1;
    display: flex; align-items: center; justify-content: center;
    padding: 0;
}
#randomize-fab.spinning { animation: dice-spin 0.6s cubic-bezier(0.15, 0.9, 0.3, 1); }

/* v13.9: Record FAB + popover ------------------------------------------- */
/* The FAB reuses .bg-fab shape; a red-pulsing state overlays a solid dot
   when local HQ recording is active (matches #recording-indicator pulse
   from v13.7 for visual continuity across surfaces). */
@keyframes record-fab-pulse {
    0%, 100% { box-shadow: 0 0 0 0 rgba(255, 68, 68, 0.55); }
    50%      { box-shadow: 0 0 0 8px rgba(255, 68, 68, 0); }
}
.record-fab.recording {
    border-color: #ff4444 !important;
    animation: record-fab-pulse 1.4s ease-out infinite;
}
.record-fab.recording .record-fab-idle { display: none; }
.record-fab.recording .record-fab-live { display: block !important; }

/* Popover inherits .bg-popover base; the sections give it structured
   vertical rhythm and the title-note is muted so scan order is clear. */
.record-popover { min-width: 220px; }
.record-section { padding: 4px 0; }
.record-section-title {
    font-size: 0.75em; text-transform: uppercase; letter-spacing: 0.06em;
    color: var(--text-primary); margin-bottom: 6px;
}
.record-section-note {
    text-transform: none; letter-spacing: 0;
    color: var(--text-muted); font-weight: normal; margin-left: 4px;
    font-size: 0.9em;
}
.record-popover .capture-setting { margin-bottom: 6px; }
.record-popover .btn-row { display: flex; gap: 6px; }
.record-popover .btn-row .btn { flex: 1; }
.record-popover .capture-hint {
    font-size: 0.7em; color: var(--text-muted);
    margin-top: 4px; line-height: 1.3;
}

/* Cloud-deploy CTA — cyan-accent, discoverable but not shouty */
#rec-fab-deploy-gpu-btn {
    width: 100%;
    background: linear-gradient(135deg,
        rgba(0, 212, 255, 0.14), rgba(0, 212, 255, 0.06));
    border-color: rgba(0, 212, 255, 0.4);
    color: var(--accent);
}
#rec-fab-deploy-gpu-btn:hover {
    background: linear-gradient(135deg,
        rgba(0, 212, 255, 0.22), rgba(0, 212, 255, 0.10));
    border-color: rgba(0, 212, 255, 0.7);
}

.rec-cloud-reconnecting {
    padding: 8px 4px;
    font-size: 0.8em;
    color: var(--text-muted);
    text-align: center;
    opacity: 0.7;
}
