/* ==========================================================
   Lenovo Boost — mission log
   Instrument panel: everything is a gauge, nothing is chrome.
   ========================================================== */

:root {
  /* palette */
  --void:   #060814;
  --deep:   #0e1430;
  --hull:   #161d3d;
  --boost:  #e2231a;   /* Lenovo red — thrust, pointer, tier accent */
  --ember:  #ff8a3d;   /* sodium flame */
  --bone:   #ede7dc;   /* chalky type, never pure white */
  --signal: #4fd1c5;   /* secondary telemetry only */
  --dim:    rgba(237, 231, 220, 0.42);
  --hair:   rgba(237, 231, 220, 0.14);

  /* type */
  --sign: "IBM Plex Sans Condensed", "Helvetica Neue", Arial, sans-serif;
  --body: "IBM Plex Sans", system-ui, sans-serif;
  --tele: "IBM Plex Mono", ui-monospace, "SFMono-Regular", Menlo, monospace;

  /* metrics */
  --rail: 66px;
  --tape-w: 76px;
  /* The instrument's own gutter. Named apart from the bar's --chrome-edge
     on purpose: this file used to call it --edge, which is also what
     site.css names the shared chrome's gutter, so redefining it here
     quietly moved the top bar 4px in on this page alone. */
  --stage-edge: 20px;
  --board-w: 288px;   /* standings column, docked right for the session */

  /* Where the nav's content starts. Everything pinned to an edge of the
     stage uses this, so the goal bar lines up with the Lenovo | Intel
     lockup and the standings stop at the Leaderboard button however
     wide the window gets. That promise is only kept by measuring off the
     bar's own gutter — --wrap-w and --chrome-edge, both from site.css. */
  --rail-inset: max(var(--chrome-edge, 24px), calc(50% - var(--wrap-w, 1200px) / 2 + var(--chrome-edge, 24px)));
}

* { box-sizing: border-box; }

/* Beats [hidden] losing to any class that sets display. */
[hidden] { display: none !important; }

.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

html, body {
  height: 100%;
  margin: 0;
  /* The site's void, not the instrument's. This colour is only ever
     visible in the strip behind the shared top bar — the stage starts
     below the nav and #sky paints its own sky over everything else — and
     that bar is 72% opaque, so whatever sits behind it is part of its
     colour. Left on the instrument's navy the bar composited to a
     different tone here than on the landing page. */
  background: rgb(var(--void-rgb, 6, 8, 20));
  color: var(--bone);
  font-family: var(--body);
  overflow: hidden;
  -webkit-font-smoothing: antialiased;
}

body { overscroll-behavior: none; }

/* The stage is held below the shared top bar rather than under it —
   `inset: 0` would run the star field behind the nav and the mission
   chrome would collide with it. --nav-h comes from site.css. */
.stage {
  position: fixed;
  inset: 0;
  top: var(--nav-h, 0px);
  display: block;
}

#sky {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  touch-action: none;
}

/* the only tell for the flight test: the ship takes a cursor */
#sky.is-live { cursor: pointer; }

/* ── rails ─────────────────────────────────────────────── */

/* Padded to the nav's rail, so the telemetry cells line up with the
   lockup and the Leaderboard button however wide the window gets.
   --wrap-w comes from site.css. */
.rail {
  position: absolute;
  left: 0;
  right: 0;
  height: var(--rail);
  display: flex;
  align-items: center;
  gap: 24px;
  padding: 0 var(--rail-inset);
  z-index: 3;
  pointer-events: none;
}

.rail > * { pointer-events: auto; }

.rail--bottom {
  bottom: 0;
  height: 84px;
  align-items: stretch;
  border-top: 1px solid var(--hair);
  background: linear-gradient(0deg, rgba(6, 8, 20, 0.92), rgba(6, 8, 20, 0));
}


/* ── team goal bar ─────────────────────────────────────────
   Held to the same rail the nav uses, so it lines up with the
   Lenovo | Intel lockup however wide the window gets rather than
   drifting out to the screen edge. --wrap-w comes from site.css. */

.tape {
  position: absolute;
  left: var(--rail-inset);
  top: 26px;
  bottom: 110px;
  width: var(--tape-w);
  z-index: 2;
  display: flex;
  flex-direction: column;
  gap: 8px;
  pointer-events: none;
}

.tape__track {
  position: relative;
  flex: 1;
  width: 34px;
  border: 1px solid var(--hair);
  border-radius: 1px;
  background: rgba(14, 20, 48, 0.55);
  overflow: visible;
}

.tape__cap, .tape__base {
  margin: 0;
  width: 34px;
  text-align: center;
  font-family: var(--tele);
  font-size: 9.5px;
  letter-spacing: 0.12em;
  color: var(--dim);
}

.tape__cap b { display: block; font-weight: 500; color: var(--bone); }

