/*
 * whatsairborne.com
 *
 * Ink on a cool paper ground. The palette is the back of an aeronautical
 * chart, not a warm cream: the product's own screens carry the sky colour, so
 * the site around them stays neutral and lets them be the only thing with a
 * temperature. No gradients outside the demo frame, no shadows, no radii.
 */

:root {
  /* ---------------------------------------------------------------- ink */
  /* Warm cream, the product's own chrome family, rather than the cool
     chart-grey this started with: the site is the bezel around the screen. */
  --paper:        #efece3;
  --paper-lift:   #f7f4ec;   /* panels, table stripes */
  --paper-deep:   #e5e1d4;   /* wells, mounts */
  --ink:          #10161b;
  --ink-soft:     #444c54;
  --ink-faint:    #6d7378;
  --rule:         rgba(26, 22, 12, 0.16);
  --rule-strong:  rgba(26, 22, 12, 0.36);

  /* Used at roughly one per cent of the surface: the live indicator, and
     nothing else. Darkened from the product's --amber to hold AA on paper. */
  --signal:       #8a5510;

  /*
   * Inside the demo frame only: the product's own sky and its cream ink.
   *
   * The unit runs a brighter blue than this. On a wall that is right; on a
   * website the same cream on the same blue lands at about 2.8:1 by the bottom
   * of the gradient, which fails AA for the metadata line. So the gradient is
   * pulled a couple of shades deeper and stopped earlier. Same picture, and
   * the small type clears 4.5:1 top to bottom.
   */
  --sky-top:      #3f63bd;
  --sky-bottom:   #527ac9;
  --sky-ink:      #f4efdf;
  --sky-ink-soft: #ece6d4;
  --sky-rule:     rgba(244, 239, 223, 0.22);
  --sky-chrome:   #edeae1;

  --phosphor:     #57e58e;

  /*
   * Three faces, each with one job.
   *
   * Fraunces sets the page: a modern variable serif with a soft, slightly
   * optical character, where Newsreader read like a newspaper. Instrument
   * Serif carries the headlines, high contrast and quite large, which is
   * where it earns its keep. Jost is the unit's own face and stays exactly
   * where it was: labels, metadata, anything that appears on the display.
   */
  --display: "Fraunces", ui-serif, Georgia, "Times New Roman", serif;
  --head:    "Instrument Serif", "Fraunces", ui-serif, Georgia, serif;
  --label:   "Jost", "Futura", ui-sans-serif, system-ui, sans-serif;
  --mono:    "Jost", ui-sans-serif, system-ui, sans-serif;  /* figures and codes; the unit sets these in Jost too */

  --page:     1180px;
  --gutter:   clamp(1.25rem, 4vw, 3rem);
  --section:  clamp(3.5rem, 8vw, 7rem);
}

* { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--display);
  font-weight: 300;
  font-optical-sizing: auto;
  font-size: 1.09rem;
  line-height: 1.72;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

img, svg { display: block; max-width: 100%; }

a { color: inherit; text-decoration: none; }
p a, .prose a { text-decoration: underline; text-underline-offset: 0.18em; text-decoration-thickness: 1px; }
p a:hover, .prose a:hover { color: var(--ink-soft); }

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

::selection { background: var(--ink); color: var(--paper); }

/* ------------------------------------------------------------- primitives */

.wrap {
  max-width: var(--page);
  margin: 0 auto;
  padding-inline: var(--gutter);
}

.label {
  font-family: var(--label);
  font-weight: 400;
  font-size: 0.6875rem;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--ink-faint);
  margin: 0;
}

.mono {
  font-family: var(--mono);
  font-size: 0.78rem;
  letter-spacing: 0.04em;
  font-variant-numeric: tabular-nums;
}

.serif-lead {
  font-size: clamp(1.25rem, 2.1vw, 1.5rem);
  line-height: 1.55;
  font-weight: 300;
  color: var(--ink-soft);
}

h1, h2, h3, h4 {
  font-family: var(--head);
  font-weight: 400;
  margin: 0;
  letter-spacing: -0.005em;
}

h1 { font-size: clamp(2.75rem, 6.2vw, 4.75rem); line-height: 1.02; }
h2 { font-size: clamp(1.9rem, 3.5vw, 2.75rem); line-height: 1.12; }
h3 { font-size: clamp(1.25rem, 2vw, 1.5rem); line-height: 1.25; }

.prose p { margin: 0 0 1.35em; max-width: 62ch; }
.prose p:last-child { margin-bottom: 0; }

hr.rule { border: 0; border-top: 1px solid var(--rule); margin: 0; }

/* ------------------------------------------------------------------ nav */

.masthead {
  border-bottom: 1px solid var(--rule);
  background: var(--paper);
}

.masthead-inner {
  display: flex;
  align-items: center;
  gap: 2rem;
  min-height: 4.5rem;
}

.wordmark {
  font-family: var(--label);
  font-size: 0.9rem;
  letter-spacing: 0.42em;
  text-transform: uppercase;
  padding-right: 0.4rem;
}

.masthead nav {
  display: flex;
  gap: 1.75rem;
  margin-left: auto;
  align-items: center;
}

.masthead nav a {
  font-family: var(--label);
  font-size: 0.72rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-soft);
  padding-block: 0.4rem;
  border-bottom: 1px solid transparent;
}
.masthead nav a:hover { color: var(--ink); border-bottom-color: var(--ink); }
.masthead nav a[aria-current="page"] { color: var(--ink); border-bottom-color: var(--ink); }

.nav-toggle {
  display: none;
  margin-left: auto;
  background: none;
  border: 1px solid var(--rule-strong);
  color: var(--ink);
  font-family: var(--label);
  font-size: 0.7rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  padding: 0.5rem 0.85rem;
  cursor: pointer;
}

/* ---------------------------------------------------------------- buttons */

.btn {
  display: inline-block;
  text-decoration: none;
  font-family: var(--label);
  font-size: 0.72rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  padding: 0.95rem 1.7rem;
  border: 1px solid var(--ink);
  background: transparent;
  color: var(--ink);
  cursor: pointer;
  transition: background-color 120ms linear, color 120ms linear;
  text-align: center;
}
.btn:hover { background: var(--ink); color: var(--paper); }

.btn-solid { background: var(--ink); color: var(--paper); }
.btn-solid:hover { background: transparent; color: var(--ink); }

.btn-quiet { border-color: var(--rule-strong); color: var(--ink-soft); }
.btn-quiet:hover { background: transparent; border-color: var(--ink); color: var(--ink); }

.btn-sm { padding: 0.6rem 1.1rem; font-size: 0.66rem; }
.btn[disabled] { opacity: 0.4; cursor: not-allowed; }
.btn[disabled]:hover { background: transparent; color: var(--ink); }

/* --------------------------------------------------------------- sections */

.section { padding-block: var(--section); }
.section-tight { padding-block: calc(var(--section) * 0.6); }

/* The catalogue grid: a narrow left margin of letter-spaced capitals against
   a wide column of setting. Every section on the site is filed the same way,
   which is the structural idea, a wall of labelled plates. */
.filed {
  display: grid;
  grid-template-columns: 8.5rem minmax(0, 1fr);
  gap: clamp(1.5rem, 4vw, 3.5rem);
  align-items: start;
}
.filed > .label { padding-top: 0.55rem; }


/* ------------------------------------------------------------------ hero */

/* The product has to be on screen without scrolling, so the hero is one
   screenful of headline and nothing more: the sentence and the button sit
   beside the title rather than under it. */
.hero { padding-top: clamp(1.75rem, 3.5vw, 3rem); }

.hero-grid {
  display: grid;
  grid-template-columns: minmax(0, 1.25fr) minmax(0, 1fr);
  gap: clamp(1.5rem, 4vw, 3.5rem);
  align-items: end;
}
.hero-aside { padding-bottom: 0.35rem; }
@media (max-width: 900px) {
  .hero-grid { grid-template-columns: 1fr; align-items: start; }
  .hero-aside { padding-bottom: 0; }
}

