/**
 * style.css
 * =============================================================================
 * Visual design for the Ten13 simulator control dashboard.
 *
 * DESIGN DIRECTION: THE FLIGHT PROGRESS STRIP RACK
 * ------------------------------------------------
 * Airline operations ran for decades on paper flight progress strips - printed
 * card, one per flight, slid into an aluminium rack in departure order and
 * annotated by hand as the flight progressed. Flight Buddy's own tvOS wallboard
 * is a descendant of that rack, so the control surface for its simulator is
 * built as the thing itself rather than as a generic admin table.
 *
 * That drives every decision here:
 *   - Buff card stock seated in a graphite rail, not cards floating on a page
 *   - Zero corner radius anywhere except the punched hole on each strip
 *   - Strips touch each other; a rack has no gaps between strips
 *   - Monospace is the primary voice, because strip data was typed and every
 *     column had to align - not because monospace reads as "technical"
 *   - Colour is strictly status coding, never decoration
 *
 * THE SIGNATURE ELEMENT
 * ---------------------
 * The NOW rail: a physical bar seated between strips at the current simulated
 * time. Scrubbing the clock makes it travel down the rack. It communicates the
 * entire idea of a controllable demo clock without needing to be labelled.
 * =============================================================================
 */

/* ---------------------------------------------------------------------------
   Tokens
   --------------------------------------------------------------------------- */

:root {
  /* Anodised aluminium rack, in shadow. Cool graphite rather than black -
     black would read as a generic dark dashboard; this reads as equipment. */
  --rack-deep: #171b21;
  --rack: #1f242b;
  --rack-raised: #262c34;
  --rail: #39424d;
  --rail-lit: #4d5966;

  /* Printed card stock. The strips are the only light surfaces on the page. */
  --stock: #e7e0d1;
  --stock-shade: #d5cdba;
  --ink: #21262c;
  --ink-soft: #5a6068;

  /* Status coding, taken from operational convention rather than a palette
     generator: printers' teal for normal, Eurocontrol amber for a regulated
     flight, oxidised red for a cancellation. */
  --normal: #1e6e6e;
  --slot: #c87b1e;
  --cancelled: #9e3b32;
  --airborne: #4c9a7a;

  --label: #8a96a3;
  --label-dim: #626d79;

  --mono: ui-monospace, "SF Mono", SFMono-Regular, "Cascadia Mono", Menlo,
    Consolas, monospace;
  --sans: -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;

  --strip-height: 58px;
}

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  background: var(--rack-deep);
  color: var(--stock);
  font-family: var(--mono);
  font-size: 14px;
  line-height: 1.4;
  -webkit-font-smoothing: antialiased;
}

/* Equipment labelling: stencilled, letterspaced, small. Used for every label
   on the page so the chrome reads as panel markings rather than UI text. */
.stencil {
  font-family: var(--sans);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--label);
}

/* ---------------------------------------------------------------------------
   Header - the instrument panel
   --------------------------------------------------------------------------- */

.panel-head {
  display: flex;
  align-items: center;
  gap: 28px;
  flex-wrap: wrap;
  padding: 14px 22px;
  background: linear-gradient(180deg, var(--rack-raised), var(--rack));
  border-bottom: 1px solid #0d1015;
  box-shadow: 0 1px 0 var(--rail) inset;
}

.identity {
  display: flex;
  align-items: baseline;
  gap: 10px;
  margin-right: auto;
}

.identity b {
  font-size: 17px;
  font-weight: 700;
  letter-spacing: 0.06em;
  color: var(--stock);
}

.identity span {
  color: var(--label-dim);
  font-size: 12px;
  letter-spacing: 0.1em;
}

/* A readout is a labelled value in the header: label above, figure below. */
.readout {
  display: flex;
  flex-direction: column;
  gap: 3px;
}

.readout output {
  font-size: 18px;
  font-variant-numeric: tabular-nums;
  color: var(--stock);
}

.readout output.offset {
  color: var(--slot);
}

/* Indicator lamps. Recessed, and lit only when the thing is actually on -
   an unlit lamp must look genuinely unlit, not merely a different colour. */
.lamps {
  display: flex;
  gap: 16px;
}

.lamp {
  display: flex;
  align-items: center;
  gap: 7px;
  color: var(--label-dim);
}

