/* The split from instruction.md rule 7 — text left, avatar right — pulled to
   54/46 so the avatar is centre stage rather than a tile in the corner, plus
   enough restraint that the card is readable while someone is talking over it.
   Two columns of examples is the whole pedagogical point, so they stay side by
   side down to tablet width and only stack below that. */

:root {
  --bg: #0b0f19;
  --panel: #131a2a;
  --panel-2: #182134;
  --line: rgba(255, 255, 255, 0.09);
  --ink: #eef2f9;
  --muted: #94a0b8;
  --do: #34d399;
  --dont: #f87171;
  --accent: #7aa2f7;
  --accent-dim: rgba(122, 162, 247, 0.16);
}

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font: 15px/1.55 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  height: 100vh;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

/* ---- top bar ---- */

.topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  padding: 14px 24px;
  border-bottom: 1px solid var(--line);
  background: var(--panel);
  flex: 0 0 auto;
}

.brand .mark {
  font-weight: 700;
  letter-spacing: 0.18em;
  color: var(--accent);
  margin-right: 12px;
}

.brand .sub { color: var(--muted); font-size: 13px; }

.progress {
  display: flex;
  gap: 8px;
  list-style: none;
  margin: 0;
  padding: 0;
}

.progress li {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 14px;
  border: 1px solid var(--line);
  border-radius: 999px;
  color: var(--muted);
  font-size: 13px;
  white-space: nowrap;
  transition: all 0.25s ease;
}

.progress li .pip {
  width: 20px;
  height: 20px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.07);
  font-size: 11px;
  font-weight: 700;
}