/* Two fills, both anchored at the foot of the same track and on the same
   scale. Lenovo's is drawn first and runs taller; yours sits inside it,
   narrower and inset, so neither hides the other and the gap between the
   two tops is the thing you read: how much of the team's climb is
   yours. */
.tape__fill {
  position: absolute;
  bottom: 0;
  height: 0;
  transition: height 220ms linear;
}

.tape__fill--team {
  left: 0; right: 0;
  z-index: 1;
  background: linear-gradient(0deg, rgba(226, 35, 26, 0.55), rgba(255, 138, 61, 0.28));
  border-top: 1px solid var(--ember);
}

/* Teal against the red: the palette keeps --signal for telemetry that is
   not Lenovo's, which is exactly what an operator's own number is. Inset
   so the team's colour still shows either side of it — yours is a part
   of that bar, not a second bar beside it. */
.tape__fill--you {
  left: 7px; right: 7px;
  z-index: 2;
  background: linear-gradient(0deg, rgba(79, 209, 197, 0.72), rgba(79, 209, 197, 0.34));
  border-top: 1px solid var(--signal);
}

.tape__ticks { position: absolute; inset: 0; z-index: 3; }

.tape__tick {
  position: absolute;
  left: 0;
  width: 7px;
  height: 1px;
  background: var(--hair);
}

.tape__tick--major { width: 13px; background: rgba(237, 231, 220, 0.34); }

.tape__pointer {
  position: absolute;
  bottom: 0;
  z-index: 4;
  height: 1px;
  transition: bottom 220ms linear;
}

.tape__pointer::after {
  content: "";
  position: absolute;
  top: -4px;
  border: 4px solid transparent;
}

/* The count, sitting on the bar. Carries a ground of its own — it is
   over a fill of its own colour, not over empty track. */
.tape__num {
  position: absolute;
  left: 0;
  bottom: 5px;
  font-family: var(--tele);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.04em;
  color: var(--bone);
  white-space: nowrap;
  text-shadow: 0 0 6px rgba(6, 8, 20, 0.95), 0 1px 3px rgba(6, 8, 20, 0.9);
}

/* And the reading outside the bar: what that count is, and what share of
   the goal it represents. Clear of the track, so it never sits on a
   fill and never has to fight one. */
.tape__read {
  position: absolute;
  left: 52px;
  bottom: 1px;
  width: max-content;
  font-family: var(--tele);
  font-size: 9px;
  font-weight: 500;
  letter-spacing: 0.13em;
  line-height: 1.5;
  text-transform: uppercase;
  white-space: nowrap;
  text-shadow: 0 0 8px rgba(6, 8, 20, 0.95);
}

.tape__read b { display: block; font-weight: 500; color: var(--dim); }
.tape__read i { display: block; font-style: normal; }
.tape__read em { font-style: normal; }

.tape__pointer--team .tape__read i { color: var(--ember); }

/* The operator's pair hangs under its line, Lenovo's sits over its own,
   so the two sets move apart as they converge rather than together. */
.tape__pointer--you .tape__num { top: 5px; bottom: auto; }
.tape__pointer--you .tape__read { top: 1px; bottom: auto; }
.tape__pointer--you .tape__read i { color: var(--signal); }

/* Lenovo's number reads above its line, the operator's below its own.
   Yours can never be the taller of the two, so the two readouts always
   move apart rather than together — which is what keeps them legible
   where it matters, at the moment the walkthrough has them converge.
   Putting them on opposite sides of the track instead would want 40px
   of clear space to the left of the bar, and on a phone there is none. */
.tape__pointer--team {
  left: -6px;
  width: 46px;
  background: var(--boost);
}

.tape__pointer--team::after { right: -1px; border-right-color: var(--boost); }

.tape__pointer--you {
  left: -6px;
  width: 46px;
  background: var(--signal);
}

.tape__pointer--you::after { right: -1px; border-right-color: var(--signal); }

.tape__pointer--you .tape__num { color: var(--signal); }

/* ── the bar topped out ────────────────────────────────────
   The goal reached is the one moment this page is building toward, so
   it says so on the bar rather than leaving a full red column to be
   inferred. Sits off the top right of the track, where Lenovo's own pair
   of readings sits — and those go with it, because at the goal they say
   5,000 and 100% met, which is what the cap above and this line below
   are already saying twice over. The operator's pair is a long way down
   the bar and is left alone. */
.tape__done {
  position: absolute;
  left: calc(100% + 11px);
  top: -3px;
  margin: 0;
  width: max-content;
  font-family: var(--tele);
  font-size: 10px;
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  line-height: 1.5;
  opacity: 0;
  transform: translateY(5px);
  transition: opacity 520ms ease, transform 520ms ease;
  pointer-events: none;
}

