/* ====================================================
   Video Sync tool styles for /tools/video-sync/

   Loaded after /css/site.css. Shared design tokens, base reset,
   and body baseline come from site.css. This file contains the
   site chrome (nav, hero, footer), the shared component styles
   the Video Sync UI uses, and the tool-specific selectors that
   exist only here (dual waveform, nudge buttons, crop overlay,
   clip timeline and panel, aspect picker).

   Some component classes (drop-zone, option-btn, slider-card,
   preview-card, btn-primary, btn-secondary) are also used by the
   other standalone tools. They live in each tool's stylesheet for
   now; a later cleanup can move shared component styles into a
   dedicated shared file.
   ==================================================== */


/* ====================================================
   Drifted palette (matches /tools/index.html)

   The Video Sync page uses the same drifted palette as the tabs
   page and the other standalone tool pages so a user moving
   between them sees no visual jump. The DRIFT comments and the
   canonicalization plan in specs/DESIGN-tools-page-extraction.md
   apply here too.
   ==================================================== */

/* DRIFT: differs from /css/site.css canonical. Resolve in the
   palette canonicalization task tracked in TASKS.md. */
:root {
  --void: #0a0a0b;
  --ink: #141416;
  --ink-light: #1e1e22;
  --ink-raised: #28282d;
  --parchment: #e8e0d0;
  --parchment-dim: rgba(232,224,208,0.65);
  --parchment-faint: rgba(232,224,208,0.35);
  --parchment-trace: rgba(232,224,208,0.12);
  --hazel: #6b7c4a;
  --hazel-glow: rgba(107,124,74,0.12);
  --hazel-dim: rgba(107,124,74,0.4);
  --gold: #c9a84c;
  --gold-bright: #d9b85c;
  --gold-dim: #7a6530;
  --gold-faint: rgba(201,168,76,0.10);
  --gold-glow: rgba(201,168,76,0.25);
  --labradorite: #5878a0;
  --rule: rgba(201,168,76,0.15);
  --danger: #c97e7e;
}

/* DRIFT: see :root above. */
[data-theme="light"] {
  --void: #f4f0e8;
  --ink: #faf7f2;
  --ink-light: #ffffff;
  --ink-raised: #ebe4d4;
  --parchment: #28221a;
  --parchment-dim: rgba(40,34,26,0.72);
  --parchment-faint: rgba(40,34,26,0.45);
  --parchment-trace: rgba(40,34,26,0.18);
  --hazel: #4a6230;
  --hazel-glow: rgba(74,98,48,0.10);
  --hazel-dim: rgba(74,98,48,0.40);
  --gold: #9a6e10;
  --gold-bright: #b07f1c;
  --gold-dim: #6e5010;
  --gold-faint: rgba(154,110,16,0.08);
  --gold-glow: rgba(154,110,16,0.25);
  --labradorite: #385870;
  --rule: rgba(40,34,26,0.14);
  --danger: #b54848;
}

html, body {
  font-size: 1.05rem;
}

button { font-family: inherit; cursor: pointer; border: none; background: none; color: inherit; }
button:disabled { cursor: not-allowed; opacity: 0.4; }
input, select { font-family: inherit; }


/* ====================================================
   Theme toggle (behavior in /scripts/site.js)
   ==================================================== */
.theme-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  border-radius: 50%;
  border: 1px solid var(--rule);
  background: transparent;
  color: var(--parchment-dim);
  cursor: pointer;
  transition: color 0.25s, border-color 0.25s, transform 0.25s;
}
.theme-toggle:hover { color: var(--gold); border-color: var(--gold-dim); }
.theme-toggle:active { transform: scale(0.95); }
.theme-toggle svg { display: block; }
.theme-toggle .icon-moon { display: block; }
.theme-toggle .icon-sun { display: none; }
[data-theme="light"] .theme-toggle .icon-moon { display: none; }
[data-theme="light"] .theme-toggle .icon-sun { display: block; }


/* ====================================================
   Navigation, hero, privacy banner, main container
   ==================================================== */
.tool-nav {
  position: relative;
  z-index: 10;
  padding: 2rem 8vw;
  display: flex;
  justify-content: space-between;
  align-items: center;
}
.tool-nav a { color: var(--parchment-faint); text-decoration: none; font-size: 1.0rem; letter-spacing: 0.08em; transition: color 0.3s; }
.tool-nav a:hover { color: var(--hazel); }
.tool-nav .home { font-family: 'Cormorant Garamond', serif; font-size: 1.3rem; color: var(--parchment); letter-spacing: 0.05em; }
.tool-nav-right { display: flex; align-items: center; gap: 1.2rem; }

.tool-hero { padding: 4vh 8vw 5vh; text-align: center; border-bottom: 1px solid var(--rule); }
.tool-hero .eyebrow { display: inline-block; padding: 5px 16px; background: var(--gold-faint); color: var(--gold); font-size: 0.85rem; letter-spacing: 0.12em; text-transform: uppercase; border-radius: 20px; margin-bottom: 1.4rem; }
.tool-hero h1 { font-family: 'Cormorant Garamond', serif; font-weight: 300; font-size: clamp(2.4rem, 5vw, 3.6rem); line-height: 1.15; color: var(--parchment); margin-bottom: 1rem; letter-spacing: -0.005em; }
.tool-hero .build-stamp { font-family: 'Source Sans 3', sans-serif; font-size: 0.7rem; letter-spacing: 0.14em; text-transform: uppercase; color: var(--parchment-faint); font-variant-numeric: tabular-nums; margin: -0.6rem 0 0.9rem; }
.tool-hero .subtag { font-family: 'Source Sans 3', sans-serif; font-weight: 300; font-size: 1.15rem; color: var(--parchment-dim); max-width: 620px; margin: 0 auto; line-height: 1.65; }

