/* The single night theme for Terminal FLX: a Roy Andersson arrivals hall at idle - steel, bone, stillness.
   Pink exists only in the clearly-marked PINK GATE section at the bottom, scoped under body[data-state]. */

/* ------------------------------------------------------------------ */
/* Tokens (SPEC section 4 - decided, not invented)                     */
/* ------------------------------------------------------------------ */

:root {
  --rosa: #F72585;
  --rosa-hot: #FF4DA6;
  --steel-900: #141619;
  --steel-800: #1B1E22;
  --steel-700: #23262B;
  --steel-500: #4A4F55;
  --steel-400: #8A9199;
  --hall-pale: #D9D6CC;
  --hall-sick: #C9CDC0;
  --bone: #EDEAE2;

  --font-identity: "Jost", "Futura PT", Futura, "Century Gothic", "Trebuchet MS", sans-serif;
  --font-data: "IBM Plex Mono", ui-monospace, "SF Mono", "Cascadia Mono", Consolas, monospace;

  --hairline: 1px solid var(--steel-700);
  --hall-gutter: clamp(1.25rem, 4vw, 3rem);
}

/* Registered so the gate choreography is a genuine custom-property crossfade:
   the browser interpolates these numbers, and everything else derives from them. */
@property --blackout {
  syntax: "<number>";
  inherits: false;
  initial-value: 0;
}
@property --flood {
  syntax: "<number>";
  inherits: false;
  initial-value: 0;
}

/* ------------------------------------------------------------------ */
/* Ground - explicit backgrounds everywhere; nothing inherits the void */
/* ------------------------------------------------------------------ */

*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  background: var(--steel-900);
}

body {
  margin: 0;
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  background: var(--steel-900);
  color: var(--bone);
  font-family: var(--font-identity);
  line-height: 1.5;
}

/* The hall set, painted once and never moved: a dark wall, a skirting
   hairline, a slightly lighter floor. Fixed, like a locked-off camera. */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -1;
  background: linear-gradient(
    to bottom,
    var(--steel-900) 0%,
    var(--steel-900) calc(62% - 1px),
    var(--steel-700) calc(62% - 1px),
    var(--steel-700) 62%,
    var(--steel-800) 62%,
    var(--steel-800) 100%
  );
}

h1,
h2,
p {
  margin: 0;
}

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

::selection {
  background: var(--hall-pale);
  color: var(--steel-900);
}

/* Before flap.mjs defines the element, show the target text plainly so the
   page still reads; once defined, the component's own light-DOM cells take over. */
flap-text {
  display: inline-flex;
  font-family: var(--font-data);
  font-variant-numeric: tabular-nums;
}
flap-text:not(:defined)::before {
  content: attr(data-text);
}

/* ------------------------------------------------------------------ */
/* The hall head - codewords high on the wall, generous air around them */
/* ------------------------------------------------------------------ */

.hall-head {
  padding: clamp(4rem, 15vh, 10rem) var(--hall-gutter) 0;
  text-align: center;
}

.codeword-main {
  font-size: clamp(1.3rem, 3.4vw, 2.2rem);
}

.codeword-sub {
  margin-top: 0.9rem;
  font-size: clamp(0.85rem, 1.8vw, 1.15rem);
  color: var(--hall-sick);
}

.codeword-main,
.codeword-sub {
  display: block;
}

.marque {
  margin-top: 2.75rem;
  font-size: 0.72rem;
  font-weight: 400;
  letter-spacing: 0.5em;
  text-indent: 0.5em; /* balances the trailing tracking so centring holds */
  color: var(--steel-400);
}

/* ------------------------------------------------------------------ */
/* The departures board - a flat steel panel hung on the wall           */
/* ------------------------------------------------------------------ */

.hall {
  flex: 1 0 auto;
  display: grid;
  align-content: center;
  padding: clamp(2.5rem, 8vh, 6rem) var(--hall-gutter);
}

.departures {
  /* The sign shrinks to the machine it labels: the DEPARTURES plate sits on
     the board's own left edge, and the pair is centred in the hall as one
     object — a title stranded at a container edge belongs to no board. */
  width: fit-content;
  max-width: min(100%, 58rem);
  margin-inline: auto;
}

.board-title {
  margin-bottom: 0.9rem;
  font-size: 0.68rem;
  font-weight: 500;
  letter-spacing: 0.45em;
  text-transform: uppercase;
  color: var(--steel-400);
}