.tape__done b { display: block; font-weight: 500; color: var(--bone); }
.tape__done span { display: block; color: var(--ember); }

.tape.is-complete .tape__done { opacity: 1; transform: none; }
.tape.is-complete .tape__pointer--team .tape__num,
.tape.is-complete .tape__pointer--team .tape__read { opacity: 0; }

.tape__num, .tape__read { transition: opacity 300ms; }

/* ── telemetry cells ───────────────────────────────────── */

.cell {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 5px;
  flex: 1 1 0;
}

.cell--score { flex: 0 0 auto; align-items: center; text-align: center; }
.cell--gap   { align-items: flex-end; text-align: right; }

/* The standing cell is a button. Stripped back to the other cells so the
   rail still reads as one row of instruments, and given a caret, a hover
   and a line of its own saying what it opens — once the board has been
   closed this is the only way back to it, so it has to look like a way
   back to it. */
.cell--action {
  border: 0;
  background: none;
  font: inherit;
  color: inherit;
  padding: 0;
  cursor: pointer;
  text-align: right;
  transition: color 180ms;
}

.cell--action .cell__value {
  display: inline-flex;
  align-items: center;
  gap: 7px;
}

.cell__caret {
  width: 13px;
  height: 13px;
  fill: none;
  stroke: var(--boost);
  stroke-width: 2.4;
  stroke-linecap: round;
  stroke-linejoin: round;
  transition: transform 300ms ease;
}

.cell--action[aria-expanded="true"] .cell__caret { transform: rotate(180deg); }

.cell__hint {
  font-family: var(--tele);
  font-size: 9px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--dim);
  transition: color 180ms;
}

.cell--action:hover .cell__hint { color: var(--bone); }
.cell--action:hover .cell__value { color: var(--bone); }

.cell__label {
  font-family: var(--tele);
  font-size: 9.5px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--dim);
}

.cell__value {
  font-family: var(--sign);
  font-weight: 600;
  font-size: 16px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

.cell__of {
  font-family: var(--tele);
  font-size: 10px;
  letter-spacing: 0.1em;
  color: var(--dim);
}

/* odometer: fixed-width digits, unfilled leading places sit dark */
.odometer {
  display: flex;
  gap: 2px;
  font-family: var(--tele);
  font-weight: 500;
  font-size: 38px;
  line-height: 1;
  letter-spacing: 0.02em;
}

.odometer i {
  font-style: normal;
  display: block;
  min-width: 0.62em;
  text-align: center;
}

.odometer i.is-lead { color: rgba(237, 231, 220, 0.13); }
.odometer i.is-hot  { color: var(--ember); }

/* ── standings ─────────────────────────────────────────── */

/* Sits opposite the altitude tape, but not for the whole session: it
   arrives once the climb has landed and closes from either its own × or
   the standing cell in the rail. Height is the content's, capped so it
   can never run under the bottom rail. */
.standings {
  position: absolute;
  top: 16px;
  /* Same rail as the nav, so its right edge lines up with the
     Leaderboard button rather than running out to the screen edge. */
  right: var(--rail-inset);
  width: var(--board-w);
  /* Against the stage, not the viewport — the stage already starts
     below the top bar, so measuring from 100vh double-counted it. */
  max-height: calc(100% - 124px);
  overflow-y: auto;
  z-index: 4;
  padding: 14px 16px 16px;
  border: 1px solid var(--hair);
  border-radius: 2px;
  background: rgba(9, 13, 33, 0.94);
  backdrop-filter: blur(6px);
}

/* Shut: out of the way and out of the tab order, so the scene has the
   screen back. Slides toward the edge it is docked to. */
.standings.is-shut {
  opacity: 0;
  visibility: hidden;
  transform: translateX(20px);
  pointer-events: none;
  transition: opacity 260ms ease, transform 320ms ease, visibility 0s linear 320ms;
}

.standings {
  transform: translateX(0);
  transition: opacity 300ms ease, transform 420ms cubic-bezier(0.2, 0.8, 0.3, 1);
}

.standings__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin: 0 0 10px;
}

.standings__title {
  margin: 0;
  font-family: var(--tele);
  font-size: 9.5px;
  font-weight: 500;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--dim);
}

.standings__close {
  display: grid;
  place-items: center;
  width: 22px;
  height: 22px;
  margin: -4px -4px -4px 0;
  padding: 0;
  border: 0;
  border-radius: 2px;
  background: none;
  color: var(--dim);
  cursor: pointer;
  transition: color 160ms, background 160ms;
}

.standings__close svg {
  width: 12px;
  height: 12px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2.2;
  stroke-linecap: round;
}

.standings__close:hover { color: var(--bone); background: rgba(237, 231, 220, 0.08); }

.standings__list { margin: 0; padding: 0; list-style: none; }