.progress li.current {
  color: var(--ink);
  border-color: var(--accent);
  background: var(--accent-dim);
}
.progress li.current .pip { background: var(--accent); color: #0b0f19; }

.progress li.passed { color: var(--do); border-color: rgba(52, 211, 153, 0.4); }
.progress li.passed .pip { background: var(--do); color: #05231a; }

/* ---- the split ---- */

/* Rule 7 asks for 60/40. The text column is narrower than that on purpose:
   the avatar is the thing being listened to, and at 40% it sat in the corner
   like a webcam tile. The card stays readable because its content is capped
   at a measure rather than filling whatever width is left. */
.split {
  flex: 1 1 auto;
  display: grid;
  grid-template-columns: 54fr 46fr;
  gap: 0;
  min-height: 0;
}

/* ---- left: the Magic Canvas ---- */

.canvas {
  padding: 26px 30px;
  overflow-y: auto;
  min-width: 0;
}

/* A measure, not a column width. Two example columns and a rubric need room
   to breathe, and letting them stretch to the panel edge is what makes a
   narrower panel feel cramped rather than tidy. */
.canvas > * { max-width: 680px; }

.placeholder { max-width: 620px; margin-top: 8vh; }
.placeholder h1 { font-size: 34px; margin: 0 0 16px; letter-spacing: -0.02em; }
.placeholder p { color: #c4cddf; font-size: 16px; }
.placeholder .muted { color: var(--muted); font-size: 14px; }

.card-head {
  display: flex;
  align-items: center;
  gap: 16px;
  margin-bottom: 18px;
}

.letter {
  width: 52px;
  height: 52px;
  display: grid;
  place-items: center;
  border-radius: 14px;
  background: var(--accent);
  color: #0b0f19;
  font-size: 26px;
  font-weight: 800;
  flex: 0 0 auto;
}

.card h2 { margin: 0; font-size: 26px; letter-spacing: -0.01em; }
.card .step { margin: 2px 0 0; color: var(--muted); font-size: 13px; }

.definition {
  font-size: 18px;
  line-height: 1.5;
  margin: 0 0 10px;
  padding-left: 14px;
  border-left: 3px solid var(--accent);
}

.why { color: var(--muted); margin: 0 0 22px; }

.columns {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 16px;
}

.col {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 14px;
  padding: 16px 18px;
}

.col h3 {
  margin: 0 0 12px;
  font-size: 13px;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  display: flex;
  align-items: center;
  gap: 8px;
}

.col.do h3 { color: var(--do); }
.col.dont h3 { color: var(--dont); }
.col .tick, .col .cross { font-size: 15px; }

.col ul { list-style: none; margin: 0; padding: 0; }
.col li + li { margin-top: 14px; }
.col .label { display: block; font-weight: 600; font-size: 14px; margin-bottom: 4px; }
.col .example { display: block; color: var(--muted); font-style: italic; font-size: 14px; }
.col.do .example { border-left: 2px solid rgba(52, 211, 153, 0.35); padding-left: 10px; }
.col.dont .example { border-left: 2px solid rgba(248, 113, 113, 0.35); padding-left: 10px; }

.rubric { margin-top: 22px; }
.rubric h3 {
  margin: 0 0 10px;
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--muted);
}
.rubric ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 8px; }
.rubric li {
  padding: 9px 13px;
  border: 1px solid var(--line);
  border-radius: 10px;
  font-size: 14px;
  color: #c4cddf;
  background: var(--panel-2);
  transition: all 0.3s ease;
}
.rubric li.met {
  border-color: rgba(52, 211, 153, 0.5);
  background: rgba(52, 211, 153, 0.1);
  color: var(--ink);
}
.rubric li.missed {
  border-color: rgba(248, 113, 113, 0.5);
  background: rgba(248, 113, 113, 0.1);
  color: var(--ink);
}
.rubric li .flag { font-weight: 700; margin-right: 8px; }
.rubric li.met .flag { color: var(--do); }
.rubric li.missed .flag { color: var(--dont); }

.verdict {
  margin-top: 18px;
  padding: 12px 16px;
  border-radius: 12px;
  font-weight: 600;
}
.verdict.pass { background: rgba(52, 211, 153, 0.14); color: var(--do); }
.verdict.retry { background: rgba(248, 113, 113, 0.12); color: var(--dont); }

/* ---- right: the avatar, centred and lit ---- */

.avatar {
  border-left: 1px solid var(--line);
  /* Darker than the card side, so the light has something to be light
     against. The wash at the top is the room, not the spotlight. */
  background:
    radial-gradient(130% 62% at 50% 0%, rgba(122, 162, 247, 0.10), transparent 62%),
    #0e1421;
  padding: 16px 20px 18px;
  display: flex;
  flex-direction: column;
  /* `safe` matters: plain `center` puts the top of an overflowing column
     above the scroll origin, and the Begin button becomes unreachable on a
     short window. */
  justify-content: safe center;
  align-items: center;
  gap: 10px;
  min-width: 0;
  overflow-y: auto;
}

/* The stage takes the height the controls do not, and the frame is sized from
   that rather than from a fixed width. A 704x1280 portrait at any generous
   width is taller than a laptop viewport, which is how the Begin button ended
   up below the fold — height first, width derived, and it always fits. */
.stage {
  position: relative;
  width: 100%;
  display: grid;
  place-items: center;
  padding: 10px 0 20px;
  flex: 1 1 auto;
  min-height: 0;
}

/* The beam: a cone opening downward from above the frame. Blurred and wide
   enough that its edges leave the panel, because a gradient whose boundary is
   visible reads as a shape behind the avatar rather than as light on it. */
.stage::before {
  content: "";
  position: absolute;
  z-index: 0;
  top: 0;
  left: 50%;
  width: min(150%, 680px);
  height: 100%;
  transform: translateX(-50%);
  background: radial-gradient(ellipse 48% 62% at 50% 16%,
              rgba(226, 237, 255, 0.34) 0%,
              rgba(140, 176, 255, 0.20) 40%,
              rgba(122, 162, 247, 0.07) 62%,
              transparent 76%);
  filter: blur(10px);
  opacity: 0.85;
  pointer-events: none;
  transition: opacity 0.45s ease;
}

/* The pool on the floor. Sits under the frame's lower edge and grounds it —
   without this the avatar floats, which is the usual tell of a fake spotlight. */
.stage::after {
  content: "";
  position: absolute;
  z-index: 0;
  bottom: 6px;
  left: 50%;
  width: min(92%, 520px);
  height: 62px;
  transform: translateX(-50%);
  background: radial-gradient(ellipse at center,
              rgba(186, 210, 255, 0.30) 0%,
              rgba(122, 162, 247, 0.12) 45%,
              transparent 74%);
  filter: blur(12px);
  opacity: 0.9;
  pointer-events: none;
  transition: opacity 0.45s ease;
}

.video-frame {
  position: relative;
  z-index: 1;                 /* above both halves of the light */
  height: 100%;
  width: auto;
  max-width: 100%;
  aspect-ratio: 704 / 1280;   /* the model's native portrait render */
  border-radius: 18px;
  overflow: hidden;
  background: #000;
  border: 1px solid rgba(255, 255, 255, 0.14);
  box-shadow:
    0 26px 60px rgba(0, 0, 0, 0.65),
    0 0 46px rgba(122, 162, 247, 0.16);
  transition: box-shadow 0.45s ease;
  flex: 0 1 auto;
}

/* Jordan speaking lifts the light. Same state the badge already renders, read
   from one attribute, so the lighting cannot disagree with the label. */
body[data-state="speaking"] .stage::before,
body[data-state="speaking"] .stage::after { opacity: 1; }

body[data-state="speaking"] .video-frame {
  box-shadow:
    0 26px 60px rgba(0, 0, 0, 0.65),
    0 0 64px rgba(122, 162, 247, 0.34);
}

/* Nothing here is load-bearing, so drop it entirely when asked to. */
@media (prefers-reduced-motion: reduce) {
  .stage::before, .stage::after, .video-frame { transition: none; }
}

.video-frame video { width: 100%; height: 100%; object-fit: cover; display: block; }

.badge {
  position: absolute;
  left: 12px;
  top: 12px;
  padding: 5px 11px;
  border-radius: 999px;
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  background: rgba(0, 0, 0, 0.6);
  backdrop-filter: blur(6px);
  color: var(--muted);
}
.badge.live { color: var(--do); }
.badge.speaking { color: var(--accent); }
.badge.error { color: var(--dont); }

/* Self-view, bottom-right of the avatar frame. Mirrored, because an
   un-mirrored self-view is disconcerting to look at while you talk. */
.selfview {
  position: absolute;
  right: 10px;
  bottom: 10px;
  width: 92px;
  aspect-ratio: 4 / 3;
  border-radius: 10px;
  overflow: hidden;
  border: 1px solid rgba(255, 255, 255, 0.25);
  background: #000;
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.5);
}
.selfview video { width: 100%; height: 100%; object-fit: cover; transform: scaleX(-1); }