.privacy-banner {
  max-width: 880px;
  margin: 2.4rem auto 0;
  padding: 0.85rem 1.5rem;
  display: flex;
  align-items: center;
  gap: 0.8rem;
  background: rgba(122,140,94,0.06);
  border: 0.5px solid var(--hazel-dim);
  border-radius: 999px;
  color: var(--parchment-dim);
  font-size: 0.92rem;
  line-height: 1.45;
  text-align: left;
}
.privacy-banner svg { color: var(--hazel); flex-shrink: 0; }
.privacy-banner strong { color: var(--parchment); font-weight: 500; }

main.tool { padding: 4vh 8vw 8vh; max-width: 1320px; margin: 0 auto; }
.section-tag { display: block; font-size: 0.78rem; letter-spacing: 0.18em; text-transform: uppercase; color: var(--parchment-faint); margin-bottom: 1rem; }
.tool-section { margin-bottom: 3rem; }
.helper-text { font-size: 0.95rem; color: var(--parchment-dim); margin-bottom: 1rem; line-height: 1.6; }


/* ====================================================
   File drop zones
   ==================================================== */
.upload-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 1rem; }
.drop-zone { background: rgba(244,241,234,0.025); backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px); border: 0.5px dashed var(--parchment-trace); border-radius: 8px; padding: 1.6rem 1.8rem; cursor: pointer; display: flex; align-items: center; gap: 1.1rem; transition: border-color 0.3s, background 0.3s; }
.drop-zone:hover, .drop-zone.dragover { border-color: var(--gold-glow); background: rgba(244,241,234,0.05); }
.drop-zone.has-file { border-style: solid; border-color: var(--hazel-dim); }
.drop-icon { width: 44px; height: 44px; display: flex; align-items: center; justify-content: center; background: var(--void); border-radius: 4px; border: 1px solid var(--rule); color: var(--parchment-faint); flex-shrink: 0; transition: color 0.3s, border-color 0.3s; }
.drop-zone.has-file .drop-icon { color: var(--gold); border-color: var(--gold-dim); }
.drop-text { flex: 1; min-width: 0; }
.drop-label { font-size: 0.78rem; letter-spacing: 0.16em; text-transform: uppercase; color: var(--parchment-faint); margin-bottom: 0.3rem; }
.drop-name { font-size: 1.0rem; color: var(--parchment-dim); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.drop-zone.has-file .drop-name { color: var(--parchment); }

.loading-row { display: flex; align-items: center; gap: 0.7rem; padding: 0.85rem 1.1rem; background: var(--ink); border-radius: 4px; color: var(--parchment-dim); font-size: 0.95rem; }
@keyframes spin { to { transform: rotate(360deg); } }
.spin { animation: spin 1s linear infinite; transform-origin: center; }


/* ====================================================
   Editor grid (preview area)
   ==================================================== */
.editor-grid { display: grid; grid-template-columns: 1fr; gap: 2.5rem; margin-bottom: 2.5rem; align-items: start; }
@media (max-width: 880px) { .editor-grid { gap: 2rem; } }


/* ====================================================
   Preview card and video frame
   ==================================================== */
.preview-card { background: rgba(244,241,234,0.03); border: 0.5px solid var(--parchment-trace); border-radius: 8px; padding: 1.4rem; display: flex; flex-direction: column; align-items: center; gap: 1rem; }
.preview-frame { background: #000; border-radius: 4px; overflow: hidden; box-shadow: 0 12px 50px rgba(0,0,0,0.55); position: relative; }
.preview-controls { display: flex; align-items: center; gap: 1rem; width: 100%; padding-top: 0.4rem; }
.play-btn { width: 46px; height: 46px; border-radius: 50%; background: var(--gold); color: var(--void); display: flex; align-items: center; justify-content: center; flex-shrink: 0; transition: background 0.2s, transform 0.1s; }
.play-btn:hover { background: var(--gold-bright); }
.play-btn:active { transform: scale(0.96); }
.play-btn svg { display: block; }
.time-display { flex: 1; }
.time-display .now { font-size: 1.0rem; color: var(--parchment); font-variant-numeric: tabular-nums; }
.time-display .dims { font-size: 0.78rem; letter-spacing: 0.1em; color: var(--parchment-faint); text-transform: uppercase; margin-top: 0.25rem; }

#syncVideoFrame { display: flex; align-items: center; justify-content: center; }
#syncVideoEl { background: #000; border-radius: 2px; }


/* ====================================================
   Sliders (shared shape for offset slider and clip fade)
   ==================================================== */
.slider-card { background: rgba(244,241,234,0.025); border: 0.5px solid var(--parchment-trace); border-radius: 6px; padding: 1.1rem 1.3rem; }
.slider-card-row { display: flex; justify-content: space-between; align-items: baseline; margin-bottom: 0.85rem; }
.slider-card-label { font-size: 0.78rem; letter-spacing: 0.12em; text-transform: uppercase; color: var(--parchment-dim); }
.slider-card-value { font-family: 'Cormorant Garamond', serif; font-size: 1.4rem; color: var(--gold); font-variant-numeric: tabular-nums; }
.slider-card-extremes { display: flex; justify-content: space-between; margin-top: 0.5rem; font-size: 0.75rem; color: var(--parchment-faint); letter-spacing: 0.04em; }

input[type="range"] { -webkit-appearance: none; appearance: none; width: 100%; height: 2px; background: var(--rule); outline: none; border-radius: 1px; margin: 0.4rem 0; }
input[type="range"]::-webkit-slider-thumb { -webkit-appearance: none; appearance: none; width: 14px; height: 14px; border-radius: 50%; background: var(--gold); cursor: pointer; border: 2px solid var(--void); }
input[type="range"]::-moz-range-thumb { width: 14px; height: 14px; border-radius: 50%; background: var(--gold); cursor: pointer; border: 2px solid var(--void); }


/* ====================================================
   Nudge buttons for sub-step offset adjustments

   Positive nudge values shift the audio later relative to the
   video; negative values shift earlier. Sub-slider-step values
   like ±10ms exist because the offset slider's step is too coarse
   for hearing audio-video sync drift.
   ==================================================== */
.nudge-btn {
  flex: 1;
  min-width: 60px;
  padding: 0.4rem 0.6rem;
  background: transparent;
  color: var(--parchment-dim);
  border: 0.5px solid var(--parchment-trace);
  border-radius: 4px;
  font-size: 0.82rem;
  letter-spacing: 0.04em;
  font-variant-numeric: tabular-nums;
  transition: all 0.2s;
}
.nudge-btn:hover { color: var(--gold); border-color: var(--gold-dim); }
.nudge-btn:active { transform: scale(0.96); }

.apply-prev-btn {
  width: 100%;
  margin-top: 0.6rem;
  padding: 0.65rem 1rem;
  background: transparent;
  color: var(--labradorite);
  border: 0.5px solid var(--labradorite);
  border-radius: 5px;
  font-size: 0.82rem;
  letter-spacing: 0.06em;
  text-align: center;
  transition: all 0.2s;
  font-family: inherit;
  cursor: pointer;
}
.apply-prev-btn:hover { color: var(--parchment); border-color: var(--parchment-faint); background: rgba(88,120,160,0.1); }


/* ====================================================
   Play-extracted-audio button: small secondary verifier
   that plays back the audio that Strategy 1 decoded or
   Strategy 2 captured during analysis. Helps the user
   confirm the envelope was built from the right signal.
   ==================================================== */
.play-extracted-btn {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  margin-top: 0.5rem;
  padding: 0.35rem 0.7rem;
  background: transparent;
  color: var(--parchment-faint);
  border: 0.5px solid var(--parchment-trace);
  border-radius: 4px;
  font-size: 0.75rem;
  letter-spacing: 0.04em;
  font-family: inherit;
  cursor: pointer;
  transition: color 0.2s, border-color 0.2s, background 0.2s;
}
.play-extracted-btn:hover { color: var(--gold); border-color: var(--gold-dim); }
.play-extracted-btn.playing { color: var(--gold); border-color: var(--gold-dim); background: var(--gold-faint); }
.play-extracted-btn svg { flex-shrink: 0; }


/* ====================================================
   Dual waveform display

   Shows the video's audio (top half, hazel) and the external
   audio (bottom half, gold) shifted by the current offset. When
   the two halves line up visually, the audio sources are in sync.
   ==================================================== */
.dual-waveform-wrap {
  position: relative;
  background: var(--ink);
  border: 1px solid var(--rule);
  border-radius: 10px;
  padding: 12px;
  overflow: hidden;
}
.dual-waveform-canvas {
  display: block;
  width: 100%;
  height: 160px;
  border-radius: 4px;
}
.dual-waveform-labels {
  display: flex;
  justify-content: space-between;
  margin-top: 8px;
  font-size: 0.78rem;
  color: var(--parchment-faint);
  letter-spacing: 0.06em;
}
.dual-waveform-labels span:first-child { color: var(--hazel); }
.dual-waveform-labels span:last-child { color: var(--gold); }


/* ====================================================
   Crop overlay: shows what portion of the source video a clip
   will use when the export aspect ratio differs from the source
   aspect ratio. The gold-bordered crop-box is draggable.
   ==================================================== */
.crop-overlay { position: absolute; inset: 0; pointer-events: none; }
.crop-overlay.active { pointer-events: auto; }
.crop-shade { position: absolute; inset: 0; background: rgba(0,0,0,0.45); }
.crop-box { position: absolute; border: 2px solid var(--gold); box-shadow: 0 0 0 9999px rgba(0,0,0,0.45); cursor: move; touch-action: none; }
.crop-box-label { position: absolute; bottom: -22px; left: 0; font-size: 0.68rem; color: var(--gold); white-space: nowrap; background: var(--void); padding: 1px 4px; border-radius: 2px; pointer-events: none; }


/* ====================================================
   Aspect ratio picker per clip
   ==================================================== */
.aspect-grid { display: grid; grid-template-columns: repeat(5, 1fr); gap: 0.3rem; margin-top: 0.5rem; }
.aspect-btn { padding: 0.45rem 0.2rem; background: rgba(244,241,234,0.025); color: var(--parchment-dim); border: 0.5px solid var(--parchment-trace); border-radius: 4px; font-size: 0.75rem; text-align: center; cursor: pointer; transition: all 0.15s; font-family: inherit; }
.aspect-btn:hover { color: var(--parchment); border-color: var(--hazel-dim); }
.aspect-btn.active { color: var(--parchment); border-color: var(--gold-dim); background: var(--gold-faint); }


/* ====================================================
   Clip timeline track

   Horizontal bar representing the full video duration. Each clip
   is a draggable region within the track; the region's left and
   right edges have handles for resizing, and the interior drags
   to slide the clip within the source video.
   ==================================================== */
.clip-timeline-wrap { margin-top: 0.8rem; }
.clip-track { position: relative; height: 56px; background: var(--ink-raised); border-radius: 4px; overflow: visible; cursor: pointer; user-select: none; touch-action: none; border: 0.5px solid var(--parchment-trace); }
.clip-track-inner { position: absolute; inset: 0; overflow: hidden; border-radius: 3px; }
.clip-region { position: absolute; top: 0; bottom: 0; z-index: 2; }
.clip-region.selected { background: var(--gold-glow); border: 1.5px solid var(--gold); }
.clip-region.unselected { background: var(--hazel-glow); border: 1.5px solid var(--hazel-dim); }
.clip-region:hover { cursor: grab; }
.clip-region:active { cursor: grabbing; }
.clip-region-label { position: absolute; top: -20px; left: 2px; font-size: 0.68rem; white-space: nowrap; color: var(--parchment-dim); pointer-events: none; }
.clip-region-label.selected { color: var(--gold); }
.clip-handle { position: absolute; top: 0; bottom: 0; width: 14px; cursor: ew-resize; z-index: 4; }
.clip-handle.left { left: -2px; }
.clip-handle.right { right: -2px; }
.clip-track-ticks { position: relative; height: 18px; margin-top: 3px; }
.clip-track-ticks span { position: absolute; font-size: 0.68rem; color: var(--parchment-faint); transform: translateX(-50%); }


/* ====================================================
   Add Clip buttons and the clips list + clip detail panel
   ==================================================== */
.clip-actions { display: flex; gap: 0.5rem; margin-top: 1rem; flex-wrap: wrap; align-items: center; }
.clip-panel { display: flex; gap: 1.2rem; margin-top: 1.2rem; }
.clips-list { flex: 0 0 220px; display: flex; flex-direction: column; gap: 0.4rem; }
.clip-row { display: flex; align-items: center; gap: 0.6rem; padding: 0.5rem 0.7rem; border-radius: 4px; border: 1px solid var(--rule); cursor: pointer; transition: border-color 0.15s, background 0.15s; position: relative; }
.clip-row.selected { border-color: var(--gold-dim); background: var(--gold-faint); }
.clip-row:hover { border-color: var(--parchment-trace); }
.clip-row:hover .clip-row-delete { opacity: 1; }
.clip-row-num { font-size: 0.72rem; color: var(--gold); font-weight: 500; min-width: 14px; text-align: center; }
.clip-row-info { flex: 1; min-width: 0; }
.clip-row-label { font-size: 0.82rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.clip-row-meta { font-size: 0.7rem; color: var(--parchment-dim); }
.clip-row-delete { position: absolute; right: 5px; top: 50%; transform: translateY(-50%); opacity: 0; color: var(--danger); font-size: 1.05rem; line-height: 1; padding: 2px 5px; transition: opacity 0.15s; }
.clip-detail { flex: 1; min-width: 0; }
.clip-detail-empty { color: var(--parchment-faint); font-size: 0.85rem; padding: 0.8rem 0; }
.clip-time-row { display: flex; gap: 0.6rem; margin-top: 0.6rem; flex-wrap: wrap; }
.clip-time-field { flex: 1; min-width: 110px; }
.clip-time-field label { display: block; font-size: 0.72rem; color: var(--parchment-dim); margin-bottom: 0.25rem; text-transform: uppercase; letter-spacing: 0.08em; }
.clip-time-field input { width: 100%; padding: 0.5rem 0.6rem; background: var(--ink-raised); border: 0.5px solid var(--parchment-trace); border-radius: 4px; color: var(--parchment); font-size: 0.88rem; font-family: inherit; outline: none; }
.clip-time-field input:focus { border-color: var(--gold-dim); }
.clip-label-row { margin-top: 0.7rem; }
.clip-label-row label { display: block; font-size: 0.72rem; color: var(--parchment-dim); margin-bottom: 0.25rem; text-transform: uppercase; letter-spacing: 0.08em; }
.clip-label-row input { width: 100%; padding: 0.5rem 0.6rem; background: var(--ink-raised); border: 0.5px solid var(--parchment-trace); border-radius: 4px; color: var(--parchment); font-size: 0.88rem; font-family: inherit; outline: none; }
.clip-label-row input:focus { border-color: var(--gold-dim); }
.clip-empty-hint { color: var(--parchment-faint); font-size: 0.82rem; padding: 0.5rem 0; }


/* ====================================================
   Export / render panel and result
   ==================================================== */
.export-card { background: rgba(244,241,234,0.025); border: 0.5px solid var(--parchment-trace); border-radius: 8px; padding: 1.8rem; display: flex; flex-direction: column; gap: 1.4rem; }
.export-header { display: flex; justify-content: space-between; align-items: flex-end; gap: 1.5rem; flex-wrap: wrap; }
.export-header .summary { flex: 1; min-width: 240px; }
.export-header .summary .tag { font-size: 0.78rem; letter-spacing: 0.18em; text-transform: uppercase; color: var(--parchment-faint); margin-bottom: 0.5rem; }
.export-header .summary .figure { font-family: 'Cormorant Garamond', serif; font-size: 1.7rem; color: var(--parchment); line-height: 1.2; }
.export-header .summary .figure .secondary { color: var(--parchment-faint); font-size: 1.2rem; }
.btn-primary { padding: 0.95rem 1.8rem; background: var(--gold); color: var(--void); font-size: 0.85rem; letter-spacing: 0.18em; text-transform: uppercase; font-weight: 500; border-radius: 4px; display: inline-flex; align-items: center; gap: 0.7rem; transition: background 0.2s, transform 0.1s; }
.btn-primary:hover:not(:disabled) { background: var(--gold-bright); }
.btn-primary:active:not(:disabled) { transform: scale(0.98); }
.btn-secondary { padding: 0.85rem 1.4rem; background: transparent; color: var(--gold); border: 1px solid var(--gold-dim); border-radius: 4px; text-decoration: none; display: inline-flex; align-items: center; gap: 0.55rem; font-size: 0.85rem; letter-spacing: 0.12em; text-transform: uppercase; align-self: flex-start; transition: all 0.2s; }
.btn-secondary:hover { color: var(--void); background: var(--gold); border-color: var(--gold); }
.progress-track { height: 2px; background: var(--rule); border-radius: 1px; overflow: hidden; }
.progress-fill { height: 100%; background: var(--gold); transition: width 0.2s; width: 0; }
.error-card { padding: 0.9rem 1.2rem; background: rgba(201,126,126,0.08); border: 1px solid rgba(201,126,126,0.3); border-radius: 4px; font-size: 0.95rem; color: var(--danger); }

/* Informational prompt shown when video audio extraction is unavailable
   (most often Safari + certain MOV codecs). Different visual register
   than .error-card: hazel/parchment instead of danger red, with a small
   headphones cue to redirect the user toward the manual workflow. */
.decode-warning {
  padding: 0.85rem 1.1rem 0.85rem 2.6rem;
  background: rgba(122,140,94,0.06);
  border: 0.5px solid var(--hazel-dim);
  border-left: 3px solid var(--hazel);
  border-radius: 4px;
  font-size: 0.92rem;
  line-height: 1.55;
  color: var(--parchment-dim);
  position: relative;
}
.decode-warning::before {
  content: '';
  position: absolute;
  left: 1rem;
  top: 1rem;
  width: 14px;
  height: 14px;
  background: var(--hazel);
  mask-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2'><path d='M3 18v-6a9 9 0 0 1 18 0v6'/><path d='M21 19a2 2 0 0 1-2 2h-1v-7h3v5z'/><path d='M3 19a2 2 0 0 0 2 2h1v-7H3v5z'/></svg>");
  -webkit-mask-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2'><path d='M3 18v-6a9 9 0 0 1 18 0v6'/><path d='M21 19a2 2 0 0 1-2 2h-1v-7h3v5z'/><path d='M3 19a2 2 0 0 0 2 2h1v-7H3v5z'/></svg>");
  mask-size: contain;
  -webkit-mask-size: contain;
  mask-repeat: no-repeat;
  -webkit-mask-repeat: no-repeat;
}

/* When auto-align is unavailable, the manual offset slider IS the
   alignment workflow. Subtle gold accent on the slider-card draws
   the eye to it. */
#syncAlignmentSection.manual-only .slider-card {
  border-color: var(--gold-dim);
  background: var(--gold-faint);
}
.export-result { padding-top: 0.5rem; border-top: 1px solid var(--rule); display: flex; flex-direction: column; gap: 1rem; }
.export-result video { width: 100%; max-height: 480px; border-radius: 4px; background: #000; margin-top: 1rem; object-fit: contain; }
.export-note { font-size: 0.85rem; color: var(--parchment-faint); line-height: 1.55; }


/* ====================================================
   Empty state, footer, utility classes
   ==================================================== */
.placeholder-state { text-align: center; padding: 3rem 1rem; color: var(--parchment-dim); font-family: 'Cormorant Garamond', serif; font-size: 1.4rem; font-style: italic; }

footer.tool-footer { padding: 3rem 8vw; border-top: 1px solid var(--rule); display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 1.5rem; color: var(--parchment-faint); font-size: 0.95rem; }
footer.tool-footer a { color: var(--parchment-faint); text-decoration: none; letter-spacing: 0.06em; transition: color 0.3s; }
footer.tool-footer a:hover { color: var(--hazel); }
.footer-privacy {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  margin-left: 0.4rem;
  color: var(--hazel);
}
.footer-privacy svg { flex-shrink: 0; }

.visually-hidden { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0; }
.hidden { display: none !important; }


/* ====================================================
   Responsive
   ==================================================== */
@media (max-width: 768px) {
  .tool-nav { padding: 1.4rem 5vw; }
  main.tool { padding: 3vh 5vw 6vh; }
  .upload-row { grid-template-columns: 1fr; }
  .export-header { flex-direction: column; gap: 1rem; }
  .btn-primary { width: 100%; justify-content: center; }
  .clip-panel { flex-direction: column; }
  .clips-list { flex: none; max-height: 160px; overflow-y: auto; }
}

@media (max-width: 480px) {
  .nudge-btn { min-width: 52px; padding: 0.6rem 0.4rem; min-height: 44px; }
  .aspect-grid { grid-template-columns: repeat(3, 1fr); }
  .drop-zone { padding: 1.2rem 1.2rem; }
}


/* ====================================================
   Full-viewport editor shell
   (specs/DESIGN-timeline-preview-layout.md)

   When a video loads, video-sync.js sets body.editor-active and the
   editor (#syncEditor) becomes a full-viewport column: a slim top bar
   in place of the tall hero, a preview pane, a draggable divider, and a
   scrollable lower region holding the waveform, clips, alignment, and
   render controls. The waveform canvas and crop box recompute from the
   time model on resize and on divider drag (see the shell controller in
   video-sync.js). This is layout only: no palette change, no network
   behavior, and the load-state markup is untouched. The page-shaped
   chrome (nav, hero, privacy banner, load section, footer) stays in the
   DOM beneath the opaque shell.
   ==================================================== */

/* The split between preview and lower region, traded by the divider.
   The controller writes it here; it is held in memory for the session
   only and nothing about layout is written off the device. */
.sync-editor-shell { --sync-preview-h: 46%; }

body.editor-active { overflow: hidden; }

/* svh first with a dvh refinement, never vh, so the mobile URL bar
   causes neither a jump nor a scroll trap. */
body.editor-active .sync-editor-shell {
  position: fixed;
  inset: 0;
  z-index: 50;
  margin: 0;
  display: flex;
  flex-direction: column;
  background: var(--void);
  height: 100svh;
  height: 100dvh;
  overflow: hidden;
}

/* Slim top bar replacing the tall hero. */
.editor-topbar {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.7rem 8vw;
  border-bottom: 1px solid var(--rule);
}
.editor-topbar-id { display: flex; align-items: baseline; gap: 1rem; min-width: 0; }
.editor-tool-name { font-family: 'Cormorant Garamond', serif; font-size: 1.35rem; color: var(--parchment); letter-spacing: 0.04em; white-space: nowrap; }
.editor-privacy-line { display: inline-flex; align-items: center; gap: 0.4rem; font-size: 0.82rem; color: var(--parchment-faint); letter-spacing: 0.02em; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.editor-privacy-line svg { color: var(--hazel); flex-shrink: 0; }
.editor-topbar-actions { display: flex; align-items: center; gap: 1.2rem; flex-shrink: 0; }
.editor-back-link { color: var(--parchment-faint); text-decoration: none; font-size: 0.95rem; letter-spacing: 0.08em; transition: color 0.3s; white-space: nowrap; }
.editor-back-link:hover { color: var(--hazel); }

/* Flexible body: preview pane, divider, lower region. */
.editor-body { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; }

/* Preview pane: fixed-fraction height (set by the divider), the video
   centered and scaled to fit so resizing the pane rescales the video. */
.editor-preview-pane {
  flex: 0 0 auto;
  height: var(--sync-preview-h, 46%);
  min-height: 132px;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  padding: 0.7rem 8vw 0;
}
.editor-preview-pane .editor-grid { margin: 0; height: 100%; min-height: 0; display: flex; }
.editor-preview-pane .editor-grid > div { display: flex; flex-direction: column; height: 100%; min-height: 0; width: 100%; align-items: center; gap: 0.5rem; }
.editor-preview-pane .section-tag { flex: 0 0 auto; margin-bottom: 0.2rem; }
.editor-preview-pane .preview-card { flex: 1 1 0; min-height: 0; width: 100%; max-width: 760px; padding: 0.9rem; }
.editor-preview-pane .preview-controls { flex: 0 0 auto; }
/* The frame is grow-driven (flex-basis 0) so it fills the card's space minus the
   controls, giving it a definite height independent of the video's intrinsic
   size. The controls keep their natural height and stay in view. */
.editor-preview-pane .preview-frame { flex: 1 1 0; min-height: 0; width: 100%; max-width: 100%; display: flex; align-items: center; justify-content: center; }
/* The video fills the frame box and letterboxes with object-fit, taken out of
   flow so its intrinsic size never dictates the frame height. A portrait clip's
   tall natural height would otherwise push the frame (and the play controls
   below it) past the fixed-height, overflow-hidden pane and out of reach. The
   frame is sized purely by flex; the video scales to contain within it at any
   divider position and any aspect ratio. */
.editor-preview-pane #syncVideoEl { position: absolute; inset: 0; width: 100% !important; height: 100% !important; max-width: 100% !important; max-height: 100% !important; object-fit: contain; }

/* Divider: keyboard reachable drag handle that trades height. */
.editor-divider {
  flex: 0 0 auto;
  height: 12px;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: row-resize;
  background: var(--ink);
  border-top: 1px solid var(--rule);
  border-bottom: 1px solid var(--rule);
  touch-action: none;
}
.editor-divider:hover { background: var(--ink-raised); }
.editor-divider:focus-visible { outline: none; background: var(--ink-raised); box-shadow: inset 0 0 0 1px var(--gold-dim); }
.editor-divider-grip { width: 46px; height: 3px; border-radius: 2px; background: var(--parchment-trace); transition: background 0.2s; }
.editor-divider:hover .editor-divider-grip,
.editor-divider:focus-visible .editor-divider-grip { background: var(--gold); }

/* Lower region: full width, scrollable, holds the timeline and controls. */
.editor-lower {
  flex: 1 1 auto;
  min-height: 0;
  overflow: auto;
  padding: 1.4rem 8vw 3rem;
}
.editor-lower > .tool-section:first-of-type { margin-top: 0; }

.editor-smallscreen-note { display: none; }

/* Medium screens: tighter spacing, privacy line drops to save room. */
@media (max-width: 900px) and (min-width: 601px) {
  .editor-topbar { padding: 0.6rem 5vw; }
  .editor-preview-pane { padding: 0.6rem 5vw 0; }
  .editor-lower { padding: 1.1rem 5vw 3rem; }
  .editor-privacy-line { display: none; }
}

/* Phone: precise multi-track editing is impractical, so the shell
   degrades to a normal stacked, scrollable page with a quiet note. It
   never becomes a fixed full-viewport trap. */
@media (max-width: 600px) {
  body.editor-active { overflow: auto; }
  body.editor-active .sync-editor-shell {
    position: static;
    height: auto;
    min-height: 100svh;
    min-height: 100dvh;
    overflow: visible;
  }
  .editor-topbar { padding: 1rem 5vw; }
  .editor-privacy-line { display: none; }
  .editor-body { display: block; }
  .editor-preview-pane { height: auto; min-height: 0; overflow: visible; padding: 1rem 5vw 0; }
  .editor-preview-pane .preview-frame { flex: 0 0 auto; }
  /* Phone is a normal stacked, scrollable page, so the video returns to the
     in-flow, content-sized box (capped at 60vh); reset the desktop fill. */
  .editor-preview-pane #syncVideoEl { position: static; inset: auto; width: auto !important; height: auto !important; max-width: 100% !important; max-height: 60vh !important; object-fit: fill; }
  .editor-divider { display: none; }
  .editor-lower { overflow: visible; padding: 1.2rem 5vw 4rem; }
  .editor-smallscreen-note {
    display: block;
    margin: 0 0 1.2rem;
    padding: 0.6rem 0.9rem;
    font-size: 0.85rem;
    line-height: 1.5;
    color: var(--parchment-dim);
    background: var(--gold-faint);
    border: 0.5px solid var(--gold-dim);
    border-radius: 6px;
  }
}


/* ====================================================
   Unified timeline slice 1: master spine and multi-video lane
   (governance/STUDIO-TOOLS-ROADMAP.md, specs/DESIGN-unified-timeline.md)

   The master audio is the full-width spine; loaded videos sit on a
   lane above it as draggable blocks for manual placement. Reuses the
   site palette and the existing waveform and clip-track conventions:
   the spine canvas mirrors the dual-waveform wrap, the lane blocks
   mirror the clip regions. Positions are percent-based off the time
   model so they reflow on resize and divider drag without drift; the
   spine canvas is redrawn from the master envelope on width change.
   ==================================================== */

/* Determinate load progress bar for the large master read. */
.load-progress { margin-top: 1rem; }
.load-progress-head { display: flex; justify-content: space-between; font-size: 0.85rem; color: var(--parchment-dim); margin-bottom: 0.4rem; font-variant-numeric: tabular-nums; }
.load-progress-track { position: relative; height: 6px; background: var(--ink-raised); border: 0.5px solid var(--parchment-trace); border-radius: 3px; overflow: hidden; }
.load-progress-fill { height: 100%; width: 0; background: var(--gold); transition: width 0.15s linear; }
/* Indeterminate state for a no-progress phase (decodeAudioData on a compressed
   master): a gold sliver slides across to signal work, no fabricated percent. */
.load-progress-track.indeterminate .load-progress-fill {
  position: absolute;
  top: 0;
  width: 40%;
  transition: none;
  animation: load-indeterminate 1.1s ease-in-out infinite;
}
@keyframes load-indeterminate {
  0% { left: -40%; }
  100% { left: 100%; }
}

.timeline-wrap { margin-top: 0.6rem; position: relative; }
/* When zoomed the content is wider than the viewport, so the wrap scrolls
   horizontally. Only engaged past zoom 1, so the unzoomed view is unchanged.
   The bottom padding leaves room for the horizontal scrollbar below the ticks. */
.timeline-wrap.is-zoomed { overflow-x: auto; overflow-y: hidden; padding-bottom: 12px; }
.timeline-content { position: relative; width: 100%; }
/* Zoom controls: magnify the time axis to place a boundary precisely. */
.timeline-zoom-text { font-size: 0.78rem; letter-spacing: 0.06em; color: var(--parchment-faint); align-self: center; }
.timeline-zoom-level { font-size: 0.8rem; color: var(--parchment-dim); font-variant-numeric: tabular-nums; min-width: 2.4rem; text-align: center; align-self: center; }
.zoom-btn { font-size: 1rem; line-height: 1; padding: 0.35rem 0.7rem; min-width: 2.1rem; }

/* Video lane: blocks placed above the spine, percent-positioned on the time
   axis and stacked on rows by time-overlap. The lane height is set in JS from
   the row count (one row reads as the original 46px); past a cap it scrolls
   vertically so it never crowds the spine. */
.video-lane { position: relative; height: 46px; background: var(--ink-raised); border: 0.5px solid var(--parchment-trace); border-radius: 4px 4px 0 0; border-bottom: none; overflow-x: hidden; overflow-y: auto; user-select: none; touch-action: none; }
.video-lane-inner { position: relative; width: 100%; height: 100%; }
.video-block {
  position: absolute;
  min-width: 6px;
  background: var(--hazel-glow);
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  border: 1.5px solid var(--hazel-dim);
  border-radius: 3px;
  cursor: grab;
  display: flex;
  align-items: center;
  overflow: hidden;
}
.video-block:active { cursor: grabbing; }
.video-block:hover { border-color: var(--gold-dim); }
/* When a captured frame is the block background, a dark gradient sits
   behind the label so the filename stays legible over the image. */
.video-block.has-thumb::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(90deg, rgba(10,10,11,0.85), rgba(10,10,11,0.35) 55%, rgba(10,10,11,0.05));
  pointer-events: none;
  z-index: 1;
}
.video-block-label { position: relative; z-index: 2; font-size: 0.68rem; color: var(--parchment-dim); padding: 0 6px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; pointer-events: none; }
.video-block.has-thumb .video-block-label { color: #f2ece0; text-shadow: 0 1px 2px rgba(0,0,0,0.65); }

/* Brief confirmation that a dropped file was accepted. */
.editor-drop-confirm { margin: -0.6rem 0 1rem; font-size: 0.82rem; color: var(--hazel); letter-spacing: 0.02em; }

/* Master spine: the full-width waveform, drawn from the envelope. */
.spine-wrap {
  position: relative;
  background: var(--ink);
  border: 0.5px solid var(--rule);
  border-radius: 0 0 4px 4px;
  /* No horizontal padding: the canvas spans the full width so the spine,
     the lane blocks, and the playhead share one time-to-x axis. */
  padding: 12px 0;
  overflow: hidden;
}
.spine-canvas { display: block; width: 100%; height: 90px; border-radius: 2px; }
.spine-canvas:not(.hidden) { cursor: ew-resize; }

.timeline-ticks { position: relative; height: 18px; margin-top: 3px; }
.timeline-ticks span { position: absolute; font-size: 0.68rem; color: var(--parchment-faint); transform: translateX(-50%); white-space: nowrap; }

.video-lane-empty { font-size: 0.85rem; color: var(--parchment-faint); margin-top: 0.8rem; font-style: italic; }

/* Song segments (slice 4): proposed song spans drawn as translucent bands over
   the spine, on the same time-to-x axis. The layer overlays the spine canvas
   (top/bottom inset matches the spine-wrap's 12px padding). The layer ignores
   pointer events so spine scrubbing passes through; only the handles, label, and
   delete control are interactive. */
.segment-band-layer { position: absolute; left: 0; right: 0; top: 12px; height: 90px; pointer-events: none; z-index: 3; }
.segment-band {
  position: absolute;
  top: 0;
  bottom: 0;
  background: var(--gold-faint);
  border-top: 2px solid var(--gold);
  box-sizing: border-box;
}
.segment-band-label {
  position: absolute;
  top: 3px;
  left: 9px;
  font-size: 0.68rem;
  letter-spacing: 0.04em;
  color: var(--gold-bright);
  white-space: nowrap;
  text-shadow: 0 1px 2px rgba(0,0,0,0.8);
  pointer-events: none;
}
.segment-band-handle {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 9px;
  background: var(--gold-dim);
  cursor: ew-resize;
  pointer-events: auto;
  touch-action: none;
}
.segment-band-handle:hover { background: var(--gold); }
.segment-band-handle.start { left: 0; }
.segment-band-handle.end { right: 0; }
.segment-band-del {
  position: absolute;
  top: 2px;
  right: 11px;
  width: 16px;
  height: 16px;
  line-height: 14px;
  text-align: center;
  font-size: 0.85rem;
  color: var(--parchment-faint);
  background: rgba(10,10,11,0.55);
  border: none;
  border-radius: 3px;
  cursor: pointer;
  pointer-events: auto;
  font-family: inherit;
  padding: 0;
}
.segment-band-del:hover { color: var(--gold); background: rgba(10,10,11,0.8); }
.segment-band-dl {
  position: absolute;
  top: 2px;
  right: 31px;
  width: 18px;
  height: 16px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--parchment-faint);
  background: rgba(10,10,11,0.55);
  border: none;
  border-radius: 3px;
  cursor: pointer;
  pointer-events: auto;
  font-family: inherit;
  padding: 0;
}
.segment-band-dl:hover { color: var(--gold); background: rgba(10,10,11,0.8); }
.segment-band-vid {
  position: absolute;
  top: 2px;
  right: 51px;
  width: 18px;
  height: 16px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--parchment-faint);
  background: rgba(10,10,11,0.55);
  border: none;
  border-radius: 3px;
  cursor: pointer;
  pointer-events: auto;
  font-family: inherit;
  padding: 0;
}
.segment-band-vid:hover { color: var(--gold); background: rgba(10,10,11,0.8); }


/* ====================================================
   Slice 1 fix: add files from inside the editor

   Once the editor is open the full-viewport shell covers the page-shaped
   load zones, so file-adding is surfaced here: a file bar with pickers, a
   master drop target in the spine area when no master is loaded, and the
   whole editor as a drop target. Reuses the site palette and components.
   ==================================================== */

.editor-file-bar { display: flex; align-items: center; flex-wrap: wrap; gap: 0.6rem; margin-bottom: 1.4rem; }
.editor-file-bar-label { font-size: 0.78rem; letter-spacing: 0.16em; text-transform: uppercase; color: var(--parchment-faint); margin-right: 0.2rem; }
.editor-add-btn {
  padding: 0.45rem 0.9rem;
  background: transparent;
  color: var(--gold);
  border: 1px solid var(--gold-dim);
  border-radius: 4px;
  font-size: 0.78rem;
  letter-spacing: 0.06em;
  cursor: pointer;
  font-family: inherit;
  transition: color 0.2s, background 0.2s, border-color 0.2s;
}
.editor-add-btn:hover { color: var(--void); background: var(--gold); border-color: var(--gold); }
.editor-file-bar-hint { font-size: 0.8rem; color: var(--parchment-faint); font-style: italic; }

/* Master drop target shown in the spine area until a master is loaded. */
.spine-drop {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.7rem;
  min-height: 90px;
  padding: 0.8rem 1rem;
  text-align: center;
  color: var(--parchment-dim);
  border: 1px dashed var(--parchment-trace);
  border-radius: 4px;
  cursor: pointer;
  transition: color 0.2s, background 0.2s, border-color 0.2s;
}
.spine-drop svg { color: var(--gold); flex-shrink: 0; }
.spine-drop span { font-size: 0.9rem; max-width: 460px; line-height: 1.45; }
.spine-drop:hover, .spine-drop.dragover { color: var(--parchment); background: var(--gold-faint); border-color: var(--gold-glow); }

/* Editor-wide cue while files are dragged over it. */
.sync-editor-shell.drag-over { outline: 2px dashed var(--gold-dim); outline-offset: -8px; }


/* ====================================================
   Slice 2: auto-sync control and confidence cue

   The "Auto-sync to the master" control sits in the Timeline section and
   shows once a master and at least one video are loaded. A weak match
   gets a restrained dashed amber edge on its lane block so the user knows
   to check it. Reuses the site palette and the lane conventions.
   ==================================================== */

.timeline-actions { display: flex; align-items: center; flex-wrap: wrap; gap: 0.8rem; margin: 0.2rem 0 0.9rem; }
.timeline-actions-hint { font-size: 0.82rem; color: var(--parchment-dim); letter-spacing: 0.02em; }

/* Low or failed match: a dashed amber edge, distinct from the gold and
   hazel of placed blocks, signalling "check this one and drag it". */
.video-block.low-match { border: 1.5px dashed #d39a3c; }
.video-block.low-match:not(.has-thumb) .video-block-label { color: #d39a3c; }

/* Match score badge, surfaced after auto-sync for threshold tuning. */
.video-block-score {
  position: absolute;
  top: 3px;
  right: 4px;
  z-index: 3;
  font-size: 0.6rem;
  letter-spacing: 0.03em;
  color: #f2ece0;
  background: rgba(10,10,11,0.6);
  padding: 1px 4px;
  border-radius: 3px;
  pointer-events: none;
  font-variant-numeric: tabular-nums;
}
.video-block.low-match .video-block-score { color: #e7b85e; }


/* ====================================================
   Slice 3: playhead and selected-block state

   The playhead is a vertical gold line over the timeline-wrap, spanning the
   lane and the spine, positioned by time on the shared axis. The selected
   lane block reads with a gold edge and glow. Both reuse the site palette.
   ==================================================== */

.timeline-playhead {
  position: absolute;
  top: 0;
  bottom: 18px; /* stop above the ticks row */
  width: 2px;
  margin-left: -1px;
  background: var(--gold);
  box-shadow: 0 0 6px var(--gold-glow);
  pointer-events: none;
  z-index: 5;
}
.timeline-playhead::before {
  content: '';
  position: absolute;
  top: -1px;
  left: -3px;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--gold);
}

.video-block.selected {
  border: 1.5px solid var(--gold);
  box-shadow: 0 0 0 1px var(--gold), 0 0 10px var(--gold-glow);
  z-index: 2;
}