#board {
  /* The board is hardware: every column track is the exact width of its
     built modules (max-content — all rows carry identical module counts, so
     nothing can drift), and the steel frame ends where the modules end. No
     stretched tracks, no dead plate; narrow screens drop columns below. */
  --board-cols:
    var(--w-time, 5ch) var(--w-dest, 28ch) var(--w-flight, 10ch)
    var(--w-gate, 2ch) var(--w-bpm, 3ch) var(--w-status, 10ch);
  --board-gap: 1.5ch;
  width: max-content;
  max-width: 100%;
  margin-inline: auto;
  min-height: 9rem;
  background: var(--steel-800);
  border: var(--hairline);
  overflow-x: auto;
}

/* Generic row contract: board.mjs adds [role=row] rows, whatever their class. */
#board [role="row"] {
  display: grid;
  grid-template-columns: var(--board-cols);
  gap: 0 var(--board-gap);
  align-items: center;
  padding: 0.85rem clamp(0.9rem, 2.5vw, 1.5rem);
  font-family: var(--font-data);
  font-size: clamp(0.72rem, 1.5vw, 0.9rem);
  font-variant-numeric: tabular-nums;
  color: var(--hall-pale);
}

#board [role="row"] > * {
  min-width: 0;
  overflow: hidden;
}

/* Only rows made keyboard-reachable by board.mjs get interactive affordances. */
#board [role="row"][tabindex] {
  cursor: pointer;
}
/* Selecting a row is a PHYSICAL event on the machine, and hardware answers in
   two states at once: the panel behind the modules lights, and the module
   fronts sit a shade under it — the shallow shadow a raised plate casts on
   its own panel, never a pit. One without the other reads as a web page
   tinting a div; with both, the flaps stay proud under the cursor. */
#board [role="row"][tabindex]:hover,
#board [role="row"][tabindex]:focus-visible {
  background: color-mix(in srgb, var(--steel-700) 78%, var(--steel-500));
  color: var(--bone);
}
#board [role="row"][tabindex]:focus-visible {
  outline: 2px solid var(--bone);
  outline-offset: -2px;
}
#board [role="row"][tabindex]:hover flap-text .ft-cell,
#board [role="row"][tabindex]:hover flap-word .fw-cell,
#board [role="row"][tabindex]:focus-visible flap-text .ft-cell,
#board [role="row"][tabindex]:focus-visible flap-word .fw-cell {
  background: #26292e;
  background: color-mix(in srgb, var(--steel-700) 92%, var(--steel-500));
}
#board [role="row"] + [role="row"] {
  border-top: var(--hairline);
}

.board-head {
  border-bottom: var(--hairline);
}
.board-row--head [role="columnheader"] {
  font-family: var(--font-identity);
  font-size: 0.6rem;
  font-weight: 500;
  letter-spacing: 0.3em;
  color: var(--steel-400);
}

/* Right-align the numeric columns, headers included, tabular fashion. */
.board-cell--gate,
.board-cell--bpm,
.board-cell--time {
  justify-self: end;
  text-align: right;
}

/* The status field is a one-piece word plate (flap-word) — module steel like
   every other cell; the printed chips it replaces are gone with the machine
   that never existed. */

.noscript-note {
  width: min(100%, 58rem);
  margin: 0 auto 1.5rem;
  padding: 1rem 1.25rem;
  border: 1px solid var(--steel-500);
  background: var(--steel-800);
  font-family: var(--font-data);
  font-size: 0.8rem;
  color: var(--hall-pale);
}

/* ------------------------------------------------------------------ */
/* The skirting - footer line and the always-visible calm control       */
/* ------------------------------------------------------------------ */

.skirting {
  border-top: var(--hairline);
  background: var(--steel-900);
  padding: 1.1rem var(--hall-gutter) 1.2rem;
  padding-right: max(var(--hall-gutter), 9rem); /* room for the fixed calm button */
}

.skirting-line {
  font-size: 0.62rem;
  letter-spacing: 0.4em;
  text-transform: uppercase;
  color: var(--steel-400);
}

/* Fixed above the gate layer so calm can be reached mid-flight, when the
   strobe cap matters most - a safety control is useless behind the show. */