.lamp::before {
  content: "";
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: #12161b;
  box-shadow: inset 0 1px 2px #000;
}

.lamp.on {
  color: var(--stock);
}

.lamp.on::before {
  background: var(--airborne);
  box-shadow: 0 0 8px rgba(76, 154, 122, 0.8);
}

.lamp.warn.on::before {
  background: var(--slot);
  box-shadow: 0 0 8px rgba(200, 123, 30, 0.8);
}

/* ---------------------------------------------------------------------------
   Layout
   --------------------------------------------------------------------------- */

.deck {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 320px;
  gap: 20px;
  padding: 20px 22px 60px;
  max-width: 1500px;
  margin: 0 auto;
}

@media (max-width: 1040px) {
  .deck {
    grid-template-columns: minmax(0, 1fr);
  }
}

.bay-head {
  display: flex;
  align-items: center;
  gap: 14px;
  margin-bottom: 10px;
}

.bay-head .rule {
  flex: 1;
  height: 1px;
  background: var(--rail);
}

/* ---------------------------------------------------------------------------
   The rack
   --------------------------------------------------------------------------- */

.rack {
  background: var(--rack);
  border: 1px solid var(--rail);
  border-top-width: 3px;
  border-top-color: var(--rail-lit);
  padding: 9px 9px 12px;
  min-height: 220px;
}

/* Strips sit flush against each other, as they do in a real rack. The only
   separation is the thin shadow line each strip casts on the one below. */
.strip {
  position: relative;
  display: grid;
  grid-template-columns: 14px 104px minmax(140px, 1fr) 176px 172px 30px;
  align-items: center;
  gap: 12px;
  min-height: var(--strip-height);
  padding: 0 12px 0 0;
  background: var(--stock);
  color: var(--ink);
  border-bottom: 1px solid var(--stock-shade);
  cursor: pointer;
  text-align: left;
  width: 100%;
  font: inherit;
  font-family: var(--mono);
}

.strip:last-of-type {
  border-bottom: none;
}

/* The seating tab: the coloured edge that keys the strip into the rail and
   carries its status. This is the only place status colour appears at full
   strength, which keeps the rack scannable. */
.strip .tab {
  align-self: stretch;
  background: var(--normal);
}

.strip.slot .tab {
  background: var(--slot);
}

.strip.cancelled .tab {
  background: var(--cancelled);
}

.strip.airborne .tab {
  background: var(--airborne);
}

/* The punched hole - the one curve on the page, and the detail that makes the
   strip read as a physical object rather than a table row. */
.strip .tab::after {
  content: "";
  display: block;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--rack);
  margin: 9px auto 0;
}

.strip:hover {
  background: #efe9dc;
}

.strip:focus-visible {
  outline: 2px solid var(--rail-lit);
  outline-offset: -2px;
}

.strip.selected {
  background: #f2ece0;
  box-shadow: inset 0 0 0 2px var(--ink);
}

.strip.cancelled .callsign,
.strip.cancelled .route {
  text-decoration: line-through;
  color: var(--ink-soft);
}

/* --- Departed ------------------------------------------------------------
   A flight that has pushed back is history for planning purposes, so its strip
   recedes and what is still to come stands out. Driven from the same clock as
   the NOW rail, so a strip greys exactly as the rail passes it.

   Deliberately a wash over the card stock rather than a colour change: a real
   strip that had been worked was annotated and set aside, not rewritten. The
   seating tab keeps its full status colour so a departed flight with a slot or
   a cancellation is still legible at a glance down the rack. */
.strip.departed {
  background: #d9d3c6;
  color: var(--ink-soft);
}

.strip.departed .callsign,
.strip.departed .times {
  color: #6a7078;
}

.strip.departed .tab {
  opacity: 0.55;
}

/* Hovering or selecting a departed strip brings it back to full strength -
   you are working on it again, so it stops being background. */
.strip.departed:hover,
.strip.departed.selected {
  background: #efe9dc;
  color: var(--ink);
}

.strip.departed:hover .callsign,
.strip.departed.selected .callsign,
.strip.departed:hover .times,
.strip.departed.selected .times {
  color: var(--ink);
}

.strip.departed:hover .tab,
.strip.departed.selected .tab {
  opacity: 1;
}

