/* Director's-monitor look: deep navy chrome around a 16:9 frame that stands in
   for the projected image. DIN-style HUD (Bahnschrift on Windows), mono slates
   and timecode, broadcast subtitles. Timeline segments are tinted by the
   storyboard's five-part movement. */

:root {
  --bg: #0b1220;
  --stage: #070b13;
  --panel: #0e1626;
  --line: #1d2940;
  --ink: #e8edf5;
  --dim: #8b96a8;
  --faint: #55607346;
  --accent: #4fe3c1; /* soup bioluminescence */
  --playhead: #ff3b5c; /* cat-god laser */
  --warn: #e8b44c;
  --part-1: #3fd9b6; /* Wonder */
  --part-2: #e8b44c; /* Compulsion */
  --part-3: #e07a45; /* Consequence */
  --part-4: #8f7fe8; /* Meaning */
  --part-5: #5ea8e8; /* Transfer */
  --display:
    "Bahnschrift", "Archivo Narrow", "Arial Narrow", "Segoe UI", sans-serif;
  --body: "Segoe UI", system-ui, sans-serif;
  --mono: "Cascadia Mono", "Consolas", ui-monospace, monospace;
}

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

html,
body {
  height: 100%;
}

body {
  display: flex;
  flex-direction: column;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--body);
  overflow: hidden;
}

button {
  font: inherit;
  color: inherit;
  background: none;
  border: 1px solid var(--line);
  border-radius: 4px;
  cursor: pointer;
}

button:hover {
  border-color: var(--dim);
}
button:focus-visible,
#timeline:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* ---------- HUD ---------- */

#hud {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.7rem 1.1rem;
  border-bottom: 1px solid var(--line);
}

.eyebrow {
  font-family: var(--display);
  font-size: 0.68rem;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--dim);
}

#scene-title {
  font-family: var(--display);
  font-size: 1.15rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  margin-top: 0.15rem;
}

.hud-right {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex-shrink: 0;
}

.hud-chip {
  font-family: var(--mono);
  font-size: 0.72rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--dim);
  border: 1px solid var(--line);
  border-radius: 3px;
  padding: 0.25rem 0.55rem;
}

#part-name {
  color: var(--part-color, var(--dim));
  border-color: currentColor;
}

#notes-toggle {
  font-family: var(--display);
  font-size: 0.78rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  padding: 0.3rem 0.7rem;
}

#notes-toggle[aria-pressed="true"] {
  color: var(--accent);
  border-color: var(--accent);
}

/* ---------- Layout ---------- */

#layout {
  flex: 1;
  display: flex;
  min-height: 0;
}

#stage {
  flex: 1;
  min-width: 0;
  display: grid;
  place-items: center;
  padding: 1rem;
  background: #05070d;
  container-type: size;
}

/* ---------- The frame: everything the audience would see ---------- */

#frame {
  position: relative;
  width: 100%; /* fallback */
  width: min(100cqw, calc(100cqh * 16 / 9));
  aspect-ratio: 16 / 9;
  overflow: hidden;
  border: 1px dashed var(--faint);
  border-radius: 4px;
  background:
    radial-gradient(120% 90% at 50% 45%, #0d1626 0%, var(--stage) 72%),
    repeating-linear-gradient(45deg, #ffffff05 0 1px, transparent 1px 11px);
  box-shadow:
    0 0 0 1px #ffffff08,
    0 18px 50px #00000070;
}

/* Captured footage fills the frame; the written description is the fallback
   for beats that have not been shot yet. Every clip gets its own element and
   stays loaded, so a cut between shots is a class swap rather than a reload —
   that is what keeps the cut from stuttering. */
#plates {
  position: absolute;
  inset: 0;
  background: #05070d;
}

#plates video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  visibility: hidden;
}

#plates video.active {
  opacity: 1;
  visibility: visible;
  z-index: 2;
}

/* A clip that is seeking to its first frame. Visible to the compositor so it
   decodes, but transparent — an undecoded <video> paints black, and showing it
   is what made every cut flash. It is swapped for the outgoing clip only once
   it has a real frame. */