#calm {
  position: fixed;
  right: clamp(0.9rem, 3vw, 1.8rem);
  bottom: clamp(0.9rem, 3vw, 1.5rem);
  z-index: 20;
  font-family: var(--font-data);
  font-size: 0.68rem;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--bone);
  background: var(--steel-800);
  border: 1px solid var(--steel-500);
  border-radius: 0;
  padding: 0.6rem 1.05rem;
  cursor: pointer;
}
#calm:hover {
  border-color: var(--steel-400);
}
#calm[aria-pressed="true"] {
  background: var(--bone);
  border-color: var(--bone);
  color: var(--steel-900);
}

/* ------------------------------------------------------------------ */
/* The gate - deepest-steel ground, scene canvas, flap HUD              */
/* ------------------------------------------------------------------ */

#gate {
  position: fixed;
  inset: 0;
  z-index: 10;
  visibility: hidden;
  /* The darkest value the token set owns; pure black is not in SPEC section 4. */
  background: var(--steel-900);
  --blackout: 0;
  opacity: var(--blackout);
  /* Exit choreography: the black ground eases back to the hall, then the
     layer leaves the accessibility tree once the fade has finished. */
  transition: --blackout 360ms ease-out 120ms, visibility 0s 480ms;
}

/* The player belt-and-braces the [hidden] attribute, but display:none would
   kill the crossfade in both directions; visibility above does the real hiding.
   Pointer events die with [hidden] at once: the layer stays visible for the
   480ms exit fade, and a click during it must reach the board underneath. */
#gate[hidden] {
  display: block;
  pointer-events: none;
}

/* The flood layer sits under the canvas (DOM order) and carries no colour
   at rest; the PINK GATE section below gives it a wash - and a value. */
#gate::before {
  content: "";
  position: absolute;
  inset: 0;
  --flood: 0;
  opacity: var(--flood);
  background: transparent;
  pointer-events: none;
}

#gate-canvas {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
}

/* The exit is a real button, not a caption: Escape serves the keyboard, but a
   touch passenger needs a door handle. Steel at rest; only the pink section
   below may recolour it, and 2.75rem keeps the tap target at 44px. */
.gate-hint {
  position: absolute;
  top: 1rem;
  right: 1rem;
  z-index: 2;
  display: inline-flex;
  align-items: center;
  min-height: 2.75rem;
  padding: 0 1.1rem;
  font-family: var(--font-data);
  font-size: 0.66rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--steel-400);
  /* 85% backing keeps the resting steel text above 4.5:1 even with a hot-rosa
     synth beam standing directly behind the button. */
  background: color-mix(in srgb, var(--steel-900) 85%, transparent);
  border: 1px solid var(--steel-700);
  border-radius: 0;
  cursor: pointer;
}
.gate-hint:hover {
  color: var(--bone);
  border-color: var(--steel-500);
}

#gate-hud {
  position: absolute;
  inset-inline: 0;
  bottom: 0;
  z-index: 2;
  display: flex;
  justify-content: center;
  align-items: end;
  gap: clamp(1.75rem, 9vw, 6.5rem);
  padding: 1.5rem var(--hall-gutter) 1.7rem;
  border-top: 1px solid var(--steel-700);
  background: linear-gradient(to top, color-mix(in srgb, var(--steel-900) 72%, transparent), transparent);
}

.hud-slot {
  display: grid;
  gap: 0.5rem;
  justify-items: center;
  /* Each slot carries its own solid panel, exactly as the flap cells do: the
     labels turn rosa-hot in flight, and the translucent HUD wash alone cannot
     hold 4.5:1 over the flood — an opaque steel ground can, in any scene. */
  padding: 0.5rem 0.6rem 0.55rem;
  background: var(--steel-900);
  border: 1px solid var(--steel-700);
}

.hud-label {
  font-family: var(--font-identity);
  font-size: 0.6rem;
  font-weight: 500;
  letter-spacing: 0.45em;
  text-indent: 0.45em;
  text-transform: uppercase;
  color: var(--steel-400);
}

#gate-hud flap-text {
  font-size: clamp(1.05rem, 2.6vw, 1.55rem);
}

/* ------------------------------------------------------------------ */
/* THE PINK GATE - nothing below this line renders at idle.             */
/* Every selector is scoped under body[data-state="boarding"] or        */
/* body[data-state="airborne"]; only here may --rosa / --rosa-hot appear. */
/* ------------------------------------------------------------------ */

/* Entry, phase one (0-240ms): the hall dies to the deepest steel. */
body[data-state="boarding"] #gate,
body[data-state="airborne"] #gate {
  visibility: visible;
  --blackout: 1;
  transition: --blackout 240ms ease-in, visibility 0s;
}