/* Callsign over registration, stacked. Side by side they compete for the same
   narrow column, and the registration breaks at its own hyphen - "G-" on one
   line and "T13C" on the next, which reads as two different aircraft. */
.callsign {
  display: block;
  font-size: 17px;
  font-weight: 700;
  letter-spacing: 0.04em;
}

.reg {
  display: block;
  font-size: 11px;
  color: var(--ink-soft);
  letter-spacing: 0.05em;
  white-space: nowrap;
}

.route {
  font-size: 15px;
  letter-spacing: 0.06em;
}

.route .arrow {
  color: var(--ink-soft);
  margin: 0 6px;
}

.route small {
  display: block;
  font-size: 11px;
  color: var(--ink-soft);
  letter-spacing: 0.04em;
}

/* Times are the densest data on the strip, so they get tabular figures and a
   fixed column - the whole point of a rack is that a column can be read down. */
.times {
  font-variant-numeric: tabular-nums;
  font-size: 15px;
  letter-spacing: 0.04em;
}

.times .est {
  color: var(--slot);
}

.times small {
  display: block;
  font-size: 11px;
  color: var(--ink-soft);
}

.marks {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
}

/* An annotation is a stamp on the card: a delay figure, a slot time, a
   diversion. Rectangular and inked, never a rounded pill. */
.mark {
  font-family: var(--sans);
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  padding: 2px 5px;
  border: 1px solid currentColor;
  color: var(--ink-soft);
  /* Marks never break internally: a wrapped "CTOT 1101" would make one strip
     taller than its neighbours, and a rack whose strips are different heights
     stops being scannable down a column. */
  white-space: nowrap;
}

.mark.slot {
  color: var(--slot);
}

.mark.delay {
  color: var(--cancelled);
}

.mark.held {
  color: var(--ink);
  background: var(--ink);
  color: var(--stock);
  border-color: var(--ink);
}

.expander {
  font-family: var(--sans);
  font-size: 16px;
  color: var(--ink-soft);
  text-align: center;
}

/* A six-column strip needs about 640px. Below that it folds into two rows -
   identity and route above, times and annotations below - which keeps every
   field readable rather than letting the times spill outside the rack. The
   seating tab still spans both rows, so the strip stays one physical object. */
@media (max-width: 760px) {
  .strip {
    grid-template-columns: 14px minmax(0, 1fr) auto;
    row-gap: 3px;
    padding: 9px 11px 9px 0;
    min-height: 0;
  }

  .strip .tab {
    grid-row: 1 / span 2;
  }

  .strip > :nth-child(2) {
    grid-column: 2;
    grid-row: 1;
  }

  .strip .route {
    grid-column: 3;
    grid-row: 1;
    text-align: right;
  }

  .strip .times {
    grid-column: 2;
    grid-row: 2;
  }

  .strip .marks {
    grid-column: 3;
    grid-row: 2;
    justify-content: flex-end;
  }

  /* The whole strip is the control at this width, so a separate affordance is
     just noise competing for a scarce column. */
  .strip .expander {
    display: none;
  }

  .drawer {
    padding-left: 20px;
  }

  .drawer-row > .stencil {
    width: 100%;
  }
}

/* --- The NOW rail: the signature element ---------------------------------- */

.now-rail {
  position: relative;
  height: 26px;
  display: flex;
  align-items: center;
  background: repeating-linear-gradient(
    -45deg,
    #2c333c,
    #2c333c 6px,
    #242a32 6px,
    #242a32 12px
  );
  border-top: 1px solid var(--rail-lit);
  border-bottom: 1px solid #0d1015;
}

.now-rail span {
  font-family: var(--sans);
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.22em;
  color: var(--rack-deep);
  background: var(--slot);
  padding: 3px 9px;
  margin-left: 12px;
}

.now-rail::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--slot);
  opacity: 0.5;
  margin-left: 10px;
  margin-right: 12px;
}

/* --- Annotation drawer ---------------------------------------------------- */

/* The drawer is the back of the strip: where a controller wrote on it. Kept on
   the card stock so it reads as part of the strip, not a modal over it. */
.drawer {
  background: #ddd5c3;
  border-bottom: 1px solid var(--stock-shade);
  padding: 14px 16px 16px 26px;
  color: var(--ink);
  display: grid;
  gap: 12px;
}