#plates video.incoming {
  opacity: 0;
  visibility: visible;
  z-index: 1;
}

#frame:not(.has-plate) #plates {
  display: none;
}
#frame.has-plate #beat-text {
  display: none;
}
/* The visual description stands in for the footage itself. */
#beat-text {
  position: absolute;
  top: 19%;
  left: 50%;
  transform: translateX(-50%);
  width: min(62%, 46rem);
  text-align: center;
  font-family: var(--mono);
  font-size: clamp(0.72rem, 1.3cqw, 1.02rem);
  line-height: 1.55;
  color: #7d8ba1;
}

/* Mocked slide copy, over the footage as it would be in the real deck.
   Legibility over moving footage is the whole problem here. Type is white at
   every size — grey was lost against bright frames — and each rendered LINE
   carries its own black plate, hugging the text the way a highlight does. An
   earlier version darkened the whole copy zone with a soft gradient instead;
   it worked, but it dimmed a third of the picture to light a few words. */
#copy {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1.15rem;
  padding: 26% 6% 20%;
  text-align: center;
  pointer-events: none;
}

/* Copy zones: the footage decides where text can live, so each scene can move
   its copy off whatever the shot puts in the middle. The scrim follows. */
#copy.zone-upper {
  justify-content: flex-start;
  padding-top: 7%;
}
#copy.zone-lower {
  justify-content: flex-end;
  padding-bottom: 17%;
}
#copy.zone-left {
  align-items: flex-start;
  text-align: left;
  padding-left: 7%;
}
#copy.zone-right {
  align-items: flex-end;
  text-align: right;
  padding-right: 7%;
}

.copy-item {
  position: relative;
  font-family: var(--display);
  font-weight: 600;
  letter-spacing: 0.06em;
  line-height: 1.45;
  color: #fff;
}

/* The plate. Inline + cloned decoration is what makes it break per rendered
   line instead of boxing the paragraph; `pre-line` keeps authored "\n" breaks
   working, so a two-line copy item gets two plates. */
.copy-ink {
  display: inline;
  white-space: pre-line;
  background: #000000d9;
  padding: 0.1em 0.4em;
  border-radius: 2px;
  box-decoration-break: clone;
  -webkit-box-decoration-break: clone;
  text-shadow: 0 1px 2px #000000b3;
}

/* The alternative: no per-line plate, a soft gradient across the copy zone
   instead. Chosen per scene in footage.js (TEXT_PLATE) for shots whose footage
   is a backdrop rather than the subject, where dimming it costs nothing. */
#copy.plate-scrim .copy-ink {
  background: none;
  padding: 0;
  text-shadow:
    0 1px 2px #000000e6,
    0 3px 14px #000000cc,
    0 0 40px #00000099;
}

#copy.plate-scrim::before {
  content: "";
  position: absolute;
  inset: 0;
  opacity: 0;
  transition: opacity 0.5s ease;
  background: radial-gradient(
    72% 44% at 50% 50%,
    #000000ad 0%,
    #00000082 48%,
    #00000000 82%
  );
}

#copy.plate-scrim.has-copy::before {
  opacity: 1;
}

#copy.plate-scrim.zone-upper::before {
  background: linear-gradient(
    180deg,
    #000000b0 0%,
    #0000009c 24%,
    #00000070 40%,
    #00000000 58%
  );
}

#copy.plate-scrim.zone-left::before {
  background: linear-gradient(
    90deg,
    #000000b8 0%,
    #0000008c 36%,
    #00000000 66%
  );
}

.copy-item.size-xl {
  font-size: clamp(1.4rem, 4.6cqw, 3.2rem);
  letter-spacing: 0.05em;
}
.copy-item.size-lg {
  font-size: clamp(1rem, 2.6cqw, 1.9rem);
}

/* Captions and stat lines. Previously grey and small — the least readable
   thing in the deck. Now white and larger, held back by weight and tracking
   rather than by colour. */