/* Entry, phase two (240-700ms): pink floods the darkened ground.
   On exit these selectors stop matching, so the wash is cut, not faded -
   rosa must never render at idle, and a hard cut is absolute compliance. */
body[data-state="boarding"] #gate::before,
body[data-state="airborne"] #gate::before {
  --flood: 1;
  transition: --flood 460ms cubic-bezier(0.16, 0.84, 0.44, 1) 240ms;
  background:
    radial-gradient(110% 85% at 50% 112%, var(--rosa-hot) 0%, transparent 55%),
    radial-gradient(170% 130% at 50% 105%, var(--rosa) 0%, color-mix(in oklab, var(--rosa), var(--steel-900) 80%) 72%, var(--steel-900) 100%);
}

body[data-state="boarding"] #gate-hud,
body[data-state="airborne"] #gate-hud {
  border-top-color: color-mix(in srgb, var(--rosa) 45%, transparent);
}

body[data-state="boarding"] .hud-label,
body[data-state="airborne"] .hud-label {
  color: var(--rosa-hot);
}

body[data-state="boarding"] #gate :focus-visible,
body[data-state="airborne"] #gate :focus-visible {
  outline-color: var(--rosa-hot);
}

/* The exit button warms to the room in flight - hover only, so the resting
   control stays legible steel over whatever the scene is painting. */
body[data-state="boarding"] .gate-hint:hover,
body[data-state="airborne"] .gate-hint:hover {
  color: var(--rosa-hot);
  border-color: color-mix(in srgb, var(--rosa) 55%, transparent);
}

/* ------------------------------------------------------------------ */
/* Narrow hall - the board keeps its alignment by redefining the grid   */
/* ------------------------------------------------------------------ */

@media (max-width: 44rem) {
  #board {
    /* The tempo plate — this terminal's own column, where a real board
       carries the aircraft type — comes off the wall first. */
    --board-cols:
      var(--w-time, 5ch) var(--w-dest, 28ch) var(--w-flight, 10ch)
      var(--w-gate, 2ch) var(--w-status, 10ch);
    --board-gap: 1.25ch;
  }
  .board-cell--bpm {
    display: none;
  }
  .hall-head {
    padding-top: clamp(3rem, 12vh, 5rem);
  }
  .skirting {
    padding-right: var(--hall-gutter);
    padding-bottom: 3.4rem; /* the fixed calm button floats below the line */
  }
}

/* ------------------------------------------------------------------ */
/* The phone build - one line per flight, every module at full size    */
/* ------------------------------------------------------------------ */

/* A 390px screen holds 350px of board, a 375px one 335px. With the
   destination sign cut to twenty modules for this wall (board.mjs reads the
   same query when it builds the machine), time, destination and remarks at
   the smallest legible flap are 328px of modules, so every flight stays on
   ONE line, as a board's rows do: nothing is shrunk, no module is cut, and
   the frame still ends where the modules end. The flight number and the
   gate join the tempo plate off the wall - the name on the sign is the
   flight here. A screen narrower still scrolls the frame sideways rather
   than clipping a module. Without this the board pushes the layout viewport
   to 489px: fixed controls sit off the right edge on Android and the whole
   hall shrinks to 80% on iOS. */
@media (max-width: 34rem) {
  #board {
    --board-cols: var(--w-time, 5ch) var(--w-dest, 20ch) var(--w-status, 10ch);
    --board-gap: 1ch;
  }
  .board-cell--flight,
  .board-cell--gate {
    display: none;
  }
  .board-cell--status,
  .board-cell--remarks {
    justify-self: end;
  }
  /* The column headings stay on the wall: the head row's cells carry the
     same column classes as the data cells, so the flight and gate headings
     leave with their columns and TIME, DESTINATION and REMARKS stand over
     the three that remain. */
  /* The calm control joins the skirting. Floating free below the line it is
     the largest plate on the screen and shoves the footer up above itself;
     here it sits IN the footer's band, at the height of the text and right
     of the words, sized to the type. It stays fixed — the element lives
     outside the inert hall so it keeps working mid-flight — and the band is
     built to its 44px tap height so the two line up when the page rests on
     the footer. Boarding and airborne below 30rem still send it top-left. */
  .skirting {
    display: flex;
    align-items: center;
    /* Border-box: the band is the plate's height plus the air above and
       below it, so the plate at bottom: 0.9rem spans exactly the content
       box the text line is centred in. */
    min-height: calc(2.75rem + 2 * 0.9rem);
    padding-block: 0.9rem;
    padding-right: calc(var(--hall-gutter) + 5rem);
  }
  #calm {
    right: var(--hall-gutter);
    bottom: 0.9rem;
    display: inline-flex;
    align-items: center;
    min-height: 2.75rem;
    padding: 0 0.9rem;
    font-size: 0.6rem;
  }
}