.drawer-row {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}

.drawer-row > .stencil {
  color: var(--ink-soft);
  width: 74px;
  flex-shrink: 0;
}

.drawer dl {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(112px, 1fr));
  gap: 10px;
  margin: 0;
  font-size: 12px;
}

.drawer dt {
  font-family: var(--sans);
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-soft);
}

.drawer dd {
  margin: 2px 0 0;
  font-variant-numeric: tabular-nums;
}

/* ---------------------------------------------------------------------------
   Controls
   --------------------------------------------------------------------------- */

/* Panel buttons: square, inked, with a real pressed state. Deliberately not
   rounded - nothing on an equipment panel is. */
.key {
  font-family: var(--sans);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  padding: 7px 11px;
  border: 1px solid var(--rail);
  background: var(--rack-raised);
  color: var(--stock);
  cursor: pointer;
  transition: background 120ms ease, border-color 120ms ease;
}

.key:hover {
  background: #303841;
  border-color: var(--rail-lit);
}

.key:active {
  background: #14181d;
}

.key:focus-visible {
  outline: 2px solid var(--slot);
  outline-offset: 1px;
}

.key.armed {
  background: var(--slot);
  border-color: var(--slot);
  color: var(--rack-deep);
}

/* Lit state, for a pair of keys that report which one is currently true.
   Borrowed from an annunciator panel rather than a web toggle: the key for the
   state you are IN is lit, the other sits dark. Green is live, red is down -
   the same two colours the strips already use for airborne and cancelled, so
   the console keeps one vocabulary rather than inventing a second.

   Only used by Systems up / Systems down. If a third pair ever wants this,
   these are general enough to reuse; a one-off pair is not worth abstracting. */
.key.lit-live,
.key.lit-down {
  color: var(--stock);
  /* The lamp itself. Sits inside the border so the key does not change size
     between states - a panel key that resizes as it lights reads as a glitch. */
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.14);
}

.key.lit-live {
  background: var(--airborne);
  border-color: var(--airborne);
}

.key.lit-down {
  background: var(--cancelled);
  border-color: var(--cancelled);
}

/* Hover must not wash the lamp out - it stays lit, just slightly brighter. */
.key.lit-live:hover {
  background: #59ab8a;
  border-color: #59ab8a;
}

.key.lit-down:hover {
  background: #b04740;
  border-color: #b04740;
}

/* The unlit half of the pair. Dimmed rather than disabled: it is still the
   control you press to change state, so it must not look inert. */
.key.unlit {
  opacity: 0.55;
}

.key.unlit:hover {
  opacity: 1;
}

/* Working state.
   Regenerate clears every position document, ticks the fleet and rebuilds
   trails - several seconds. Without this the console looked like it had ignored
   the click, which invites a second press of a destructive button. */
.key.busy {
  background: var(--rack-deep);
  border-color: var(--slot);
  color: var(--slot);
  cursor: progress;
  position: relative;
  overflow: hidden;
}

/* A sweep along the top edge, so progress reads at a glance from across a room
   without the layout shifting as the label changes. */
.key.busy::after {
  content: '';
  position: absolute;
  inset: 0 auto auto 0;
  height: 2px;
  width: 40%;
  background: var(--slot);
  animation: sweep 900ms linear infinite;
}

@keyframes sweep {
  from { transform: translateX(-100%); }
  to { transform: translateX(350%); }
}

/* Busy beats lit, including on hover. A key that is mid-request must show that
   and nothing else - the lamp is about to change anyway, and the two states
   competing for the same key is exactly when a second click gets pressed. */
.key.busy.lit-live,
.key.busy.lit-down,
.key.busy.lit-live:hover,
.key.busy.lit-down:hover {
  background: var(--rack-deep);
  border-color: var(--slot);
  color: var(--slot);
  box-shadow: none;
}

/* While any action runs, the rest of the console is inert - another control
   could otherwise change the same state underneath it. */
body.busy .deck,
body.busy .panel-head {
  pointer-events: none;
  opacity: 0.72;
  transition: opacity 120ms ease;
}

/* The running button stays legible and interactive-looking. */
body.busy .key.busy {
  opacity: 1;
}

.key:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.key.danger:hover {
  background: var(--cancelled);
  border-color: var(--cancelled);
}