.standings__row {
  display: grid;
  grid-template-columns: 26px 1fr auto;
  align-items: baseline;
  gap: 10px;
  padding: 6px 0;
  border-top: 1px solid rgba(237, 231, 220, 0.07);
  font-size: 12.5px;
}

.standings__row:first-child { border-top: 0; }
.standings__row.is-you { color: var(--bone); }
.standings__row.is-you .standings__pos { color: var(--boost); }
.standings__row:not(.is-you) { color: var(--dim); }

.standings__pos, .standings__pts {
  font-family: var(--tele);
  font-size: 11px;
  letter-spacing: 0.04em;
}

.standings__pts { font-weight: 500; }

.standings__who {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ── flight test overlay ───────────────────────────────── */

.run {
  position: absolute;
  top: 22px;
  left: 0;
  right: 0;
  z-index: 5;
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  padding: 0 var(--rail-inset);
  pointer-events: none;
  font-family: var(--tele);
}

.run__level {
  font-size: 10px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--dim);
}

.run__level b { color: var(--bone); font-weight: 600; margin-left: 5px; }

.run__shield {
  display: block;
  margin-top: 5px;
  font-style: normal;
  font-size: 9.5px;
  letter-spacing: 0.2em;
  color: var(--signal);
  animation: breathe 1.6s ease-in-out infinite;
}

@keyframes breathe { 50% { opacity: 0.45; } }

.run__score { text-align: right; }
.run__score b { display: block; font-size: 34px; font-weight: 500; line-height: 1; }

.run__score span {
  font-size: 9.5px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--dim);
}

.run__combo {
  font-style: normal;
  color: var(--ember);
  letter-spacing: 0.06em;
}

/* the run HUD arrives with the hand-off, so let it fade up */
.run:not([hidden]) { animation: riseIn 320ms ease-out; }

@keyframes riseIn { from { opacity: 0; transform: translateY(-8px); } }

/* ── panel ─────────────────────────────────────────────── */

.panel {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 6;
  width: min(392px, calc(100vw - 32px));
  padding: 26px 26px 22px;
  text-align: center;
  border: 1px solid var(--hair);
  border-top: 2px solid var(--boost);
  background: rgba(9, 13, 33, 0.95);
  backdrop-filter: blur(8px);
  animation: drop 260ms cubic-bezier(0.2, 0.8, 0.3, 1);
}

@keyframes drop { from { opacity: 0; transform: translateY(-6px); } }

.panel__eyebrow {
  margin: 0;
  font-family: var(--tele);
  font-size: 10px;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--dim);
}

.panel__score {
  margin: 6px 0 18px;
  font-family: var(--tele);
  font-weight: 500;
  font-size: 54px;
  line-height: 1;
}

.panel__stats {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 10px;
  margin: 0 0 20px;
  padding: 12px 0;
  border-top: 1px solid var(--hair);
  border-bottom: 1px solid var(--hair);
}

.panel__stats dt {
  font-family: var(--tele);
  font-size: 9px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--dim);
}

.panel__stats dd {
  margin: 3px 0 0;
  font-family: var(--tele);
  font-size: 15px;
}

.panel__actions { display: flex; gap: 8px; }

.panel-btn {
  flex: 1;
  padding: 9px 12px;
  border: 1px solid var(--hair);
  border-radius: 2px;
  background: transparent;
  color: var(--bone);
  font-family: var(--sign);
  font-weight: 600;
  font-size: 13px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  white-space: nowrap;
  cursor: pointer;
  transition: border-color 150ms, background 150ms;
}

.panel-btn:hover, .panel-btn:focus-visible { border-color: var(--bone); }
.panel-btn--primary { background: var(--boost); border-color: var(--boost); }
.panel-btn--primary:hover, .panel-btn--primary:focus-visible { background: #f6372e; border-color: #f6372e; }

.panel__hint {
  margin: 14px 0 0;
  font-family: var(--tele);
  font-size: 9.5px;
  letter-spacing: 0.1em;
  color: rgba(237, 231, 220, 0.28);
}

/* ── notice ────────────────────────────────────────────── */

.notice {
  position: absolute;
  left: var(--rail-inset);
  bottom: 96px;
  z-index: 4;
  margin: 0;
  padding: 6px 10px;
  border-left: 2px solid var(--ember);
  background: rgba(9, 13, 33, 0.9);
  font-family: var(--tele);
  font-size: 10.5px;
  letter-spacing: 0.06em;
  color: var(--dim);
}

/* A failure to load is not an aside — it is the reason the page looks
   empty, so it sits in the middle of the stage and does not fade. */
.notice--bad {
  left: 50%;
  top: 50%;
  bottom: auto;
  transform: translate(-50%, -50%);
  max-width: min(460px, calc(100% - 48px));
  padding: 16px 20px;
  border: 1px solid var(--boost);
  border-left-width: 3px;
  border-radius: 2px;
  font-size: 12px;
  line-height: 1.7;
  color: var(--bone);
}

/* ── the game's top ten ────────────────────────────────────
   Lives on the end-of-run panel. Kept visually distinct from the
   standings: this board is pilots, that one is reps. */

.hiscore {
  margin: 4px 0 18px;
  padding-top: 14px;
  border-top: 1px solid var(--hair);
  text-align: left;
}

.hiscore__title {
  margin: 0 0 10px;
  font-family: var(--tele);
  font-size: 9.5px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--dim);
}