@media (max-width: 30rem) {
  #gate-hud {
    justify-content: space-between;
    gap: 1rem;
  }
  /* With the slots spread edge to edge, the bar cell stands exactly where
     the calm button floats, and the strobe cap ends up behind the very
     readout it is meant to be reached over. In flight the control moves to
     the top-left corner, opposite the exit: the two door handles frame the
     top edge at the same 44px tap height, and the HUD keeps the bottom to
     itself. Idle placement is untouched - the hall has no HUD to collide with. */
  body[data-state="boarding"] #calm,
  body[data-state="airborne"] #calm {
    top: 1rem;
    left: 1rem;
    right: auto;
    bottom: auto;
    display: inline-flex;
    align-items: center;
    min-height: 2.75rem;
    padding: 0 1.05rem;
  }
}

/* ------------------------------------------------------------------ */
/* Touch - the hardware answers a finger the way it answers a cursor   */
/* ------------------------------------------------------------------ */

/* Scoped to touch screens so a desktop pointer sees no change. The grey
   tap flash is a browser artefact, not a state of the machine; a finger
   held on a row must not turn the flaps into selectable text; and a
   second tap on the runway must never zoom the flight. Panning and pinch
   stay allowed everywhere - the page remains the visitor's to zoom. */
@media (hover: none) and (pointer: coarse) {
  #board [role="row"][tabindex],
  #calm,
  .gate-hint,
  .arrival-enter {
    -webkit-tap-highlight-color: transparent;
    touch-action: manipulation;
  }
  #board [role="row"] {
    -webkit-user-select: none;
    user-select: none;
    -webkit-touch-callout: none;
  }
  #gate {
    touch-action: manipulation;
  }
}

/* ------------------------------------------------------------------ */
/* Reduced motion - the boarding crossfade is skipped, states just swap */
/* ------------------------------------------------------------------ */

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    transition: none !important;
    animation: none !important;
  }
}

/* ------------------------------------------------------------------ */
/* The arrival door - covers the hall until the visitor steps inside.  */
/* Steel and bone only: nothing has boarded yet, so rosa has no right  */
/* to exist here. The dissolve is the visitor being admitted.          */
/* ------------------------------------------------------------------ */

#arrival {
  position: fixed;
  inset: 0;
  z-index: 30;
  display: grid;
  place-items: center;
  background: var(--steel-900);
  text-align: center;
  transition: opacity 1.5s ease 0.1s, visibility 0s linear 1.7s;
}

#arrival[data-entered] {
  opacity: 0;
  visibility: hidden;
}

.arrival-inner {
  display: grid;
  gap: 1.5rem;
  justify-items: center;
  padding: 2rem 1.25rem;
  transition: transform 1.5s ease 0.1s;
}

#arrival[data-entered] .arrival-inner {
  transform: translateY(-0.8rem);
}

.arrival-eyebrow {
  margin: 0;
  font-family: var(--font-data);
  font-size: 0.72rem;
  letter-spacing: 0.34em;
  text-transform: uppercase;
  color: var(--steel-400);
}

.arrival-title {
  font-size: clamp(1.5rem, 5vw, 2.2rem);
}

.arrival-codeword {
  margin: 0;
  font-family: var(--font-data);
  font-size: 0.82rem;
  letter-spacing: 0.2em;
  line-height: 2;
  color: var(--hall-pale);
}

.arrival-enter {
  margin-top: 1.25rem;
  padding: 0.95rem 1.9rem;
  font-family: var(--font-data);
  font-size: 0.8rem;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--bone);
  background: var(--steel-800);
  border: 1px solid var(--steel-500);
  border-radius: 2px;
  cursor: pointer;
  transition: border-color 0.3s ease, background 0.3s ease;
}

.arrival-enter:hover {
  border-color: var(--hall-pale);
  background: var(--steel-700);
}

.arrival-enter:focus-visible {
  outline: 2px solid var(--hall-pale);
  outline-offset: 4px;
}