/* On the card stock the same key has to invert, or it disappears. */
.drawer .key {
  background: var(--stock);
  border-color: var(--ink-soft);
  color: var(--ink);
}

.drawer .key:hover {
  background: #fff;
  border-color: var(--ink);
}

.drawer .key.danger:hover {
  background: var(--cancelled);
  border-color: var(--cancelled);
  color: var(--stock);
}

.bays {
  display: grid;
  gap: 16px;
  align-content: start;
}

.bay {
  background: var(--rack);
  border: 1px solid var(--rail);
  border-top: 3px solid var(--rail-lit);
  padding: 13px 14px 15px;
}

.bay h2 {
  margin: 0 0 11px;
  font-family: var(--sans);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--label);
}

.keyrow {
  display: flex;
  gap: 5px;
  flex-wrap: wrap;
}

.keyrow + .keyrow {
  margin-top: 7px;
}

/* A row of keys directly after a field or a readout needs the same separation
   as one after another row, or the button reads as part of the text above it. */
.field + .keyrow,
.field-pair + .keyrow,
.stencil + .keyrow {
  margin-top: 11px;
}

/* --- Fleet list ----------------------------------------------------------- */

.craft {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 2px 10px;
  padding: 7px 0;
  border-bottom: 1px solid #2a3139;
  font-size: 12px;
}

.craft:last-child {
  border-bottom: none;
}

.craft b {
  font-weight: 700;
  letter-spacing: 0.05em;
}

.craft .state {
  font-variant-numeric: tabular-nums;
  color: var(--label);
}

.craft .state.up {
  color: var(--airborne);
}

.craft .sub {
  grid-column: 1 / -1;
  color: var(--label-dim);
  font-size: 11px;
}

/* --- Day selector --------------------------------------------------------- */

.days {
  display: flex;
  gap: 4px;
  overflow-x: auto;
  padding-bottom: 4px;
}

.day {
  font-family: var(--mono);
  font-size: 11px;
  font-variant-numeric: tabular-nums;
  padding: 5px 8px;
  border: 1px solid var(--rail);
  background: transparent;
  color: var(--label);
  cursor: pointer;
  white-space: nowrap;
}

.day:hover {
  border-color: var(--rail-lit);
  color: var(--stock);
}

.day.today {
  border-color: var(--slot);
  color: var(--slot);
}

.day.active {
  background: var(--stock);
  border-color: var(--stock);
  color: var(--ink);
}

.day:focus-visible {
  outline: 2px solid var(--slot);
  outline-offset: 1px;
}

/* ---------------------------------------------------------------------------
   Sign-in and messages
   --------------------------------------------------------------------------- */

.gate {
  max-width: 400px;
  margin: 90px auto;
  background: var(--rack);
  border: 1px solid var(--rail);
  border-top: 3px solid var(--slot);
  padding: 30px 28px;
  text-align: center;
}

.gate h1 {
  margin: 0 0 6px;
  font-size: 19px;
  letter-spacing: 0.06em;
}

.gate p {
  color: var(--label);
  font-size: 12px;
  line-height: 1.6;
  margin: 0 0 20px;
}

/* ---------------------------------------------------------------------------
   State banner
   ---------------------------------------------------------------------------
   Full-bleed and hazard-striped, matching the NOW rail. Deliberately loud: it
   is only ever on screen when something is switched off, and "the app is
   showing nothing" needs to be answered before it is asked. */

.state-banner {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 9px 22px;
  font-family: var(--sans);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--rack-deep);
  background: repeating-linear-gradient(
    -45deg,
    var(--slot),
    var(--slot) 14px,
    #b06d18 14px,
    #b06d18 28px
  );
  border-bottom: 1px solid var(--rack-deep);
}

.state-banner.severe {
  color: var(--stock);
  background: repeating-linear-gradient(
    -45deg,
    var(--cancelled),
    var(--cancelled) 14px,
    #86322a 14px,
    #86322a 28px
  );
}

/* ---------------------------------------------------------------------------
   Form fields inside the control bays
   --------------------------------------------------------------------------- */

.field {
  display: grid;
  gap: 4px;
  margin-bottom: 9px;
}

.field-pair {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 9px;
}