.hiscore__entry { display: flex; gap: 8px; margin-bottom: 12px; }

.hiscore__input {
  flex: 1;
  min-width: 0;
  padding: 8px 10px;
  border: 1px solid var(--hair);
  border-radius: 2px;
  background: rgba(6, 8, 20, 0.7);
  color: var(--bone);
  font-family: var(--tele);
  font-size: 12px;
  letter-spacing: 0.06em;
}

.hiscore__input::placeholder { color: rgba(237, 231, 220, 0.32); }
.hiscore__input:focus { outline: none; border-color: var(--boost); }

.hiscore__save {
  padding: 8px 14px;
  border: 1px solid var(--boost);
  border-radius: 2px;
  background: var(--boost);
  color: #fff;
  font-family: var(--tele);
  font-size: 11px;
  letter-spacing: 0.12em;
  cursor: pointer;
}

.hiscore__save:hover { background: #f6372e; }

.hiscore__list {
  margin: 0;
  padding: 0;
  list-style: none;
  /* Ten rows is taller than the panel wants on a laptop, so it
     scrolls inside rather than pushing the buttons off. */
  max-height: 208px;
  overflow-y: auto;
}

.hiscore__row {
  display: grid;
  grid-template-columns: 22px 1fr auto;
  align-items: baseline;
  gap: 10px;
  padding: 5px 0;
  border-top: 1px solid rgba(237, 231, 220, 0.07);
  font-family: var(--tele);
  font-size: 11.5px;
  color: var(--dim);
}

.hiscore__row:first-child { border-top: 0; }

.hiscore__row.is-you { color: var(--bone); }
.hiscore__row.is-you .hiscore__pos { color: var(--boost); }

.hiscore__who {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-family: var(--body);
  font-size: 12.5px;
}

.hiscore__pts { font-weight: 500; color: var(--bone); }

.hiscore__none {
  margin: 0;
  font-size: 12px;
  color: var(--dim);
}

/* ── standings: before the board opens ─────────────────────
   The same box the ranked list will fill, holding the reason it
   is empty and something to do in the meantime. */

.soon { padding: 4px 0 2px; }

.soon__head {
  margin: 0 0 10px;
  font-family: var(--sign);
  font-weight: 600;
  font-size: 17px;
  letter-spacing: 0.02em;
  color: var(--bone);
}

.soon__body {
  margin: 0 0 16px;
  font-size: 12.5px;
  line-height: 1.65;
  color: var(--dim);
}

.soon__body i {
  font-style: normal;
  font-family: var(--tele);
  font-size: 0.8em;
  color: var(--boost);
}

.soon__cta {
  width: 100%;
  padding: 10px 14px;
  border: 1px solid var(--boost);
  border-radius: 2px;
  background: rgba(226, 35, 26, 0.14);
  color: var(--bone);
  font-family: var(--tele);
  font-size: 11px;
  letter-spacing: 0.14em;
  cursor: pointer;
  transition: background 160ms;
}

.soon__cta:hover { background: var(--boost); }

/* ══ THE PRE-LAUNCH WALKTHROUGH ════════════════════════════
   Points at the page rather than covering it. Everything the
   walkthrough names stays lit and legible; the rest goes under
   a dim. Nothing here takes a click except Skip. */

.tour {
  position: fixed;
  inset: 0;
  z-index: 50;
  pointer-events: none;
}

/* The dim and its hole are one element. A spread far larger than the
   screen means the shadow is the dim, and the box itself is the hole —
   so there is only ever one thing to move. */
.tour__spot {
  position: absolute;
  border-radius: 4px;
  box-shadow: 0 0 0 9999px rgba(6, 8, 20, 0.78);
  outline: 1px solid rgba(226, 35, 26, 0.55);
  outline-offset: -1px;
  transition: left 420ms cubic-bezier(0.2, 0.8, 0.3, 1),
              top 420ms cubic-bezier(0.2, 0.8, 0.3, 1),
              width 420ms cubic-bezier(0.2, 0.8, 0.3, 1),
              height 420ms cubic-bezier(0.2, 0.8, 0.3, 1);
}

.tour__card {
  position: absolute;
  width: min(330px, calc(100vw - 32px));
  padding: 18px 20px 16px;
  border: 1px solid var(--hair);
  border-top: 2px solid var(--boost);
  border-radius: 2px;
  background: rgba(9, 13, 33, 0.97);
  backdrop-filter: blur(8px);
  box-shadow: 0 20px 50px rgba(2, 3, 10, 0.6);
  transition: left 420ms cubic-bezier(0.2, 0.8, 0.3, 1),
              top 420ms cubic-bezier(0.2, 0.8, 0.3, 1);
}

.tour__eyebrow {
  margin: 0 0 8px;
  font-family: var(--tele);
  font-size: 9.5px;
  font-weight: 500;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--boost);
}