.copy-item.size-sm {
  font-size: clamp(0.82rem, 1.85cqw, 1.35rem);
  font-weight: 500;
  letter-spacing: 0.2em;
  line-height: 1.8;
}

.copy-item {
  opacity: 0;
  transform: translateY(6px);
}
.copy-item.shown {
  opacity: 1;
  transform: none;
  transition:
    opacity 0.45s ease,
    transform 0.45s ease;
}

#subtitle-wrap {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 4.5%;
  display: flex;
  justify-content: center;
  pointer-events: none;
}

/* Broadcast subtitles: the box hugs each line rather than sitting as one wide
   slab, which is what a burned-in caption actually looks like. */
.subtitle-box {
  max-width: 80%;
  text-align: center;
  font-size: clamp(0.8rem, 2cqw, 1.25rem);
  line-height: 1.55;
}

#subtitle {
  display: inline;
  color: #fff;
  background: #000000cc;
  padding: 0.18em 0.55em;
  border-radius: 3px;
  box-decoration-break: clone;
  -webkit-box-decoration-break: clone;
  text-shadow: 0 1px 2px #000000cc;
  visibility: hidden;
}

#subtitle.shown {
  visibility: visible;
}

body.no-subs #subtitle-wrap {
  display: none;
}

/* ---------- Notes panel ---------- */

#notes {
  width: min(24rem, 38%);
  flex-shrink: 0;
  border-left: 1px solid var(--line);
  background: var(--panel);
  padding: 1.1rem 1.2rem 2rem;
  overflow-y: auto;
}

#notes-heading {
  font-family: var(--display);
  font-size: 0.95rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  margin-bottom: 0.9rem;
}

#notes-body dt {
  font-family: var(--mono);
  font-size: 0.62rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--accent);
  margin: 0.9rem 0 0.25rem;
}

#notes-body dd {
  font-size: 0.84rem;
  line-height: 1.5;
  color: var(--ink);
}

.notes-sub {
  font-family: var(--mono);
  font-size: 0.62rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--accent);
  margin: 1.4rem 0 0.5rem;
  padding-top: 0.9rem;
  border-top: 1px solid var(--line);
  display: flex;
  justify-content: space-between;
  gap: 0.5rem;
}

#pacing-summary {
  color: var(--dim);
  letter-spacing: 0.06em;
}

#note-pacing {
  list-style: none;
}

#note-pacing li {
  display: grid;
  grid-template-columns: 4.6rem 1fr;
  gap: 0.5rem;
  font-size: 0.76rem;
  line-height: 1.45;
  padding: 0.28rem 0;
  border-bottom: 1px solid #ffffff08;
  color: var(--dim);
}

#note-pacing .fit {
  font-family: var(--mono);
  font-size: 0.68rem;
  color: var(--dim);
  white-space: nowrap;
}

#note-pacing li.over .fit {
  color: var(--warn);
}
#note-pacing li.speaking {
  color: var(--ink);
}
#note-pacing li.speaking .fit {
  color: var(--accent);
}
#note-pacing li.over.speaking .fit {
  color: var(--warn);
}

/* ---------- Transport ---------- */

#transport {
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: 0.65rem 1.1rem 0.8rem;
  border-top: 1px solid var(--line);
}

.transport-buttons {
  display: flex;
  gap: 0.35rem;
}

.transport-buttons button {
  width: 2.3rem;
  height: 2.05rem;
  font-size: 0.95rem;
  display: grid;
  place-items: center;
}

#btn-play {
  color: var(--accent);
  border-color: var(--accent);
}
#btn-mute {
  font-size: 0.85rem;
}
#btn-mute[aria-pressed="true"] {
  color: var(--playhead);
  border-color: var(--playhead);
}

.btn-label {
  font-family: var(--display);
  font-size: 0.72rem !important;
  letter-spacing: 0.1em;
}

#btn-subs[aria-pressed="true"] {
  color: var(--accent);
  border-color: var(--accent);
}
#btn-subs[aria-pressed="false"] {
  color: var(--faint);
}