.hero-title {
  max-width: 20ch;
  margin-bottom: 1.5rem;
}

.hero-sub {
  max-width: 46ch;
  font-size: clamp(1.1rem, 1.7vw, 1.3rem);
  font-weight: 300;
  color: var(--ink-soft);
  line-height: 1.6;
  margin: 0;
}

/* -------------------------------------------------------------- the frame */

/*
 * The signature: the product itself, mounted like a print.
 *
 * The rules below are lifted from the application's own globals.css rather
 * than reinterpreted, same class names (.oh-row, .oh-art, .oh-headline,
 * .oh-meta), same 46%/1fr split, same multiples of --u, same separator
 * handling. If the unit's layout changes, the diff between the two files is
 * the whole job.
 *
 * The one structural difference: the unit sizes --u from the viewport, because
 * it owns the whole screen. Here the miniature sizes it from its own width, so
 * the composition stays proportional inside a page that has other things on
 * it.
 */
/*
 * A picture frame: black moulding, cream mount board, then the display. The
 * hairline box it replaces said "screenshot"; this says "the thing hanging in
 * the hallway", which is what is actually being sold.
 *
 * Flat, with no drop shadow: the frame reads as a frame because of the mount
 * inside it, not because of a fake light source.
 */
.mount {
  margin-bottom: 0;
  border: clamp(9px, 0.9vw, 15px) solid #10161b;
  background: #f6f3ea;
  padding: clamp(0.9rem, 2vw, 2rem);
  /* The bevel where mount board meets the picture. One hairline, inset. */
  box-shadow: inset 0 0 0 1px rgba(16, 22, 27, 0.16);
}

/* The chrome bar is part of the product, so it sits inside the mount with the
   display rather than floating on the board. */
.mount .chrome { border-bottom: 1px solid rgba(16, 22, 27, 0.12); }

/* The unit's top bar, reproduced rather than reinterpreted. */
.chrome {
  display: flex;
  align-items: center;
  gap: clamp(0.9rem, 2vw, 1.6rem);
  background: var(--sky-chrome);
  color: var(--ink);
  padding: 0.55rem clamp(0.7rem, 1.6vw, 1.1rem);
  font-family: var(--label);
  font-weight: 300;
  font-size: clamp(0.72rem, 1.1vw, 0.9rem);
}
.chrome-screen {
  color: var(--ink-soft);
  white-space: nowrap;
  background: none;
  border: 1px solid transparent;
  padding: 0.1rem 0.45rem;
  margin: 0;
  font: inherit;
  letter-spacing: inherit;
  cursor: pointer;
}
.chrome-screen:hover { color: var(--ink); }
.chrome-screen.on {
  color: var(--ink);
  border: 1px solid var(--ink);
  padding: 0.1rem 0.45rem;
}
.chrome-screen.on::before { content: "\2022 "; }
.chrome-right {
  margin-left: auto;
  display: flex;
  align-items: baseline;
  gap: clamp(0.7rem, 1.6vw, 1.2rem);
  color: var(--ink-faint);
  font-size: 0.62rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
}
.chrome-clock {
  color: var(--ink);
  font-size: clamp(0.8rem, 1.3vw, 1.05rem);
  letter-spacing: 0.02em;
  text-transform: none;
}

/*
 * Portrait. A tall panel is a real configuration, not a preview trick: the
 * unit finds a 9:16 screen, shows one aircraft instead of three, and gives it
 * the room. The frame narrows to match, because a portrait picture in a
 * landscape frame is neither.
 */
.mount[data-orient="portrait"] {
  max-width: min(33rem, 100%);
  margin-inline: auto;
}

/* The chrome has the same five screens in half the width, so it loses the
   settings and the clock rather than shrinking everything to nothing. */
.mount[data-orient="portrait"] .chrome { gap: 0.55rem; font-size: 0.66rem; padding-inline: 0.6rem; }
.mount[data-orient="portrait"] .chrome-screen { padding: 0.1rem 0.3rem; }
.mount[data-orient="portrait"] .chrome-right { display: none; }

.plate[data-orient="portrait"] {
  aspect-ratio: 9 / 14;
  /* A tall panel is narrow, and --u comes off the width. Without lifting the
     scale the metadata lands at five pixels: the same trade the windowed
     landscape frame makes, further along. */
  --scale: 2.1;
}
.plate[data-orient="portrait"] .oh-row {
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: auto auto;
  gap: calc(var(--u) * 120);
  justify-items: start;
}
.plate[data-orient="portrait"] .oh-art { justify-content: flex-start; }
/* One aircraft with the whole panel to itself can be drawn much larger, which
   is the entire point of the tall configuration. */
.plate[data-orient="portrait"] .oh-art img { max-width: 92%; }
.plate[data-orient="portrait"] .oh-headline { white-space: normal; }
.plate[data-orient="portrait"] .oh-rows { justify-content: center; }

/*
 * The board stacks on a tall panel. Two columns at this width truncated the
 * place names to "GLASG..." and "MARSEI...", and a board that cannot say
 * where the aeroplane went is not a board. Direction still alternates the
 * text alignment, so the arrivals-and-departures reading survives.
 */
.plate[data-orient="portrait"] .bd-row {
  grid-template-columns: minmax(0, 1fr);
  gap: calc(var(--u) * 40);
}
.plate[data-orient="portrait"] .bd-row.in .bd-art,
.plate[data-orient="portrait"] .bd-row.out .bd-art { justify-content: flex-start; }
.plate[data-orient="portrait"] .bd-art { order: 2; }
.plate[data-orient="portrait"] .bd-art img { max-width: 62%; }
.plate[data-orient="portrait"] .bd-row.in .bd-text,
.plate[data-orient="portrait"] .bd-row.out .bd-text { text-align: left; }
.plate[data-orient="portrait"] .bd-row.in .bd-meta { justify-content: flex-start; }
.plate[data-orient="portrait"] .bd-place { white-space: normal; }
.plate[data-orient="portrait"] .bd-rows { justify-content: space-evenly; }
.plate[data-orient="portrait"] .bd { padding-block: calc(var(--u) * 60); }

.plate {
  position: relative;
  container-type: inline-size;
  /* 16:10, which is what the unit reports as its most common panel and what
     the screenshots were taken at. */
  aspect-ratio: 16 / 10;
  background: linear-gradient(to bottom, var(--sky-top), var(--sky-bottom));
  color: var(--sky-ink);
  overflow: hidden;
  display: flex;
  flex-direction: column;

  /*
   * The application's unit, re-based.
   *
   * There --u is 0.0219vmin times the owner's display scale. Here the frame is
   * pinned to 16:9, so the smaller dimension is the height and the height is
   * 0.5625 of the width: 0.0219% of that is 0.0123% of the width, or
   * 0.0123cqw. The 1.6 multiplier is the owner's display scale, a television
   * three metres away needs everything larger than a browser at arm's length,
   * and this is the same control, set the other way.
   */
  --scale: 1.6;
  --u: calc(0.0123cqw * var(--scale));
  --text-display: calc(var(--u) * 330);
  --text-xs: calc(var(--u) * 62);
}

.plate-head {
  display: flex;
  align-items: baseline;
  gap: 1rem;
  padding: calc(var(--u) * 200) calc(var(--u) * 300) 0;
}

.plate-head .label { color: var(--sky-ink); opacity: 0.7; }

.live {
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  font-family: var(--label);
  font-size: 0.6875rem;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: #f7d9a0;
}

.live-dot {
  width: 6px; height: 6px;
  background: #f7d9a0;
  border-radius: 50%;
  animation: pulse 3s ease-in-out infinite;
}
@keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.25; } }

/* The thirty-second poll, drawn. */
.tick {
  height: 1px;
  background: rgba(247, 239, 221, 0.25);
  margin: calc(var(--u) * 120) calc(var(--u) * 300) 0;
  position: relative;
  overflow: hidden;
}
.tick span {
  position: absolute;
  inset: 0 auto 0 0;
  width: 0;
  background: rgba(247, 239, 221, 0.7);
}