.tour__line {
  margin: 0 0 16px;
  font-family: var(--sign);
  font-weight: 600;
  font-size: 17px;
  line-height: 1.35;
  letter-spacing: 0.01em;
  color: var(--bone);
  animation: tour-in 460ms cubic-bezier(0.2, 0.8, 0.3, 1) both;
}

/* The lockup inside a walkthrough line is the lockup — .lockup in
   site.css owns both halves of it, and this page adds nothing. */

@keyframes tour-in {
  from { opacity: 0; transform: translateY(6px); }
}

.tour__foot { display: flex; align-items: center; justify-content: space-between; gap: 16px; }

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

.tour__dots li {
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: rgba(237, 231, 220, 0.22);
  transition: background 300ms, transform 300ms;
}

.tour__dots li.is-past { background: rgba(237, 231, 220, 0.45); }
.tour__dots li.is-now { background: var(--boost); transform: scale(1.5); }

.tour__skip {
  pointer-events: auto;
  padding: 5px 11px;
  border: 1px solid var(--hair);
  border-radius: 2px;
  background: none;
  color: var(--dim);
  font-family: var(--tele);
  font-size: 10px;
  letter-spacing: 0.14em;
  cursor: pointer;
  transition: color 160ms, border-color 160ms;
}

.tour__skip:hover { color: var(--bone); border-color: var(--bone); }

.tour.is-leaving { opacity: 0; transition: opacity 500ms; }

/* The standings stay lit through the guide walkthrough — one of its
   beats is about them, and a walkthrough cannot point at something it
   hid.

   The story version is the opposite case: it never mentions them, and a
   board holding a note about a board that has not opened is the one
   thing on screen competing with the words. It goes, and comes back when
   the walkthrough hands the page over. Lenovo's goal and the score rail
   both stay — the goal is what the ship is flying on. */
.is-touring-story .standings {
  opacity: 0;
  pointer-events: none;
}

/* ── story mode ────────────────────────────────────────────
   The first version, kept alongside for comparison: no dim,
   nothing pointed at, the campaign's own words set large over
   the scene. Same markup, different clothes. */

.tour--story {
  /* Clear of the goal bar, which it does not mention but does sit
     beside — and clear of the bar's readings, which hang off its right
     and are the part that actually reaches this far. Measured: the wider
     of the two ends 132px past the track's left edge, so the words start
     past that with room to spare. */
  left: calc(var(--rail-inset) + var(--tape-w) + 80px);
  right: var(--rail-inset);
  top: var(--nav-h, 0px);
  bottom: 0;
}

.tour--story .tour__spot { display: none; }

.tour--story .tour__card {
  position: static;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  width: min(34vw, 470px);
  height: 100%;
  padding: 44px 0 100px;
  border: 0;
  border-radius: 0;
  background: none;
  backdrop-filter: none;
  box-shadow: none;
}

/* Carries its own ground, like the line below it: there is no dim in
   this version, and a red label at 11px over a passing world is not a
   label anybody can read. */
.tour--story .tour__eyebrow {
  font-size: 11px;
  margin-bottom: 14px;
  text-shadow: 0 0 18px rgba(6, 8, 20, 0.95), 0 0 6px rgba(6, 8, 20, 0.9);
}

/* The opening eyebrow is the campaign lockup, not a label for one, so it
   is set at the size of a mark rather than of a label. Everything about
   how the mark itself is drawn lives with .lockup in site.css — this
   only says how big and how bright. */
.tour--story .tour__eyebrow--lockup {
  margin-bottom: 20px;
  font-size: clamp(20px, 2.2vw, 32px);
  text-transform: none;
  color: var(--bone);
  text-shadow: 0 0 34px rgba(6, 8, 20, 0.9);
}

.tour--story .tour__line {
  margin: 0 auto auto 0;
  font-family: "Archivo Black", var(--sign), sans-serif;
  font-weight: 400;
  font-size: clamp(24px, 2.9vw, 42px);
  line-height: 1.08;
  letter-spacing: -0.02em;
  /* No dim behind it, so the words carry their own ground. */
  text-shadow: 0 0 40px rgba(6, 8, 20, 0.9), 0 2px 30px rgba(6, 8, 20, 0.8);
}