.field input {
  font-family: var(--mono);
  font-size: 14px;
  font-variant-numeric: tabular-nums;
  padding: 7px 9px;
  background: var(--rack-deep);
  border: 1px solid var(--rail);
  color: var(--stock);
  width: 100%;
}

.field input:focus {
  outline: none;
  border-color: var(--slot);
}

/* The date input's native picker icon is near-invisible on a dark field. */
.field input[type='date']::-webkit-calendar-picker-indicator {
  filter: invert(1);
  opacity: 0.6;
  cursor: pointer;
}

/* ---------------------------------------------------------------------------
   Crew editor
   --------------------------------------------------------------------------- */

.modal {
  position: fixed;
  inset: 0;
  background: rgba(10, 13, 17, 0.82);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  z-index: 50;
}

.modal-panel {
  background: var(--rack);
  border: 1px solid var(--rail);
  border-top: 3px solid var(--rail-lit);
  width: min(1000px, 100%);
  max-height: 90vh;
  display: flex;
  flex-direction: column;
}

.modal-head,
.modal-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 13px 16px;
  flex-wrap: wrap;
}

.modal-head {
  border-bottom: 1px solid var(--rail);
}

.modal-foot {
  border-top: 1px solid var(--rail);
}

.crew-table-wrap {
  overflow: auto;
  padding: 0 16px;
}

.crew-table {
  width: 100%;
  border-collapse: collapse;
}

.crew-table th {
  text-align: left;
  padding: 10px 8px 8px;
  position: sticky;
  top: 0;
  background: var(--rack);
  border-bottom: 1px solid var(--rail);
}

.crew-table td {
  padding: 3px 4px;
  border-bottom: 1px solid #2a3139;
}

.crew-table input {
  font-family: var(--mono);
  font-size: 13px;
  padding: 6px 7px;
  width: 100%;
  background: var(--rack-deep);
  border: 1px solid transparent;
  color: var(--stock);
}

.crew-table input:hover {
  border-color: var(--rail);
}

.crew-table input:focus {
  outline: none;
  border-color: var(--slot);
}

/* A duplicate code silently merges two people's rosters, so it is flagged as
   the operator types rather than on save. */
.crew-table input.invalid {
  border-color: var(--cancelled);
  background: #2a1a1a;
}

.crew-table .code-cell input {
  text-transform: uppercase;
  letter-spacing: 0.1em;
  font-weight: 700;
}

/* The row for a crew member whose code matches a real Flight Buddy user —
   the one who will actually see "my flights" in the app. */
.crew-table tr.linked .code-cell input {
  color: var(--airborne);
}

/* Sign-in form. Left-aligned labels against the centred panel, because a
   centred label above a full-width field reads as decoration rather than as
   the name of the thing below it. */
.gate-form {
  display: grid;
  gap: 6px;
  text-align: left;
}

.gate-form label {
  margin-top: 6px;
}

.gate-form input {
  font-family: var(--mono);
  font-size: 14px;
  padding: 9px 11px;
  background: var(--rack-deep);
  border: 1px solid var(--rail);
  color: var(--stock);
}

.gate-form input:focus {
  outline: none;
  border-color: var(--slot);
}

.gate-form .key {
  margin-top: 12px;
  padding: 10px;
}

/* The authenticator code is read back digit by digit, so it gets the space to
   be checked at a glance. */
#gate-totp {
  font-size: 22px;
  letter-spacing: 0.5em;
  text-align: center;
  padding-left: 0.5em;
}

.note {
  padding: 10px 13px;
  border-left: 3px solid var(--slot);
  background: var(--rack-raised);
  color: var(--label);
  font-size: 12px;
  margin-bottom: 16px;
}

.note.bad {
  border-left-color: var(--cancelled);
  color: #e0b3ae;
}

.empty {
  padding: 34px 16px;
  text-align: center;
  color: var(--label-dim);
  font-size: 12px;
}

.hidden {
  display: none !important;
}

/* ---------------------------------------------------------------------------
   Motion
   --------------------------------------------------------------------------- */

/* Strips seat into the rack in sequence, the way a rack is loaded. Short and
   staggered - long enough to read as physical, short enough not to delay work. */
@keyframes seat {
  from {
    opacity: 0;
    transform: translateX(-9px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

.strip {
  animation: seat 180ms ease-out backwards;
}

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