/* ---- from the application, verbatim in structure ---- */

.oh-rows {
  list-style: none;
  margin: 0;
  padding: calc(var(--u) * 170) calc(var(--u) * 300);
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  justify-content: space-around;
  gap: calc(var(--u) * 190);
}

.oh-row {
  display: grid;
  grid-template-columns: 46% 1fr;
  align-items: center;
  gap: calc(var(--u) * 260);
  min-height: 0;
  animation: oh-in 900ms cubic-bezier(0.2, 0, 0.1, 1) both;
  transition: opacity 700ms ease;
}
.oh-row.swap { opacity: 0; }

@keyframes oh-in {
  from { opacity: 0; transform: translateY(calc(var(--u) * 90)); }
  to { opacity: 1; transform: none; }
}

.oh-art {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 0;
  height: 100%;
}

.oh-art img {
  max-width: 100%;
  max-height: 100%;
  width: auto;
  height: auto;
  object-fit: contain;
  display: block;
  /* No fixed ratio: every livery now fills the width of its own canvas, and
     the canvases differ in height because a helicopter is not the shape of a
     777. Letting each file keep its own proportions is what makes them all
     come out the same visual size. */
}

.oh-text {
  display: flex;
  flex-direction: column;
  gap: calc(var(--u) * 60);
  min-width: 0;
}

/*
 * The destination, at display size.
 *
 * It must never be truncated. "BRIZE NO..." is the one thing this line cannot
 * say: the whole screen exists to tell you where the aeroplane is going, and
 * a name clipped mid-word is worse than a smaller name. Long names step down
 * in size instead, and only wrap if even the smallest step will not fit.
 */
.oh-headline {
  font-family: var(--label);
  font-size: var(--text-display);
  font-weight: 300;
  line-height: 1.02;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: #f7efdd;
  margin: 0;
  /* Never break a place name across lines: JOHANNESBUR / G is worse than
     truncation. The renderer shrinks the type until the name fits instead. */
  white-space: nowrap;
  overflow: visible;
}

/* A callsign standing in for an unknown destination is drawn markedly
   quieter: an aircraft whose route is unknown should look less certain than
   one whose route is known, not equally certain. */
.oh-headline-id {
  font-size: calc(var(--text-display) * 0.62);
  letter-spacing: 0.3em;
  opacity: 0.72;
}

.oh-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 0 calc(var(--u) * 90);
  font-family: var(--label);
  font-size: var(--text-xs);
  letter-spacing: 0.22em;
  text-transform: uppercase;
  font-variant-numeric: tabular-nums;
  margin: 0;
  /*
   * The unit dims this line to 0.6 against the sky. Dim cream on a bright
   * blue at three metres is a texture; the same thing in a browser at fifty
   * centimetres fails AA, and on this sky ANY opacity below one does. So the
   * hierarchy the unit draws with opacity, route first, everything else
   * behind it, is drawn here with weight instead, and every span stays at
   * full cream. Add class "exact" to the plate for the unit's true values.
   */
  color: #f7efdd;
}

.oh-meta span { opacity: 1; font-weight: 300; }
.oh-meta span:first-child { font-weight: 500; }

/* Separators are drawn after the item rather than before the next one, so a
   line that wraps never begins with an orphaned bullet. */
.oh-meta span:not(:last-child)::after {
  content: "\00B7";
  margin-left: calc(var(--u) * 90);
  opacity: 0.55;
  font-weight: 300;
}