.tour--story .tour__line b { font-weight: 400; color: var(--ember); }
.tour--story .tour__line sup { font-size: 0.4em; }


.tour--story .tour__foot { justify-content: flex-start; gap: 20px; }

@media (max-width: 720px) {
  .tour__card { width: calc(100vw - 32px); }

  /* Still clear of the goal bar, and of its two readouts — those hang off
     the right of the track on a phone, so clearing the track alone is not
     clearing the bar. This used to sit flush at --rail-inset, which is
     exactly where the track starts, and put the words straight over it.

     There is no room left for a second column beside that, so the words
     take everything to the right of it and come down a size to suit. */
  .tour--story { left: calc(var(--rail-inset) + var(--tape-w) + 52px); }
  /* The foot clears the telemetry rail rather than sitting in it — Skip
     is a control and the odometer is a readout, and they were landing on
     each other. */
  .tour--story .tour__card { width: 100%; padding: 16px 0 calc(var(--rail-h) + 18px); }
  .tour--story .tour__line { font-size: clamp(18px, 5.4vw, 26px); }
  .tour--story .tour__eyebrow--lockup { font-size: clamp(17px, 5vw, 24px); }
}

/* ── the finish ────────────────────────────────────────────
   The tour page's last note, held over the whole stage while the ship
   is still on station. The goal bar says the same thing in its own
   corner at the same moment — twice, in two places, because reaching
   the goal is what the whole piece has been building to and a 10px
   label beside a full column is not the size of it. */
.finale {
  position: absolute;
  inset: 0;
  z-index: 45;
  display: grid;
  place-content: center;
  justify-items: center;
  gap: 18px;
  text-align: center;
  /* Held above the vehicle rather than over it. The ship is still on
     station at this point and about to leave, and the last thing the
     piece should do is hide the thing it has spent half a minute
     flying. Padded rather than positioned, so it stays centred in what
     is left. */
  padding: 0 var(--stage-edge) 38%;
  opacity: 0;
  transform: translateY(12px);
  pointer-events: none;
  transition: opacity 640ms ease, transform 760ms cubic-bezier(0.2, 0.8, 0.3, 1);
}

.finale.is-on { opacity: 1; transform: none; }

.finale__head {
  margin: 0;
  font-family: "Archivo Black", var(--sign), sans-serif;
  font-weight: 400;
  font-size: clamp(40px, 7.4vw, 96px);
  line-height: 0.94;
  letter-spacing: -0.035em;
  color: var(--bone);
  /* Its own ground, and a little of the ember it just filled the bar
     with — there is no dim behind this, only the field. */
  text-shadow:
    0 0 50px rgba(6, 8, 20, 0.95),
    0 0 26px rgba(6, 8, 20, 0.9),
    0 0 120px rgba(255, 138, 61, 0.4);
}

.finale__sub {
  margin: 0;
  font-family: var(--tele);
  font-size: clamp(10px, 1.15vw, 13px);
  font-weight: 500;
  letter-spacing: 0.26em;
  text-transform: uppercase;
  color: var(--ember);
  text-shadow: 0 0 24px rgba(6, 8, 20, 0.95);
}

/* ── the hand-over to the landing page ─────────────────────
   The tour page's ending. Painted in the landing page's own ground
   colour rather than in this page's black, so the two meet on one shade
   and the arrival reads as the same room rather than a cut. --void-rgb
   comes from site.css; the fallback is that same aubergine. */
.depart {
  position: fixed;
  inset: 0;
  z-index: 200;
  background: rgb(var(--void-rgb, 20, 8, 22));
  opacity: 0;
  pointer-events: none;
  transition: opacity 900ms ease-in;
}

.depart.is-gone { opacity: 1; }

/* ── focus ─────────────────────────────────────────────── */

:focus-visible { outline: 2px solid var(--signal); outline-offset: 2px; }

/* ── dimmed chrome while flight-testing ────────────────── */

/* The standings go with the rest of it. They are the selling
   leaderboard; the run has a score of its own and the two have nothing
   to do with each other, so leaving the board up during a flight put
   two unrelated scoreboards on one screen. */
.is-testing .rail--bottom,
.is-testing .tape,
.is-testing .standings,
.is-testing .notice { opacity: 0; pointer-events: none; transition: opacity 220ms; }

.rail--bottom { transition: opacity 320ms; }

/* The bar's own transition covers its length as well as its fade: on a
   phone it grows and shrinks as the board is closed and opened, and it
   should do that with the board rather than snapping first. */
.tape {
  transition: opacity 320ms,
              top 420ms cubic-bezier(0.2, 0.8, 0.3, 1),
              bottom 420ms cubic-bezier(0.2, 0.8, 0.3, 1);
}