/* Flashes when a frame is actually admitted into the model's context — not
   when one is captured. The learner should be able to see the difference. */
.selfview .shutter {
  position: absolute;
  inset: 0;
  background: #fff;
  opacity: 0;
  pointer-events: none;
}
.selfview .shutter.fire { animation: flash 0.45s ease-out; }
@keyframes flash { from { opacity: 0.55; } to { opacity: 0; } }

/* Top-right of the frame, next to the camera it describes. Absolute, so an
   empty counter costs the avatar no height at all. */
.frames {
  position: absolute;
  right: 12px;
  top: 12px;
  margin: 0;
  padding: 4px 9px;
  border-radius: 999px;
  font-size: 11px;
  color: var(--muted);
  background: rgba(0, 0, 0, 0.55);
  backdrop-filter: blur(6px);
  pointer-events: none;
}
.frames:empty { display: none; }

/* A subtitle, not a paragraph under the video. Over the picture it costs no
   layout height, and the scrim keeps it legible over a bright frame. Capped
   at a third of the frame so a long turn cannot cover Jordan's face. */
.caption {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  margin: 0;
  padding: 44px 16px 14px;
  max-height: 34%;
  overflow: hidden;
  font-size: 15px;
  line-height: 1.45;
  color: #eaf0fb;
  text-align: center;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.9);
  background: linear-gradient(to top, rgba(4, 7, 14, 0.88), rgba(4, 7, 14, 0.62) 55%, transparent);
  pointer-events: none;
}
.caption:empty { display: none; }

/* Two rows, and that is the budget. Every row here is height the avatar does
   not get, and the avatar is what the demo is for: Begin and the two toggles
   share one row, the text entry takes the next. */
.controls {
  width: 100%;
  max-width: 560px;
  display: grid;
  grid-template-columns: 1.15fr 1fr 1fr;
  gap: 9px;
  flex: 0 0 auto;
}
.controls .text-entry { grid-column: 1 / -1; }

.controls button { padding: 10px 10px; font-size: 14px; }
.controls button.mic { white-space: nowrap; }

button {
  font: inherit;
  border-radius: 10px;
  border: 1px solid var(--line);
  padding: 11px 14px;
  cursor: pointer;
  background: var(--panel-2);
  color: var(--ink);
  transition: all 0.2s ease;
}
button:disabled { opacity: 0.4; cursor: not-allowed; }
button:not(:disabled):hover { border-color: var(--accent); }

button.primary { background: var(--accent); color: #0b0f19; border-color: var(--accent); font-weight: 600; }

button.mic { display: flex; align-items: center; justify-content: center; gap: 9px; }
button.mic .dot {
  width: 9px; height: 9px; border-radius: 50%;
  background: var(--muted); flex: 0 0 auto;
}
button.mic.on { border-color: var(--dont); color: var(--dont); }
button.mic.on .dot { background: var(--dont); animation: pulse 1.4s infinite; }

@keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.25; } }

.text-entry { display: flex; gap: 8px; }
.text-entry input {
  flex: 1 1 auto;
  min-width: 0;
  font: inherit;
  padding: 10px 12px;
  border-radius: 10px;
  border: 1px solid var(--line);
  background: var(--panel-2);
  color: var(--ink);
}
.text-entry input::placeholder { color: var(--muted); }
.text-entry input:focus { outline: none; border-color: var(--accent); }

.heard {
  margin: 0;
  width: 100%;
  max-width: 360px;
  font-size: 13px;
  color: var(--muted);
  text-align: center;
  font-style: italic;
  min-height: 1.6em;
  max-width: 560px;
  flex: 0 0 auto;
}

@media (max-width: 1000px) {
  .split { grid-template-columns: 1fr; grid-template-rows: 1fr auto; }
  .avatar { border-left: none; border-top: 1px solid var(--line); flex-direction: row; align-items: flex-start; }
  .columns { grid-template-columns: 1fr; }

  /* Stacked, the panel is a row and has no height to hand down, so a frame
     sized by height collapses to nothing — it measured 2x4 pixels before this
     rule existed. Width drives it here instead. */
  .stage { flex: 0 0 auto; width: auto; padding: 4px 0 12px; }
  .video-frame { height: auto; width: 38vw; max-width: 300px; }
  .controls { max-width: 340px; grid-template-columns: 1fr 1fr; }
  .controls button.primary { grid-column: 1 / -1; }
}