/* The unit's own values, for comparison. */
.plate.exact { --sky-top: #446bc6; --sky-bottom: #6791d9; }
.plate.exact .oh-meta { color: #b9cde0; }
.plate.exact .oh-meta span { opacity: 0.6; }
.plate.exact .oh-meta span:first-child { opacity: 0.92; }

.plate-foot {
  display: flex;
  gap: 1rem;
  align-items: baseline;
  justify-content: space-between;
  padding: 0 calc(var(--u) * 300) calc(var(--u) * 200);
  color: #f7efdd;
  opacity: 0.8;
  font-family: var(--label);
  font-size: var(--text-xs);
  letter-spacing: 0.22em;
  text-transform: uppercase;
}
.plate-foot .stale { color: #f7d9a0; opacity: 1; }
.plate-foot [data-stamp] {
  display: flex;
  width: 100%;
  justify-content: space-between;
  gap: 1rem;
}

/* ------------------------------------------------- the switchable screens */

.stage {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  position: relative;
}
.stage > * { flex: 1; min-height: 0; transition: opacity 450ms ease; }
.stage > .swap { opacity: 0; }
.stage svg { width: 100%; height: 100%; display: block; }

/* A photographed screen fills the plate the way a rendered one does: the
   frame decides the shape, and the picture is cropped to it rather than
   letterboxed inside it. */
.stage-shot {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
  display: block;
}

/* The photographed radar keeps the same ink treatment as the drawn one did:
   the rules for that live with the other flat screens further down. */

/*
 * Each screen brings its own ground, exactly as on the unit: radar and the
 * board are paper, the scope is near-black, and only Overhead carries the
 * sky, so the weather picker only visibly does anything there, which is
 * also true of the product, where flat panels suppress weather.
 */
.plate[data-screen="radar"],
.plate[data-screen="hangar"] {
  background: #f2f2f0;
  color: #10161b;
}


.plate[data-screen="radar"] .plate-head .label,
.plate[data-screen="hangar"] .plate-head .label { color: #10161b; opacity: 0.6; }
.plate[data-screen="radar"] .plate-foot,
.plate[data-screen="hangar"] .plate-foot { color: #414c54; }
.plate[data-screen="radar"] .tick,
.plate[data-screen="hangar"] .tick { background: rgba(16, 22, 27, 0.16); }
.plate[data-screen="radar"] .tick span,
.plate[data-screen="hangar"] .tick span { background: rgba(16, 22, 27, 0.5); }
.plate[data-screen="radar"] .live,
.plate[data-screen="hangar"] .live { color: var(--signal); }
.plate[data-screen="radar"] .live-dot,
.plate[data-screen="hangar"] .live-dot { background: var(--signal); }

.plate[data-screen="scope"] { background: #050d08; }

/* --------------------------------------------------------------- weather */

/*
 * A picker, not a forecast. Each option is a fixed pair of sky colours with
 * the cream ink checked against the deeper of the two, plus a particle layer
 * for the ones that have particles.
 */
.plate[data-screen="overhead"][data-weather="overcast"],
.plate[data-screen="board"][data-weather="overcast"] { background: linear-gradient(to bottom, #4e5c70, #66788c); }
.plate[data-screen="overhead"][data-weather="rain"],
.plate[data-screen="board"][data-weather="rain"] { background: linear-gradient(to bottom, #3b4a5e, #4d6076); }
.plate[data-screen="overhead"][data-weather="snow"],
.plate[data-screen="board"][data-weather="snow"] { background: linear-gradient(to bottom, #4a5f7e, #5e7899); }

/*
 * Night. Deeper than the daytime sky and cooler with it, taken off the unit's
 * own night screens. Cream ink against these clears AA by a wide margin, so
 * unlike the daylight case there is nothing to trade here.
 *
 * The chrome bar stays cream at night, as it does on the unit: it is a
 * physical bezel around the picture, not part of the sky.
 */
.plate[data-light="night"][data-screen="overhead"],
.plate[data-light="night"][data-screen="board"] {
  background: linear-gradient(to bottom, #0b1020, #1c2136);
}
.plate[data-light="night"][data-screen="overhead"][data-weather="overcast"],
.plate[data-light="night"][data-screen="board"][data-weather="overcast"] {
  background: linear-gradient(to bottom, #161b28, #262c3c);
}
.plate[data-light="night"][data-screen="overhead"][data-weather="rain"],
.plate[data-light="night"][data-screen="board"][data-weather="rain"] {
  background: linear-gradient(to bottom, #0d1420, #1b2432);
}
.plate[data-light="night"][data-screen="overhead"][data-weather="snow"],
.plate[data-light="night"][data-screen="board"][data-weather="snow"] {
  background: linear-gradient(to bottom, #131a2a, #232b3e);
}

/*
 * At night the sky no longer competes with the type, so the metadata can go
 * back to the hierarchy the unit actually draws: the route bright, the rest
 * stepped back. In daylight that same step fails AA, which is why it only
 * appears here.
 */
.plate[data-light="night"] .oh-meta span { opacity: 0.72; }
.plate[data-light="night"] .oh-meta span:first-child { opacity: 1; }
.plate[data-light="night"] .bd-meta { color: rgba(247, 239, 221, 0.76); }

/*
 * The artwork is the day set. The night liveries, dimmed fuselage and lit
 * cabin, are a separate library and are not shipped here, so nothing is done
 * to these files: a day livery with a filter over it is a night livery that
 * is confidently wrong, which is the one thing this product does not do.
 */

/*
 * Weather is a canvas drawn by assets/weatherfx.js, which is the product's
 * own overlay ported. The CSS stripes that used to live here were the exact
 * failure the product's source documents: uniform dashes, no depth, no wind.
 */
.weather-overlay {
  position: absolute;
  inset: 0;
  z-index: 3;
  pointer-events: none;
  width: 100%;
  height: 100%;
}

/* The sweep is the only frame loop the scope needs. Under reduced motion it
   stops and every contact is already painted at full brightness. */
.sc-sweep { animation: sc-turn 4.2s linear infinite; }
@keyframes sc-turn { to { transform: rotate(360deg); } }

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

/* ------------------------------------------------------- radar text bits */

/* Sizes are in the SVG's own units (1600 wide), so they scale with the frame
   rather than with the page. */
.rd-road-major path { stroke: #ffffff; stroke-width: 3.4; fill: none; opacity: 0.8; }
.rd-road-ring path { stroke: #ffffff; stroke-width: 2.8; fill: none; opacity: 0.62; }
.rd-road-minor path { stroke: #ffffff; stroke-width: 1.8; fill: none; opacity: 0.45; }
.rd-ring { stroke: #9aa0a2; stroke-width: 1.6; fill: none; stroke-dasharray: 5 12; }
.rd-lbl, .rd-place {
  font-family: var(--label);
  font-size: 15px;
  letter-spacing: 2.4px;
  fill: #5d6366;
}
.rd-place { font-size: 17px; letter-spacing: 2.8px; }
.rd-legend { font-family: var(--label); font-size: 15px; letter-spacing: 1.6px; fill: #5d6366; }
.rd-home { fill: none; stroke: #1b2438; stroke-width: 2.2; }
.rd-homelbl {
  font-family: var(--label);
  font-size: 22px;
  letter-spacing: 4px;
  fill: #1b2438;
}

/* ------------------------------------------------------- scope text bits */

.sc-ring { stroke: #2f7a4b; stroke-width: 1.5; fill: none; }
.sc-ring-dot { stroke: #2f7a4b; stroke-width: 1.2; fill: none; stroke-dasharray: 3 9; }
.sc-axis { stroke: #245c39; stroke-width: 1; }
.sc-lbl { font-family: var(--label); font-size: 21px; fill: #5d9a6f; }
.sc-cs { font-family: var(--label); font-size: 24px; letter-spacing: 0.08em; fill: #8fe6a8; }
.sc-brg { font-family: var(--label); font-size: 20px; fill: #5d9a6f; }
.sc-corner text { font-family: var(--label); font-size: 22px; letter-spacing: 0.28em; fill: #79c98f; }

/* ------------------------------------------------------------- the board */

.bd, .hg {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  padding: calc(var(--u) * 100) calc(var(--u) * 300) calc(var(--u) * 40);
}

.bd-head {
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  margin: 0 0 calc(var(--u) * 60);
  font-family: var(--label);
  font-size: max(0.58rem, var(--text-xs));
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: #6b757c;
}
.bd-key {
  display: inline-flex;
  gap: clamp(1.5rem, 4vw, 3.5rem);
  margin-left: clamp(2rem, 8vw, 7rem);
  opacity: 0.75;
}
.plate[data-orient="portrait"] .bd-key {
  margin-left: 0.9rem;
  gap: 0.9rem;
  white-space: nowrap;
}

.bd-rows {
  list-style: none;
  margin: 0;
  padding: 0;
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  justify-content: space-evenly;
  overflow: hidden;
}

/*
 * Artwork one side, text the other, and which side depends on the direction.
 * Arrivals read inward and departures read outward, so the layout says which
 * it is without an icon doing the work.
 */
.bd-row {
  display: grid;
  grid-template-columns: 0.85fr 1.25fr;
  align-items: center;
  gap: clamp(1rem, 3vw, 2.5rem);
  min-width: 0;
}

.bd-art { min-width: 0; display: flex; align-items: center; }
.bd-row.out .bd-art { justify-content: flex-start; }
.bd-row.in .bd-art { justify-content: flex-end; }
.bd-art img {
  width: 100%;
  max-width: calc(var(--u) * 1500);
  height: auto;
  display: block;
}

.bd-text { min-width: 0; }
.bd-row.out .bd-text { text-align: left; }
.bd-row.in .bd-text { text-align: right; }

.bd-place {
  font-family: var(--label);
  font-weight: 300;
  font-size: max(1.05rem, calc(var(--u) * 190));
  line-height: 1.05;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: #f7efdd;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  margin-bottom: calc(var(--u) * 40);
}

.bd-meta {
  display: flex;
  flex-wrap: wrap;
  gap: calc(var(--u) * 20) calc(var(--u) * 80);
  font-family: var(--label);
  font-size: max(0.56rem, var(--text-xs));
  letter-spacing: 0.16em;
  text-transform: uppercase;
  font-variant-numeric: tabular-nums;
  color: #f7efdd;
}
.bd-meta span { white-space: nowrap; }
.bd-row.in .bd-meta { justify-content: flex-end; }
.bd-meta span:not(:last-child)::after {
  content: "\00B7";
  margin-left: calc(var(--u) * 90);
  opacity: 0.5;
}

@media (max-width: 700px) {
  /* Two columns at this width would be a stamp and three words, so the row
     stacks and the direction moves into the meta line, where it already is. */
  .bd-row { grid-template-columns: 1fr; gap: 0.35rem; }
  .bd-row.in .bd-text, .bd-row.out .bd-text { text-align: left; }
  .bd-row.in .bd-meta { justify-content: flex-start; }
  .bd-row.in .bd-art, .bd-row.out .bd-art { justify-content: flex-start; }
  .bd-art img { max-width: 62%; }
  .bd-rows { gap: 0.75rem; }
}

/* ---------------------------------------------------------------- hangar */

.hg-grid {
  list-style: none;
  margin: 0;
  padding: 0;
  flex: 1;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: calc(var(--u) * 100) calc(var(--u) * 200);
  align-content: space-around;
}

.hg-card {
  border-top: 1px solid rgba(16, 22, 27, 0.14);
  padding-top: calc(var(--u) * 70);
  display: flex;
  flex-direction: column;
  gap: 0.3em;
  min-width: 0;
}
.hg-reg {
  font-family: var(--label);
  font-weight: 300;
  font-size: max(0.9rem, calc(var(--u) * 140));
  letter-spacing: 0.14em;
  color: #10161b;
}
.hg-type {
  font-family: var(--label);
  font-size: max(0.54rem, calc(var(--u) * 55));
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: #6b757c;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.hg-fig {
  font-family: var(--label);
  font-size: max(0.54rem, calc(var(--u) * 55));
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--signal);
}

/* --------------------------------------------------------- weather picker */

/*
 * A segmented control rather than a pair of dropdowns. Two reasons: a select
 * hides its options until it is opened, which is the wrong shape for a demo
 * whose whole purpose is to invite a poke; and a native select renders as the
 * operating system's widget, which looked like a form control that had
 * wandered in from another site.
 *
 * The marker is a single element that slides between buttons, positioned from
 * the chosen button's own offset rather than by counting, so it survives
 * relabelling and wrapping.
 */

.wx-row {
  display: flex;
  align-items: center;
  gap: clamp(0.75rem, 2vw, 1.25rem);
  flex-wrap: wrap;
  margin-top: 1.25rem;
}

.seg {
  position: relative;
  display: inline-flex;
  align-items: stretch;
  border: 1px solid var(--rule-strong);
  border-radius: 999px;
  padding: 3px;
  background: var(--paper-lift);
  isolation: isolate;
}

.seg-btn {
  position: relative;
  z-index: 2;
  appearance: none;
  border: 0;
  background: none;
  cursor: pointer;
  font-family: var(--label);
  font-size: 0.7rem;
  font-weight: 400;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-faint);
  padding: 0.5rem 0.95rem;
  border-radius: 999px;
  white-space: nowrap;
  transition: color 220ms linear;
}
.seg-btn:hover { color: var(--ink); }
.seg-btn.on { color: var(--paper); }
.seg-btn:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }

.seg-thumb {
  position: absolute;
  z-index: 1;
  top: 3px;
  bottom: 3px;
  left: 0;
  width: 0;
  border-radius: 999px;
  background: var(--ink);
  transition: transform 340ms cubic-bezier(0.22, 0.61, 0.36, 1),
              width 340ms cubic-bezier(0.22, 0.61, 0.36, 1);
  will-change: transform, width;
}

@media (prefers-reduced-motion: reduce) {
  .seg-thumb { transition: none; }
}

@media (max-width: 560px) {
  .seg-btn { padding: 0.45rem 0.7rem; font-size: 0.64rem; letter-spacing: 0.1em; }
}

/* ---------------------------------------------------------------- motion */

/*
 * The classes here are only ever applied by assets/motion.js, so a page whose
 * JavaScript fails renders in its final state rather than invisibly. Nothing
 * below hides content by default.
 */

.rise {
  opacity: 0;
  transform: translateY(18px);
  transition: opacity 700ms cubic-bezier(0.22, 0.61, 0.36, 1),
              transform 700ms cubic-bezier(0.22, 0.61, 0.36, 1);
  will-change: opacity, transform;
}
.risen { opacity: 1; transform: none; }

/* The screen dissolve. Short, because the reader pressed a button and is
   waiting for the answer. */
.stage.cut > * { animation: stage-in 420ms cubic-bezier(0.22, 0.61, 0.36, 1) both; }
@keyframes stage-in {
  from { opacity: 0; transform: translateY(10px); }
  to { opacity: 1; transform: none; }
}

/* The flyover: one aircraft, across the masthead, once. */
.flyover-host { position: relative; overflow: hidden; }
.masthead-inner { position: relative; z-index: 2; }
.flyover {
  position: absolute;
  top: 8%;
  left: 0;
  width: clamp(120px, 13vw, 230px);
  height: auto;
  opacity: 0;
  pointer-events: none;
  z-index: 1;
  transform: translateX(-40vw);
}
.flyover.go { animation: flyover 9s cubic-bezier(0.4, 0.05, 0.6, 0.95) 900ms both; }
@keyframes flyover {
  0% { transform: translateX(-40vw) translateY(0); opacity: 0; }
  12% { opacity: 0.32; }
  85% { opacity: 0.32; }
  100% { transform: translateX(112vw) translateY(-22px); opacity: 0; }
}

/* The live dot already pulses; this gives the poll bar a soft leading edge so
   the thirty seconds read as elapsing rather than as a bar filling. */
.tick span::after {
  content: "";
  position: absolute;
  right: 0;
  top: -2px;
  bottom: -2px;
  width: 24px;
  background: linear-gradient(to right, transparent, rgba(247, 239, 221, 0.55));
}
.plate[data-screen="radar"] .tick span::after,
.plate[data-screen="hangar"] .tick span::after {
  background: linear-gradient(to right, transparent, rgba(16, 22, 27, 0.45));
}

/* Plates on the dark band lift very slightly under the pointer. Enough to say
   they are objects, not enough to be a effect. */
.plate-shot {
  transition: transform 400ms cubic-bezier(0.22, 0.61, 0.36, 1),
              border-color 400ms linear;
}
@media (hover: hover) {
  .plate-row:hover .plate-shot { transform: translateY(-4px); }
  .band .plate-row:hover .plate-shot { border-color: rgba(247, 239, 221, 0.65); }
}

@media (prefers-reduced-motion: reduce) {
  .rise { opacity: 1; transform: none; transition: none; }
  .flyover { display: none; }
  .stage.cut > * { animation: none; }
  .plate-row:hover .plate-shot { transform: none; }
}

/* The gallery caption under the print, filed on the same grid as every other
   section so the labels line up down the whole page. */
.caption {
  display: grid;
  grid-template-columns: 8.5rem minmax(0, 1fr);
  gap: clamp(1.5rem, 4vw, 3.5rem);
  border-top: 1px solid var(--rule);
  margin-top: 1.5rem;
  padding-top: 1rem;
  align-items: start;
}
.caption p { margin: 0; max-width: 60ch; color: var(--ink-soft); }
.caption .label { padding-top: 0.35rem; }
@media (max-width: 900px) {
  .caption { grid-template-columns: 1fr; gap: 0.75rem; }
  .caption .label { padding-top: 0; }
}

/* --------------------------------------------------------------- columns */

.cols-3 {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(1.5rem, 3.5vw, 3rem);
}
.cols-2 {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(1.5rem, 3.5vw, 3rem);
}

.cols-3 > div > .step-num:first-child { padding-top: 1rem; border-top: 1px solid var(--rule); }

.step-num {
  font-family: var(--head);
  font-size: clamp(1.5rem, 2.4vw, 1.9rem);
  font-weight: 400;
  line-height: 1;
  letter-spacing: -0.01em;
  color: var(--signal);
  display: block;
  margin-bottom: 0.65rem;
}

.wide-only { display: inline; }
@media (max-width: 700px) { .wide-only { display: none; } }

.checkout-grid { }
@media (max-width: 820px) {
  .checkout-grid { grid-template-columns: 1fr !important; }
}

/* ---------------------------------------------------------- section marks */

/*
 * The page read as one continuous scroll: every section was the same cream,
 * separated by a hairline that nothing else on the page distinguished it
 * from. Three things fix that, in order of how much work they do:
 *
 *   1. Alternating grounds. A section on a different ground is a different
 *      section, and no rule is needed to say so.
 *   2. A number. "03" beside a heading tells you where you are in a way that
 *      a heading alone cannot.
 *   3. Air. The gap between sections is now clearly larger than the gaps
 *      inside them, which is what makes a group read as a group.
 */

/* A band of ground that runs the full width of the window while its content
   stays in the page column. */
.sheet {
  padding-block: clamp(3.5rem, 8vw, 7rem);
}
.sheet-tint { background: var(--paper-deep); }
.sheet-lift { background: var(--paper-lift); }

/* The dark band is the same idea, already defined above; these keep the two
   in step so a section can be moved between them without editing markup. */
.sheet + .sheet { border-top: 1px solid var(--rule); }
.band + .sheet, .sheet + .band { border-top: 0; }
.sheet-tint + .sheet-tint,
.band + .band { border-top: 0; }

.section-head {
  border-top: 2px solid var(--ink);
  padding-top: 1.4rem;
  margin-bottom: clamp(2rem, 4vw, 3rem);
}
.band .section-head { border-top-color: #f2efe6; }

/* The number sits in the label column, above the label, and is the largest
   thing in it. */
.section-num {
  display: block;
  font-family: var(--label);
  font-size: 1.35rem;
  font-weight: 300;
  letter-spacing: 0.04em;
  color: var(--ink);
  line-height: 1;
  margin-bottom: 0.55rem;
  font-variant-numeric: tabular-nums;
}
.band .section-num { color: #f2efe6; }

@media (max-width: 900px) {
  .section-num { display: inline-block; margin-right: 0.75rem; margin-bottom: 0; }
}

/* ------------------------------------------------------------ try it row */

/*
 * The controls were a row of pills under a paragraph and read as decoration:
 * people did not know they could touch the thing. Now they sit in a panel of
 * their own with a heading that says what they are for, directly under the
 * display.
 */
.try {
  border: 1px solid var(--rule-strong);
  border-top: 0;
  /* Sits directly on the frame, so the controls read as belonging to the
     display rather than being the next thing down the page. */
  margin-top: -1px;
  background: var(--paper-lift);
  padding: clamp(1rem, 2.2vw, 1.5rem) clamp(1rem, 2.5vw, 1.75rem);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: clamp(0.75rem, 2vw, 2rem);
}

.try-head { flex: 1 1 16rem; min-width: 0; }

.try-label {
  font-family: var(--label);
  font-size: 0.7rem;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--ink);
  margin: 0 0 0.35rem;
  display: flex;
  align-items: center;
  gap: 0.5rem;
}
/* A small pointer down at the controls. Drawn, not an emoji. */
.try-label::after {
  content: "";
  width: 0;
  height: 0;
  border-left: 4px solid transparent;
  border-right: 4px solid transparent;
  border-top: 5px solid var(--signal);
  display: inline-block;
}
.try-line {
  margin: 0;
  font-size: 0.95rem;
  color: var(--ink-soft);
  max-width: 46ch;
}

.try .wx-row { margin: 0; flex: 1 1 auto; justify-content: flex-end; }

/*
 * Nothing here is live, and the page says so three times: on the plate, under
 * it, and in the caption of every still. A visitor who assumes these are real
 * aircraft over London and later finds out otherwise has been told something
 * false by a product whose entire claim is that it never does that.
 */
.sample-note {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1rem;
  align-items: baseline;
  margin: 1.25rem 0 0;
  font-size: 0.92rem;
  color: var(--ink-soft);
  max-width: 76ch;
}
.sample-note > span:first-child {
  font-family: var(--label);
  font-size: 0.66rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--signal);
  border: 1px solid currentColor;
  padding: 0.2rem 0.55rem;
  white-space: nowrap;
}

@media (max-width: 860px) {
  .try { flex-direction: column; align-items: flex-start; gap: 1rem; }
  .try-head { flex: 0 0 auto; }
  .try .wx-row { justify-content: flex-start; flex: 0 0 auto; gap: 0.5rem; }
}

/* ------------------------------------------------------------- the ticker */

/*
 * Place names running past, three rows, alternate directions. The point is
 * that the list has no end: the display can be pointed at any of these and
 * thousands more, and a finite list on a page implies a finite list in the
 * product.
 *
 * Each track holds its content twice and travels exactly half its width, so
 * the loop closes with no jump. Duplicated content is hidden from screen
 * readers by aria-hidden on the wrapper, and the sentence above the ticker
 * carries the same meaning in prose.
 */
.ticker {
  overflow: hidden;
  padding: clamp(1.5rem, 3vw, 2.5rem) 0;
  display: flex;
  flex-direction: column;
  gap: clamp(0.35rem, 1vw, 0.75rem);
  user-select: none;
}


.ticker-track {
  display: flex;
  width: max-content;
  gap: clamp(1.5rem, 4vw, 3rem);
  animation: ticker 46s linear infinite;
  will-change: transform;
}
.ticker-row.reverse .ticker-track { animation-direction: reverse; animation-duration: 58s; }
.ticker-row:nth-child(3) .ticker-track { animation-duration: 52s; }

.ticker-track span {
  font-family: var(--label);
  font-weight: 300;
  font-size: clamp(1.15rem, 2.4vw, 1.9rem);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--ink-faint);
}
/* Alternating emphasis, so the row reads as a list rather than a sentence. */
.ticker-track span:nth-child(odd) { color: var(--ink); }

@keyframes ticker {
  from { transform: translateX(0); }
  to { transform: translateX(-50%); }
}

/* Fade the ends so names arrive and leave rather than being cut off mid-word.
   Declared on the row itself, above the overflow rule, or the mask is applied
   to a box that has already clipped its content. */
.ticker-row {
  overflow: hidden;
  -webkit-mask-image: linear-gradient(to right, transparent 0, #000 10%, #000 90%, transparent 100%);
  mask-image: linear-gradient(to right, transparent 0, #000 10%, #000 90%, transparent 100%);
}

@media (prefers-reduced-motion: reduce) {
  .ticker-track { animation: none; flex-wrap: wrap; width: auto; }
  .ticker-row { mask-image: none; -webkit-mask-image: none; }
  .ticker-row:not(:first-child) { display: none; }
}

/* --------------------------------------------------------------- pricing */

.tier {
  border: 1px solid var(--rule);
  padding: clamp(1.5rem, 3vw, 2.25rem);
  display: flex;
  flex-direction: column;
  background: var(--paper-lift);
}
.tier-price {
  font-family: var(--head);
  font-size: clamp(2.4rem, 4.4vw, 3.25rem);
  font-weight: 400;
  line-height: 1;
  margin: 1.2rem 0 0.35rem;
  letter-spacing: -0.02em;
}
.tier-note { font-size: 0.9rem; color: var(--ink-faint); margin: 0 0 1.5rem; }
.tier ul { list-style: none; margin: 0 0 2rem; padding: 0; }
.tier li {
  border-top: 1px solid var(--rule);
  padding: 0.7rem 0;
  font-size: 0.95rem;
  color: var(--ink-soft);
}
.tier .btn { margin-top: auto; }
.tier-quiet { background: transparent; }

/* ---------------------------------------------------------------- stills */

.still { border: 1px solid var(--rule); background: var(--paper-lift); }
.still svg { width: 100%; height: auto; display: block; }
.still-cap {
  border-top: 1px solid var(--rule);
  padding: 0.85rem 1rem;
  display: flex;
  gap: 1rem;
  align-items: baseline;
}
.still-cap p { margin: 0; font-size: 0.95rem; color: var(--ink-soft); }

/* ----------------------------------------------------------------- forms */

.field { margin-bottom: 1.35rem; }
.field label {
  display: block;
  font-family: var(--label);
  font-size: 0.68rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ink-faint);
  margin-bottom: 0.5rem;
}
.field input, .field select, .field textarea {
  width: 100%;
  font-family: var(--display);
  font-size: 1rem;
  color: var(--ink);
  background: var(--paper-lift);
  border: 1px solid var(--rule-strong);
  padding: 0.8rem 0.9rem;
}
.field input:focus, .field select:focus { outline: 2px solid var(--ink); outline-offset: 0; }
.field .hint { font-size: 0.85rem; color: var(--ink-faint); margin: 0.5rem 0 0; }
.field-error { color: var(--signal); font-size: 0.9rem; margin: 0.5rem 0 0; }

.checkline {
  display: flex;
  gap: 0.75rem;
  align-items: flex-start;
  font-size: 0.95rem;
  color: var(--ink-soft);
  margin-bottom: 1.35rem;
}
.checkline input { margin-top: 0.35rem; }

.notice {
  border: 1px solid var(--rule-strong);
  border-left-width: 3px;
  padding: 1rem 1.15rem;
  font-size: 0.95rem;
  color: var(--ink-soft);
  background: var(--paper-lift);
  margin-bottom: 1.75rem;
}
.notice p { margin: 0 0 0.6rem; }
.notice p:last-child { margin-bottom: 0; }

/* ---------------------------------------------------------------- tables */

.table { width: 100%; border-collapse: collapse; }
.table th {
  text-align: left;
  font-family: var(--label);
  font-weight: 400;
  font-size: 0.66rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ink-faint);
  padding: 0 0 0.7rem;
  border-bottom: 1px solid var(--rule-strong);
  white-space: nowrap;
}
.table td {
  padding: 0.9rem 1rem 0.9rem 0;
  border-bottom: 1px solid var(--rule);
  font-size: 0.95rem;
  vertical-align: top;
}
.table td:last-child, .table th:last-child { padding-right: 0; text-align: right; }

.key {
  font-family: var(--mono);
  font-size: 0.85rem;
  letter-spacing: 0.12em;
  background: var(--paper-deep);
  padding: 0.25rem 0.5rem;
  display: inline-block;
}

.pill {
  font-family: var(--label);
  font-size: 0.62rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  border: 1px solid var(--rule-strong);
  padding: 0.2rem 0.55rem;
  color: var(--ink-soft);
  white-space: nowrap;
}
.pill-on { border-color: var(--ink); color: var(--ink); }
.pill-off { color: var(--ink-faint); }

/* ------------------------------------------------------------------ misc */

.panel {
  border: 1px solid var(--rule);
  background: var(--paper-lift);
  padding: clamp(1.25rem, 2.5vw, 2rem);
}

.stack > * + * { margin-top: 1.25rem; }
.stack-lg > * + * { margin-top: 2.5rem; }

.faq dt {
  font-size: 1.15rem;
  font-weight: 400;
  border-top: 1px solid var(--rule);
  padding-top: 1.5rem;
  margin-top: 1.5rem;
}
.faq dd { margin: 0.6rem 0 0; color: var(--ink-soft); max-width: 62ch; }

/* ------------------------------------------------------- reading rhythm */

/*
 * The opening paragraph of a section, set at display size. Its job is to be
 * read at a glance from across the page, so it is closer to a headline than
 * to body copy and it is measured short to keep the lines even.
 */
.lead {
  font-family: var(--head);
  font-size: clamp(1.5rem, 2.6vw, 2.1rem);
  line-height: 1.28;
  font-weight: 400;
  letter-spacing: -0.004em;
  color: var(--ink);
  max-width: 26ch;
  margin: 0 0 1.75rem;
  text-wrap: balance;
}

/* The first few words of a section, in the same tracked capitals the display
   uses for its own labels. It hands the paragraph off from the heading and
   gives the eye somewhere to land. */
.opener { max-width: 54ch; }
.opener b {
  font-family: var(--label);
  font-weight: 400;
  font-size: 0.82em;
  letter-spacing: 0.16em;
  text-transform: uppercase;
}

/* Hanging punctuation and a proper measure. Long paragraphs at a short
   measure are what made the page feel like a form to be completed. */
.prose p, .prose-cols p { hyphens: none; }
.prose { max-width: 64ch; }

.prose-cols {
  column-count: 2;
  column-gap: clamp(2rem, 4vw, 3.5rem);
  max-width: 58rem;
}
.prose-cols p { margin: 0 0 1.3em; max-width: none; break-inside: avoid; }
.prose-cols p:last-child { margin-bottom: 0; }
@media (max-width: 760px) { .prose-cols { column-count: 1; } }

/* A note in the margin rather than a second headline: the section already
   has one, and two of them at the same size is two of nothing. */
.pull {
  border-top: 1px solid var(--rule-strong);
  padding: clamp(1.25rem, 2.5vw, 1.75rem) 0 0;
  margin: clamp(2rem, 4vw, 3rem) 0 0;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: clamp(1rem, 3vw, 2.5rem);
  align-items: start;
}
@media (max-width: 760px) { .pull { grid-template-columns: 1fr; } }
.pull p {
  font-family: var(--head);
  font-size: clamp(1.35rem, 2.2vw, 1.75rem);
  line-height: 1.25;
  font-weight: 400;
  letter-spacing: -0.004em;
  max-width: 24ch;
  margin: 0;
}
.pull p.label {
  margin-top: 0.35rem;
  font-size: 0.6875rem;
  line-height: 1.5;
  letter-spacing: 0.24em;
  max-width: none;
  color: var(--ink-faint);
}

/*
 * Figures. Four numbers across, each with a one-word label, and the sentence
 * that explains all four underneath rather than four fragments trying to
 * explain themselves in two lines each. Long labels under big numbers is the
 * shape of a dashboard, not of a specimen sheet.
 */
.figures {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(1rem, 3vw, 2.5rem);
  border-top: 2px solid var(--ink);
  padding: 1.5rem 0 0;
  margin: 0;
  list-style: none;
}
.figures li { display: flex; flex-direction: column; gap: 0.35rem; }

.figure-n {
  font-family: var(--head);
  font-size: clamp(2.2rem, 4.6vw, 3.6rem);
  font-weight: 400;
  line-height: 1;
  letter-spacing: -0.02em;
  font-variant-numeric: lining-nums tabular-nums;
}
.figure-u {
  font-family: var(--label);
  font-size: 0.3em;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  margin-left: 0.15em;
  vertical-align: 0.35em;
  color: var(--ink-faint);
}
.figure-l {
  font-family: var(--label);
  font-size: 0.68rem;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--ink-faint);
  line-height: 1.4;
}

.figures-note {
  margin: 1.25rem 0 0;
  font-size: 0.95rem;
  color: var(--ink-soft);
  max-width: 74ch;
}

@media (max-width: 700px) {
  .figures { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 1rem 0.75rem; }
  .figure-n { font-size: clamp(1.7rem, 8vw, 2.4rem); }
  .figures-note { font-size: 0.9rem; }
}

/* Place names as a specimen rather than a sentence. */
.specimen {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1.5rem;
  align-items: baseline;
  margin: 0 0 1.75rem;
  padding: 0;
  list-style: none;
}
.specimen li {
  font-family: var(--label);
  font-weight: 300;
  font-size: clamp(1.1rem, 2.2vw, 1.7rem);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink);
}
.specimen li:nth-child(even) { color: var(--ink-faint); }

.faq-cols {
  column-count: 2;
  column-gap: clamp(2rem, 4vw, 3.5rem);
  column-fill: balance;
  orphans: 3;
  widows: 3;
}
.faq-cols dt { break-after: avoid; }
.faq-cols dd { break-inside: avoid; margin-bottom: 0.5rem; }
.faq-cols dt:first-of-type { margin-top: 0; }
@media (max-width: 820px) { .faq-cols { column-count: 1; } }

/* ------------------------------------------------------------ full bleed */

/*
 * The product is the argument, so it gets the whole width of the window. A
 * live display boxed in a 1180px column behind a hairline border looks like a
 * screenshot of software; the same display running edge to edge looks like
 * the thing on the wall.
 */
.bleed {
  width: 100vw;
  margin-left: calc(50% - 50vw);
  margin-right: calc(50% - 50vw);
}
.bleed .mount { border: 0; background: none; padding: 0; }

/*
 * Full bleed changes which dimension is the constraint. The boxed plate is
 * limited by its width, so --u comes off cqw; here the height is capped
 * against the window, so it comes off cqh instead, the same way the unit
 * takes it from vmin. Without this the type keeps growing with the width of
 * a wide monitor while the frame stops growing, and the rows collide.
 */
.bleed .plate {
  container-type: size;
  height: min(82vh, 50vw);
  aspect-ratio: auto;
  --scale: 1.05;
  --u: calc(0.0219cqh * var(--scale));
}
.bleed .oh-rows { gap: calc(var(--u) * 200); }
.bleed .chrome { padding-inline: clamp(1rem, 4vw, 3rem); }

/* One dark band. The page is pale, so a single near-black section is the
   loudest thing available without introducing a colour. */
/*
 * The heavy section is the product's own sky.
 *
 * Black was cold, navy was colder, oat was warm and dull. All three were new
 * colours invented for one band. This one is already on the page: the exact
 * gradient the display is running in the frame above it, cream type and all.
 * The section showing the screens is set in the colour the screens are set
 * in, which is the only justification a band this loud needs.
 */
.band {
  background: linear-gradient(to bottom, var(--sky-top), var(--sky-bottom));
  color: #f7efdd;
  padding-block: clamp(3.5rem, 8vw, 7rem);
}
.band .label, .band .figure-l { color: rgba(247, 239, 221, 0.72); }
.band h2, .band h3, .band p { color: inherit; }
.band .figures { border-top-color: rgba(247, 239, 221, 0.28); }
.band .btn { border-color: #f7efdd; color: #f7efdd; }
.band .btn:hover { background: #f7efdd; color: var(--sky-top); }
.band .btn-solid { background: #f7efdd; color: var(--sky-top); }
.band .btn-solid:hover { background: transparent; color: #f7efdd; }
.band .pull { border-color: rgba(247, 239, 221, 0.35); }
.band .section-head { border-top-color: #f7efdd; }
.band .section-num { color: #f7efdd; }

/* The screens as plates in a catalogue: one to a row, large, caption beside.
   Four small tiles made the most interesting part of the product look like a
   feature grid. */
.plates { display: flex; flex-direction: column; gap: clamp(3rem, 7vw, 5.5rem); }
.plate-row {
  display: grid;
  grid-template-columns: minmax(0, 1.9fr) minmax(0, 1fr);
  gap: clamp(1.5rem, 4vw, 3.5rem);
  align-items: center;
}
.plate-row:nth-child(even) { grid-template-columns: minmax(0, 1fr) minmax(0, 1.9fr); }
.plate-row:nth-child(even) .plate-shot { order: 2; }
.plate-shot { border: 1px solid var(--rule); background: var(--paper-lift); }
/* The same frame as the demo above: black moulding, cream mount, picture.
   These are prints of the product hung on a wall, and they should be framed
   like the product is. */
.band .plate-shot {
  border: clamp(7px, 0.7vw, 12px) solid #10161b;
  background: #f6f3ea;
  padding: clamp(0.6rem, 1.4vw, 1.2rem);
  box-shadow: inset 0 0 0 1px rgba(16, 22, 27, 0.16);
}
.band .plate-row:hover .plate-shot { border-color: #10161b; }
.plate-shot svg { width: 100%; height: auto; display: block; }
.plate-note h3 { font-size: clamp(1.4rem, 2.6vw, 2rem); margin: 0.75rem 0; }
.plate-note p { color: var(--ink-soft); margin: 0; max-width: 34ch; }
.band .plate-note p { color: rgba(247, 239, 221, 0.78); }
@media (max-width: 820px) {
  .plate-row, .plate-row:nth-child(even) { grid-template-columns: 1fr; }
  .plate-row:nth-child(even) .plate-shot { order: 0; }
}

/* ---------------------------------------------------------------- footer */

.footer {
  border-top: 1px solid var(--rule);
  padding-block: 3.5rem 4rem;
  margin-top: var(--section);
}
.footer-grid {
  display: grid;
  grid-template-columns: 1.4fr repeat(3, minmax(0, 1fr));
  gap: 2.5rem;
}
.footer h4 {
  font-family: var(--label);
  font-size: 0.66rem;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--ink-faint);
  margin-bottom: 1rem;
}
.footer ul { list-style: none; margin: 0; padding: 0; }
.footer li { margin-bottom: 0.5rem; font-size: 0.95rem; }
.footer li a:hover { text-decoration: underline; text-underline-offset: 0.18em; }

.attrib {
  border-top: 1px solid var(--rule);
  margin-top: 3rem;
  padding-top: 1.5rem;
  font-size: 0.85rem;
  color: var(--ink-faint);
  line-height: 1.9;
}
.attrib p { margin: 0; max-width: 70ch; }
.attrib a { text-decoration: underline; text-underline-offset: 0.18em; }

/* ----------------------------------------------------------- responsive */

@media (max-width: 900px) {
  .cols-3 { grid-template-columns: 1fr; }
  .footer-grid { grid-template-columns: 1fr 1fr; }
  .filed { grid-template-columns: 1fr; gap: 0.9rem; }
  .filed > .label { padding-top: 0; }
}

@media (max-width: 760px) {
  .masthead nav {
    display: none;
    position: absolute;
    top: 4.5rem;
    left: 0;
    right: 0;
    background: var(--paper);
    border-bottom: 1px solid var(--rule);
    flex-direction: column;
    align-items: flex-start;
    gap: 0;
    padding: 0.5rem var(--gutter) 1.25rem;
    z-index: 20;
  }
  .masthead nav.open { display: flex; }
  .masthead nav a { padding-block: 0.75rem; width: 100%; }
  .masthead-inner { position: relative; }
  .nav-toggle { display: block; }
  .cols-2 { grid-template-columns: 1fr; }
  .table td, .table th { font-size: 0.88rem; }
}

/* Below 700px the demo shows one aircraft. The application does the same
   thing in portrait, art above, text below, so this follows its rule rather
   than inventing one, and drops to a single row because three at this width
   would each be the size of a postage stamp. */
@media (max-width: 700px) {
  /*
   * The orientation control has to keep working here. This block used to
   * force 3/4 on every plate, so pressing Portrait on a phone changed the row
   * count and nothing else: the frame stayed exactly the shape it already
   * was, which made the control look broken. Each orientation now keeps its
   * own proportions, narrow and wide or narrow and tall.
   */
  /* Landscape on a phone is a wide, shallow frame: the artwork does not fit
     beside or above the text at this size, so the row keeps its two columns
     and simply gets smaller, which is what the unit does on a small panel. */
  .plate[data-orient="landscape"] { aspect-ratio: 4 / 3; --scale: 2.5; }
  .plate[data-orient="landscape"] .oh-row {
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: auto auto;
  }
  /* A phone in landscape is a wide, shallow frame. Keeping the art beside the
     text rather than above it is what the unit does on a short panel, and it
     is the only arrangement where both are legible at this height. */
  .plate[data-orient="landscape"] .oh-row {
    grid-template-columns: 38% minmax(0, 1fr);
    grid-template-rows: auto;
    gap: calc(var(--u) * 100);
    align-items: center;
    justify-items: stretch;
  }
  .plate[data-orient="landscape"] .oh-art { justify-content: center; }
  .plate[data-orient="landscape"] .oh-headline {
    font-size: max(1rem, var(--text-display));
    white-space: normal;
    overflow: visible;
    text-overflow: clip;
    letter-spacing: 0.08em;
  }
  .plate[data-orient="landscape"] .oh-meta { font-size: max(0.52rem, var(--text-xs)); letter-spacing: 0.1em; }

  .plate[data-orient="portrait"] { aspect-ratio: 9 / 15; --scale: 3.6; }
  .mount[data-orient="portrait"] { max-width: 100%; }
  .oh-row {
    grid-template-columns: 1fr;
    grid-template-rows: 1fr auto;
    gap: calc(var(--u) * 90);
    justify-items: start;
  }
  .oh-art { justify-content: flex-start; }
  .oh-row + .oh-row { display: none; }
  /* Floors, because the ratio between headline and metadata that works at
     television size leaves the metadata at four pixels on a phone. */
  .oh-headline { font-size: max(1.7rem, var(--text-display)); white-space: normal; }
  .oh-meta { font-size: max(0.62rem, var(--text-xs)); letter-spacing: 0.16em; }
  /* Two halves of the footer wrapping into four lines reads as a fault.
     Below this width only the left half is kept; the age of the reading is
     announced in the live region either way. */
  .plate-foot { font-size: 0.55rem; letter-spacing: 0.12em; }
  .plate-foot [data-stamp] span:last-child { display: none; }
  .chrome { gap: 0.45rem; font-size: 0.66rem; padding-inline: 0.5rem; }
  .chrome-screen { padding: 0.1rem 0.3rem; }
  /* Five buttons and a clock do not fit in 350px; the buttons are the
     interactive part, so the clock is the thing that goes. */
  .chrome-right { display: none; }
  /* The board loses its detail column and the hangar drops to two across ,
     columns removed rather than squeezed, the way the product treats a
     field it cannot show properly. */
  .bd-row { grid-template-columns: auto auto 1fr; gap: 0.6rem; }
  .bd-detail { display: none; }
  .hg-grid { grid-template-columns: 1fr 1fr; }
  .hg-card:nth-child(n+5) { display: none; }
}

@media (prefers-reduced-motion: reduce) {
  * { animation-duration: 0.001ms !important; animation-iteration-count: 1 !important; transition-duration: 0.001ms !important; }
}