/* The run HUD gets the full width back now nothing is docked right. */
.is-testing .run { padding-right: var(--rail-inset); }

/* ── small screens ─────────────────────────────────────── */

@media (max-width: 720px) {
  :root { --stage-edge: 14px; --tape-w: 34px; }


  /* The rail is a known height on a phone because the standings dock on
     top of it and have to know where its lip is. Measured, not guessed:
     the wrapped two-row layout comes out at 119px at every width from
     320 up, so this is that with a pixel in hand. */
  :root { --rail-h: 120px; }

  .rail--bottom {
    height: var(--rail-h);
    flex-wrap: wrap;
    gap: 6px 16px;
    padding: 10px var(--stage-edge) 14px;
    /* Opaque, so the rail and the board docked on it read as one block
       of chrome. The desktop gradient let the star field through its top
       edge, which with a panel sitting directly above it left a seam of
       sky between two solid things. */
    background: rgba(6, 8, 20, 0.94);
  }
  .cell { flex: 1 1 40%; }
  .cell--score { order: -1; flex: 1 1 100%; }
  .odometer { font-size: 32px; }

  /* `.tour` itself is left alone. It is the fixed frame the guide's card
     is positioned inside, and placeCard() measures in viewport
     coordinates — inset it and every card it places lands off by that
     much. The story version's own frame is set above; the guide's card
     places itself. */

  /* The bar runs all the way down to the rail, and stops short only for
     a board that is actually docked at the foot — so it lengthens when
     the board is closed and shortens when it is opened, rather than
     holding a gap for something that may not be there. On the
     walkthrough's page there is no board at all and it always runs long.

     It moves with the panel, on the same easing, so the two read as one
     thing making room for the other. */
  .tape { top: 20px; bottom: calc(var(--rail-h) + 14px); }
  .is-board-open .tape { bottom: calc(var(--rail-h) + 22vh + 12px); }

  /* And on the walkthrough's page the words own the top of the screen —
     on a phone there is no "beside" for them to sit in — so the bar
     starts a little under them rather than up beside the nav. Only a
     little: the words are their own column to the right of the track,
     so the bar is not waiting on them, and dropping it a third of the
     way down left it stubby against a tall phone. */
  .is-tour-page .tape { top: 12%; }
  .tape__track { width: 26px; }
  .tape__pointer { width: 38px; }
  .tape__num { left: 44px; font-size: 10px; }
  .tape__tick--major { width: 11px; }

  /* Both readings still fit, but not both lines of the outside one —
     the ship is centred a hundred pixels away. The count keeps its place
     beside the bar and the share follows it, and what each of them is is
     already on the rail below. */
  .tape__read { left: 44px; font-size: 8.5px; letter-spacing: 0.1em; }
  .tape__read b, .tape__read em { display: none; }
  .tape__pointer--team .tape__num { bottom: 8px; }
  .tape__pointer--team .tape__read { bottom: -5px; }
  .tape__pointer--you .tape__num { top: -5px; }
  .tape__pointer--you .tape__read { top: 8px; }

  /* Docked, not floating. On a desktop the board is a column beside the
     scene; squeezed onto a phone it became a card hanging over the star
     field, and a panel with nothing under it reads as something in the
     way of the view rather than part of the instrument.

     So on a phone it locks to the foot of the stage instead: full width,
     square, sitting on the telemetry rail as one block of chrome along
     the bottom edge. The ship, the goal bar and the sky above them get
     the whole upper screen, which is the half worth looking at. Short
     enough to leave the ship clear of it at rest, and it scrolls inside
     itself rather than growing. */
  .standings {
    top: auto;
    bottom: var(--rail-h);
    left: 0;
    right: 0;
    width: auto;
    max-height: 22vh;
    padding: 11px var(--stage-edge) 12px;
    border: 0;
    border-top: 1px solid var(--hair);
    border-radius: 0;
    background: rgba(6, 8, 20, 0.94);
  }

  /* Docked to the foot here, so it leaves downward rather than sideways. */
  .standings.is-shut { transform: translateY(16px); }

  /* The vehicle takes proportionally more of a phone, so the finish has
     to sit higher again to stay off its nose. A fraction of the stage
     rather than a fixed height, so it holds on a short screen too. */
  .finale { padding-bottom: 58%; }

  .standings__row { grid-template-columns: 21px 1fr auto; gap: 7px; font-size: 11.5px; }

  /* The rail is tight on a phone; the caret carries the affordance and
     the word underneath would push the row to three lines. */
  .cell__hint { display: none; }

  .run { top: 16px; }
  .notice { bottom: 124px; right: var(--stage-edge); }
}

/* ── reduced motion ────────────────────────────────────── */

@media (prefers-reduced-motion: reduce) {
  .panel { animation: none; }
}