.timecode {
  font-family: var(--mono);
  font-size: 0.8rem;
  color: var(--dim);
  white-space: nowrap;
  display: flex;
  gap: 0.35rem;
  align-items: baseline;
}

#tc-current {
  color: var(--ink);
  min-width: 4.2ch;
}
.tc-sep {
  color: var(--faint);
}
#tc-local {
  font-size: 0.68rem;
  color: var(--faint);
  min-width: 6ch;
}

.timeline-stack {
  flex: 1;
  min-width: 0;
}

#timeline {
  position: relative;
  display: flex;
  gap: 2px;
  height: 1.7rem;
  cursor: pointer;
  touch-action: none;
}

.seg {
  position: relative;
  height: 100%;
  border-radius: 2px;
  background: color-mix(in srgb, var(--seg-color) 16%, transparent);
  border: 1px solid color-mix(in srgb, var(--seg-color) 35%, transparent);
  overflow: hidden;
}

.seg.current {
  background: color-mix(in srgb, var(--seg-color) 34%, transparent);
  border-color: var(--seg-color);
}

.seg-num {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  font-family: var(--mono);
  font-size: 0.6rem;
  color: var(--dim);
  pointer-events: none;
}

.seg.current .seg-num {
  color: var(--ink);
}

#playhead {
  position: absolute;
  top: -3px;
  bottom: -3px;
  width: 2px;
  margin-left: -1px;
  background: var(--playhead);
  box-shadow: 0 0 6px var(--playhead);
  pointer-events: none;
  z-index: 2;
}

#part-track {
  display: flex;
  gap: 2px;
  margin-top: 3px;
}

.part-label {
  font-family: var(--mono);
  font-size: 0.56rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  text-align: center;
  color: var(--seg-color);
  opacity: 0.75;
  border-top: 2px solid var(--seg-color);
  padding-top: 2px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ---------- Presentation mode ----------
   The deck is meant to be presentable from, so the director's chrome gets out
   of the way entirely: image edge to edge, no HUD, no slate, cursor gone once
   you stop moving it. The transport reappears on any pointer movement and
   fades again two seconds later, so it can be driven live without leaving. */

body.presenting #hud,
body.presenting #notes {
  display: none;
}

body.presenting #stage {
  padding: 0;
  background: #000;
}

body.presenting #frame {
  border: none;
  border-radius: 0;
  box-shadow: none;
}

body.presenting #transport {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 20;
  border-top: none;
  background: linear-gradient(0deg, #000000e0 0%, #00000090 60%, transparent);
  opacity: 0;
  transform: translateY(12px);
  pointer-events: none;
  transition:
    opacity 0.25s ease,
    transform 0.25s ease;
}

body.presenting.hot #transport {
  opacity: 1;
  transform: none;
  pointer-events: auto;
}

body.presenting:not(.hot) {
  cursor: none;
}

/* ---------- Display-only ----------
   A phone is holding the remote, so the projector is a display and nothing
   else: no chrome, no transport, no cursor. Same rules as presentation mode,
   applied because someone else is driving rather than because a key was
   pressed. */

body.display-only #hud,
body.display-only #notes,
body.display-only #transport {
  display: none;
}

body.display-only #stage {
  padding: 0;
  background: #000;
}

body.display-only #frame {
  border: none;
  border-radius: 0;
  box-shadow: none;
}

body.display-only {
  cursor: none;
}

/* The link chip is the one piece of chrome worth keeping visible while the
   deck is unattended — but only when something is wrong. */
#link-state {
  color: var(--accent);
  border-color: currentColor;
}
body.offline #link-state {
  color: var(--playhead);
}

/* ---------- Small screens ---------- */

@media (max-width: 900px) {
  #layout {
    position: relative;
  }
  #notes {
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    width: min(22rem, 85vw);
    z-index: 5;
    box-shadow: -12px 0 30px #00000090;
  }
  .part-label {
    display: none;
  }
  #tc-local {
    display: none;
  }
  #beat-text {
    top: 15%;
    width: 82%;
  }
}

@media (prefers-reduced-motion: reduce) {
  .copy-item.shown {
    transition: none;
  }
}
