/* Run My Airline
   Palette borrowed from a ramp at night: sodium floodlight amber against wet
   asphalt, with taxiway blue for anything that moves. Numbers are the content
   here, so everything is tabular and condensed. */

:root {
  --ink:        #080e14;
  --panel:      #0f1922;
  --raised:     #16232e;
  --hair:       #223341;
  --hair-soft:  #1a2833;

  --text:       #dde8f1;
  --muted:      #7b93a6;
  --faint:      #506575;

  --amber:      #e6a33c;
  --amber-dim:  #8a6524;
  --taxi:       #4ea3dd;
  --gain:       #46cf98;
  --loss:       #ef5f6b;

  /* consumed by the generated aircraft silhouettes */
  --ac-body:    #c6d3df;
  --ac-shade:   #93a5b5;
  --ac-accent:  #e6a33c;
  --ac-glass:   #1a2833;

  --rail-w: 92px;
  --panel-w: 396px;
  --top-h: 58px;
}

* { box-sizing: border-box; }

/* Author styles beat the user-agent sheet, so any class that sets `display`
   silently defeats the `hidden` attribute. `.shell` is display:grid, which
   left it painted over the sign-up screen. Keep this rule above everything
   that sets display. */
[hidden] { display: none !important; }

html, body {
  margin: 0; height: 100%;
  background: var(--ink);
  color: var(--text);
  font-family: Barlow, system-ui, sans-serif;
  font-size: 15px;
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3, .num, dd, th, .rail-btn, .btn {
  font-family: "Barlow Condensed", "Barlow", sans-serif;
  letter-spacing: .01em;
}
.num, dd, td.n, th.n, input[type=number] { font-variant-numeric: tabular-nums; }

button, input, select { font: inherit; color: inherit; }
a { color: var(--taxi); }

/* ------------------------------------------------------------- controls -- */

.btn {
  border: 1px solid var(--hair);
  background: var(--raised);
  color: var(--text);
  border-radius: 3px;
  padding: 7px 14px;
  font-size: 16px;
  font-weight: 500;
  cursor: pointer;
  transition: background .12s, border-color .12s;
}
.btn:hover { background: #1d2d3a; border-color: #2e4657; }
.btn:focus-visible { outline: 2px solid var(--taxi); outline-offset: 2px; }
.btn:disabled { opacity: .42; cursor: not-allowed; }

.btn-primary {
  background: var(--amber); border-color: var(--amber); color: #17110320;
  color: #1a1204; font-weight: 600;
}
.btn-primary:hover { background: #f2b352; border-color: #f2b352; }

.btn-quiet { background: transparent; }
.btn-danger:hover { border-color: var(--loss); color: var(--loss); background: #22141a; }
.btn-sm { padding: 4px 9px; font-size: 14px; }
.btn-block { width: 100%; }

input[type=text], input[type=number], input[type=email],
input[type=password], input:not([type]), select {
  width: 100%;
  background: #0a141c;
  color: var(--text);
  border: 1px solid var(--hair);
  border-radius: 3px;
  padding: 8px 10px;
}
input:focus, select:focus { outline: none; border-color: var(--taxi); }
/* Autofill paints its own background over anything set above, and the only
   way to override it is a very long inset shadow in the colour we wanted. */
input:-webkit-autofill,
input:-webkit-autofill:focus {
  -webkit-text-fill-color: var(--text);
  -webkit-box-shadow: 0 0 0 40px #0a141c inset;
  caret-color: var(--text);
}

input[type=range] { width: 100%; accent-color: var(--amber); }

.field { display: block; margin-bottom: 14px; }
.field > span {
  display: block; font-size: 13px; color: var(--muted); margin-bottom: 5px;
}
.field-row { display: flex; gap: 10px; }
.field-row .field { flex: 1; }
.field-narrow { max-width: 90px; }

/* ---------------------------------------------------------------- boot --- */

.boot {
  position: fixed; inset: 0; display: grid; place-items: center;
  padding: 24px; overflow: auto;
  background:
    radial-gradient(900px 500px at 70% -10%, #14232f 0%, transparent 70%),
    var(--ink);
}
.boot-card {
  width: min(520px, 100%);
  background: var(--panel);
  border: 1px solid var(--hair);
  border-radius: 6px;
  padding: 30px 30px 26px;
}
/* The apron behind the opening screen.

   Fixed rather than scrolling, and behind everything: the card in front of it
   carries a form, and a background that competes with the text over it is a
   background that has failed. The gradient is doubled onto the image so the
   card's corner of the screen stays darkest wherever the window is cropped. */
.boot {
  background-image:
    linear-gradient(115deg, rgba(8, 14, 20, .93) 0%,
                            rgba(8, 14, 20, .66) 42%,
                            rgba(8, 14, 20, .28) 100%),
    url("/assets/boot-apron.svg");
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  background-attachment: fixed;
}
@media (max-width: 760px) {
  /* Fixed attachment is unreliable on mobile and costs a repaint per scroll. */
  .boot { background-attachment: scroll; }
}

/* The inlined apron sits between the background image and the card. */
.boot { position: relative; }
.boot-art {
  /* Fixed to the viewport rather than to .boot, which is as tall as the form
     inside it. Sized to the page, the drawing was scaled up to a thousand-odd
     pixels of height and cropped, so the aeroplanes came out twice the size
     they were drawn at. This also matches the still background underneath,
     which is attached the same way. */
  position: fixed; inset: 0; z-index: 0;
  overflow: hidden; pointer-events: none;
}
.boot-art svg { width: 100%; height: 100%; display: block; }
.boot-card { position: relative; z-index: 1; }

.wordmark {
  margin: 0 0 6px; font-size: 42px; font-weight: 600; line-height: .95;
}
.wordmark span { color: var(--amber); }
.boot-lede {
  margin: 0 0 26px; color: var(--muted); max-width: 44ch; line-height: 1.5;
}
.boot-existing h2 { margin: 0 0 8px; font-size: 18px; font-weight: 500; }
.boot-list { display: grid; gap: 6px; margin-bottom: 16px; }
.boot-item {
  display: flex; justify-content: space-between; align-items: center;
  width: 100%; text-align: left;
  background: var(--raised); border: 1px solid var(--hair);
  border-radius: 3px; padding: 9px 12px; cursor: pointer;
}
.boot-item:hover { border-color: var(--amber-dim); }
.boot-item small { color: var(--muted); }
.boot-or {
  color: var(--faint); font-size: 13px; margin-bottom: 18px;
  border-top: 1px solid var(--hair-soft); padding-top: 14px;
}

.modes { border: 0; padding: 0; margin: 4px 0 20px; }
.modes legend {
  font-size: 13px; color: var(--muted); padding: 0; margin-bottom: 6px;
}
.mode {
  display: grid; grid-template-columns: 18px 1fr; gap: 4px 9px;
  padding: 10px 12px; border: 1px solid var(--hair); border-radius: 3px;
  margin-bottom: 6px; cursor: pointer;
}
.mode:has(input:checked) { border-color: var(--amber-dim); background: #14202b; }
.mode input { grid-row: span 2; margin-top: 3px; accent-color: var(--amber); }
.mode-name { font-weight: 600; }
.map-country { margin-left: 8px; font: inherit; font-weight: 400; max-width: 100%; }
.mode-count { font-weight: 400; color: var(--muted); margin-left: 6px; }
.mode-note { font-size: 13px; color: var(--muted); line-height: 1.45; }

/* ---------------------------------------------------- choices as tiles ---

   The opening screen only. Seven groups of options stacked as full-width
   paragraphs made the first thing anybody sees three and a third screens
   tall; across instead of down, a whole group is one glance.

   auto-fit rather than a fixed count, so a group of two takes half the card
   each and a group of four takes a quarter, without either being told how
   many it has. Under about 380px they fall to two across on their own. */
.tiles .modes-row {
  display: grid; gap: 6px;
  grid-template-columns: repeat(auto-fit, minmax(104px, 1fr));
}
.tiles .modes-row .mode {
  display: flex; flex-direction: column; align-items: center; gap: 7px;
  text-align: center; margin: 0; padding: 11px 7px 10px;
  min-height: 84px; justify-content: center;
  color: var(--muted); transition: border-color .12s, color .12s, background .12s;
}
.tiles .modes-row .mode:hover { border-color: var(--hair); color: var(--text); }
/* The radio is hidden, not removed: arrow keys still walk the group, the
   form still submits the same value, and the tile is only its painting. */
.tiles .modes-row .mode input {
  position: absolute; opacity: 0; width: 0; height: 0; margin: 0;
}
.tiles .modes-row .mode:has(input:checked) {
  border-color: var(--amber-dim); background: #14202b; color: var(--text);
}
.tiles .modes-row .mode:has(input:checked) .mode-ico { color: var(--amber); }
/* Keyboard focus has to be visible on something, and the input it would
   normally land on is the one thing nobody can see. */
.tiles .modes-row .mode:has(input:focus-visible) {
  outline: 2px solid var(--taxi); outline-offset: 1px;
}
.mode-ico { width: 23px; height: 23px; flex: none; color: var(--faint); }
.tiles .modes-row .mode-name {
  font-size: 12.5px; font-weight: 500; line-height: 1.25; color: inherit;
}
.tiles .modes-row .mode-count { display: block; margin: 2px 0 0; font-size: 11px; }
/* Kept in the markup, read by the script, never drawn inside the tile. */
.tiles .modes-row .mode-note { display: none; }
.mode-why {
  margin: 8px 0 0; font-size: 13px; line-height: 1.45; color: var(--muted);
  /* Two lines of room whatever is in it. A paragraph that grows and shrinks
     as you click along a row drags every fieldset under it up and down. */
  min-height: 2.9em;
}
.tiles .map-country { display: block; margin: 0; width: 100%; }

.boot-legal { margin: 14px 0 0; font-size: 12.5px; }
.boot-legal a { color: var(--taxi); }

/* The "use it" drawer under a slot the player holds and is not flying. */
.aim-row > td { padding: 0 0 10px; border-top: 0; }
.aim-box { padding: 2px 2px 0; }
.aim-box .split { gap: 6px; }
.aim-box .aim-n { width: 72px; }
.aim-out { margin: 8px 0 0; line-height: 1.5; }

.form-error {
  color: var(--loss); font-size: 14px; margin: 12px 0 0;
}

/* --------------------------------------------------------------- shell --- */

/* The panel/map split is the player's to set. A 396px column is right when
   the map is the thing you are reading; it is not right when the thing you
   are reading is a rival's route list. */
.shell {
  display: grid; height: 100%;
  grid-template-columns: var(--rail-w) var(--panel-w) 1fr;
  grid-template-rows: var(--top-h) 1fr;
  grid-template-areas: "top top top" "rail panel stage";
}
/* With adverts on, every layout grows a row for them rather than having the
   strip float over whatever was there. Written out per layout because the
   grid areas differ, and a row that only sometimes exists is exactly the kind
   of thing to be explicit about. */
.shell.has-ads {
  grid-template-rows: var(--top-h) 1fr auto;
  grid-template-areas: "top top top" "rail panel stage" "adbar adbar adbar";
}
.shell.has-ads[data-layout="nomap"] {
  grid-template-rows: var(--top-h) 1fr auto;
  grid-template-areas: "top top" "rail panel" "adbar adbar";
}
.shell.has-ads[data-layout="overlay"] {
  grid-template-rows: var(--top-h) 1fr auto;
  grid-template-areas: "top top" "rail stage" "adbar adbar";
}

.shell[data-layout="split"]  { --panel-w: min(52vw, 760px); }
.shell[data-layout="panel"]  { --panel-w: min(70vw, 1100px); }
/* Rows are written out wherever the areas are, in every layout. At 1180px
   and below the base .shell stacks into three rows (map, then a docked
   panel), and a layout that redefined only its areas inherited those rows:
   the map got 46vh and an empty row sat under it. That is the default layout
   at the width a portal frames the game, so it was most players. */
.shell[data-layout="nomap"] {
  grid-template-columns: var(--rail-w) 1fr;
  grid-template-rows: var(--top-h) 1fr;
  grid-template-areas: "top top" "rail panel";
}
.shell[data-layout="nomap"] .stage { display: none; }

/* Over the map: the map is the application and everything else is a card
   floating on it, which is how the genre does it and how the map stops being
   a strip beside a wall of text. */
.shell[data-layout="overlay"] {
  grid-template-columns: var(--rail-w) 1fr;
  grid-template-rows: var(--top-h) 1fr;
  grid-template-areas: "top top" "rail stage";
}
.shell[data-layout="overlay"] .panel {
  position: absolute; z-index: 700;
  top: calc(var(--top-h) + 12px); left: calc(var(--rail-w) + 12px);
  width: min(460px, calc(100vw - var(--rail-w) - 24px));
  max-height: calc(100vh - var(--top-h) - 24px);
  border: 1px solid var(--hair); border-radius: 5px;
  box-shadow: 0 14px 40px rgba(0, 0, 0, .55);
  background: var(--panel);
}
.shell[data-layout="overlay"].panel-hidden .panel { display: none; }
.shell[data-layout="overlay"] .panel-bar { display: flex; }

/* Everywhere else the panel is a column and needs no chrome of its own. */
.panel-bar { display: none; }


/* With more room, the panel lays its cards out in columns instead of running
   one long strip down the left. */
.shell[data-layout="split"] .panel,
.shell[data-layout="panel"] .panel { --col: 340px; }
.shell[data-layout="nomap"] .panel { --col: 360px; }
.panel .cols {
  display: grid; gap: 0 16px;
  grid-template-columns: repeat(auto-fill, minmax(var(--col, 100%), 1fr));
  align-items: start;
}
/* Section headings inside a column grid span it rather than sitting in one
   column with the cards flowing round them. */
.panel .cols > h3 { grid-column: 1 / -1; }

.topbar {
  grid-area: top;
  display: flex; align-items: center; gap: 18px;
  padding: 0 18px;
  background: var(--panel);
  border-bottom: 1px solid var(--hair);
}
.brand {
  display: flex; align-items: center; gap: 9px;
  flex: 0 1 auto; min-width: 120px; max-width: 280px;
}
.brand-code {
  background: var(--amber); color: #1a1204; font-weight: 600;
  font-family: "Barlow Condensed", sans-serif; font-size: 19px;
  padding: 1px 7px; border-radius: 2px;
}
.brand-name {
  font-size: 17px; font-weight: 500; min-width: 0;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.brand-leave {
  background: none; border: 1px solid var(--hair); border-radius: 3px;
  color: var(--faint); font: inherit; font-size: 11.5px; padding: 2px 7px;
  cursor: pointer; margin-left: 4px; flex: none;
}
.brand-leave:hover { color: var(--text); border-color: var(--muted); }

/* Ten readouts do not wrap gracefully into a 58px bar, so they never wrap.
   The row takes whatever the brand and the controls leave, and readouts that
   do not fit are hidden least-important first (fitReadout in app.js) rather
   than clipped half-way through a number. */
.readout {
  display: flex; align-items: center; gap: 18px; margin: 0; flex: 1 1 0;
  flex-wrap: nowrap; overflow: hidden; min-width: 0;
}
.readout > div { flex: 0 0 auto; }
.readout > div.is-squeezed { display: none; }

/* A narrow window cannot hold the brand, ten readouts and six controls in
   one 58px row. The bar grows a second row there: controls on top, readouts
   underneath, still one line each. */
@media (max-width: 1280px) {
  :root { --top-h: 96px; }
  .topbar { flex-wrap: wrap; align-content: center; row-gap: 4px; padding: 4px 14px; }
  .topbar .brand { flex: 1 1 auto; }
  .topbar .readout { order: 3; flex: 1 1 100%; }
}
.readout dt { font-size: 11.5px; color: var(--faint); white-space: nowrap; }
.readout dd {
  margin: 0; font-size: 18px; font-weight: 500; line-height: 1.15;
  white-space: nowrap;
}
.readout .up { color: var(--gain); }
.readout .down { color: var(--loss); }

.topbar-actions {
  display: flex; align-items: center; gap: 10px; position: relative; flex: none;
}
.autorun {
  font-size: 14px; color: var(--muted); display: flex; gap: 5px;
  align-items: center; white-space: nowrap;
}
.autorun input { accent-color: var(--amber); }

.airline-pick {
  position: absolute; top: calc(100% + 8px); right: 0; z-index: 900;
  background: var(--panel); border: 1px solid var(--hair); border-radius: 4px;
  padding: 10px 12px; min-width: 240px; max-height: 60vh; overflow-y: auto;
  box-shadow: 0 8px 24px #0008;
}
.airline-pick h4 { margin: 0 0 6px; font-size: 13px; }
.airline-pick label { display: flex; align-items: center; gap: 8px; padding: 3px 0;
  font-size: 13px; cursor: pointer; }
.airline-pick .swatch { width: 12px; height: 3px; border-radius: 2px; flex: none; }
.airline-pick .swatch.is-dashed {
  background-image: repeating-linear-gradient(90deg, transparent 0 3px, var(--panel) 3px 5px);
}

.legend .tb-open { margin-left: 4px; padding: 1px 8px; font-size: 12px; }
.legend .tb-open.is-on { border-color: var(--amber); color: var(--amber); }
.timebar {
  position: absolute; left: 14px; right: 60px; bottom: 50px; z-index: 500;
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  background: #0a141ccc; border: 1px solid var(--hair); border-radius: 3px;
  padding: 6px 10px; backdrop-filter: blur(4px);
}
.timebar input[type=range] { flex: 1; min-width: 160px; accent-color: var(--amber); }
.timebar .tb-day { font-size: 13px; color: var(--text); white-space: nowrap;
  font-variant-numeric: tabular-nums; }
.timebar .tb-note { flex-basis: 100%; font-size: 12px; color: var(--muted); }
.timebar .tb-note:empty { display: none; }
.airline-pick .muted { margin-left: auto; font-size: 12px; }
.airline-pick .pick-actions { display: flex; gap: 6px; margin-top: 8px; }

.rail {
  grid-area: rail;
  background: var(--ink);
  border-right: 1px solid var(--hair);
  display: flex; flex-direction: column; padding-top: 8px;
  /* Sixteen tabs and the view controls under them do not fit a short
     window, and a rail that cannot scroll simply hides whatever came last
     — which, now that the controls are last, is the controls. */
  overflow-y: auto;
  scrollbar-width: thin;
}
/* The tabs keep their height while the controls take what is left; without
   this, flex shrinks the buttons to fit and the labels start clipping. */
.rail-btn { flex: none; }
.rail-btn {
  background: none; border: 0; border-left: 2px solid transparent;
  color: var(--muted); padding: 11px 6px; font-size: 15px; cursor: pointer;
}
.rail-btn:hover { color: var(--text); }
.rail-btn.is-on { color: var(--amber); border-left-color: var(--amber); }

/* The view controls, at the foot of the rail.

   They used to sit in the top bar beside the readout, and every one added
   pushed the readout's right-hand end further off the screen — the day the
   basemap picker arrived, the passenger count went with it. They are not
   readings. They are set once and then left alone, which is what the
   bottom of a navigation rail is for.

   Pushed down with auto rather than positioned, so they sit under the last
   tab on a tall window and scroll with it on a short one. */
.rail-tools {
  margin-top: auto;
  display: grid; gap: 5px;
  padding: 10px 6px calc(10px + env(safe-area-inset-bottom, 0px));
  border-top: 1px solid var(--hair-soft);
}
.rail-tools .autorun {
  display: flex; align-items: center; gap: 5px;
  font-size: 11px; color: var(--muted); margin: 0;
}
.rail-tools select {
  width: 100%; min-width: 0; font-size: 11px; padding: 4px 3px;
}
/* The rail is narrow, so a long option — "24h / day · real time" — has to
   be allowed to clip rather than widen the column. The title attribute on
   each control says what it is for. */
.rail-tools select { text-overflow: ellipsis; }

.panel {
  grid-area: panel;
  background: var(--panel);
  border-right: 1px solid var(--hair);
  display: flex; flex-direction: column; min-height: 0;
}
.panel-body {
  overflow-y: auto; flex: 1 1 auto;
  padding: 18px 18px 40px;
}
.panel h2 {
  margin: 0 0 3px; font-size: 22px; font-weight: 600;
}
.panel .sub { color: var(--muted); font-size: 13.5px; margin: 0 0 18px; line-height: 1.45; }
.panel h3 {
  margin: 22px 0 8px; font-size: 16px; font-weight: 600; color: var(--text);
  border-bottom: 1px solid var(--hair-soft); padding-bottom: 5px;
}

.stage { grid-area: stage; position: relative; }
#map { position: absolute; inset: 0; background: #0a1218; }

.legend {
  position: absolute; left: 14px; bottom: 14px; z-index: 500;
  display: flex; gap: 14px; font-size: 12.5px; color: var(--muted);
  background: #0a141ccc; border: 1px solid var(--hair); border-radius: 3px;
  padding: 6px 10px; backdrop-filter: blur(4px);
}
/* The standings, in the corner of the map, permanently. Same glass as the
   legend opposite it, so the two read as one furniture set rather than as a
   widget somebody bolted on. */
.rivalrail {
  position: absolute; right: 14px; bottom: 14px; z-index: 500;
  width: 242px; background: #0a141ce6;
  border: 1px solid var(--hair); border-radius: 3px;
  backdrop-filter: blur(4px); overflow: hidden;
}
.rr-head {
  display: flex; align-items: center; gap: 8px; width: 100%;
  background: none; border: 0; border-bottom: 1px solid var(--hair);
  padding: 6px 9px; cursor: pointer; color: var(--muted);
  font: inherit; font-size: 12px; text-align: left;
}
.rr-head:hover { color: var(--text); }
.rr-title { letter-spacing: .04em; text-transform: uppercase; font-size: 11px; }
.rr-rank {
  margin-left: auto; color: var(--amber); font-size: 12px;
  font-variant-numeric: tabular-nums;
}
/* Drawn rather than typed: a glyph picks up the emoji font on some platforms
   and arrives as a coloured triangle. */
.rr-caret {
  width: 0; height: 0; flex: none;
  border-left: 4px solid transparent; border-right: 4px solid transparent;
  border-top: 5px solid currentColor; transition: transform .12s ease;
}
.rivalrail.is-shut .rr-caret { transform: rotate(-90deg); }
.rivalrail.is-shut .rr-head { border-bottom: 0; }
.rivalrail.is-shut .rr-body { display: none; }

.rr-list { list-style: none; margin: 0; padding: 4px 0; }
.rr-list li {
  display: grid; grid-template-columns: 14px 1fr 34px 42px;
  align-items: center; gap: 6px; padding: 2px 9px; font-size: 12px;
}
.rr-n { color: var(--muted); font-variant-numeric: tabular-nums; }
.rr-who { overflow: hidden; display: flex; gap: 5px; align-items: baseline; }
.rr-who b { color: var(--text); font-size: 11.5px; }
.rr-name {
  color: var(--muted); overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap;
}
.rr-bar { height: 4px; background: #ffffff14; border-radius: 2px; overflow: hidden; }
.rr-bar > i { display: block; height: 100%; background: var(--taxi); }
.rr-pax {
  text-align: right; color: var(--text);
  font-variant-numeric: tabular-nums;
}
/* Your own row in amber, like every other place the interface points at you. */
.rr-list li.is-you .rr-n,
.rr-list li.is-you .rr-name,
.rr-list li.is-you .rr-pax { color: var(--amber); }
.rr-list li.is-you .rr-who b { color: var(--amber); }
.rr-list li.is-you .rr-bar > i { background: var(--amber); }
/* The rows skipped over when you are outside the leaders. */
.rr-gap {
  display: block !important; padding: 0 9px 1px; color: var(--muted);
  font-size: 11px; line-height: 1;
}

/* Room for it. The history scrubber runs the width of the map, and the two
   would sit on top of each other otherwise. */
.stage:has(.rivalrail:not([hidden])) .timebar { right: 270px; }

/* On a narrow screen the map is small enough that a fixed panel in the
   corner is in the way rather than in the background. The header stays, so
   the rank is still there; the table goes. */
@media (max-width: 760px) {
  .rivalrail { width: 160px; }
  .rivalrail .rr-body { display: none; }
  .rivalrail .rr-head { border-bottom: 0; }
  .rr-caret { display: none; }
  .stage:has(.rivalrail:not([hidden])) .timebar { right: 60px; bottom: 92px; }
}

.dot { width: 8px; height: 8px; border-radius: 50%; display: inline-block; margin-right: 5px; }
.dot-hub { background: var(--amber); }
.dot-route { background: var(--taxi); }
.dot-loss { background: var(--loss); }
/* Hollow, to read as the dashed thin line it stands for rather than as a
   fourth solid colour. */
.dot-idle {
  background: none; border: 1px solid var(--taxi); opacity: .75;
}
.dot-unassigned { background: #e8d44d; }

/* --------------------------------------------------------------- lists --- */

.card {
  background: var(--raised); border: 1px solid var(--hair);
  border-radius: 4px; padding: 12px; margin-bottom: 8px;
}
.card.tap { cursor: pointer; }
.card.tap:hover { border-color: var(--amber-dim); }
.card-head { display: flex; justify-content: space-between; gap: 10px; align-items: baseline; }
.card-title { font-weight: 600; font-size: 16px; font-family: "Barlow Condensed", sans-serif; }
.card-meta { color: var(--muted); font-size: 13px; }
.card-body { margin-top: 7px; font-size: 13.5px; color: var(--muted); line-height: 1.5; }

.kv { display: grid; grid-template-columns: 1fr auto; gap: 3px 12px; font-size: 13.5px; }
.kv dt { color: var(--muted); }
.kv dd { margin: 0; font-variant-numeric: tabular-nums; text-align: right; }

table.grid { width: 100%; border-collapse: collapse; font-size: 13.5px; }
table.grid th {
  text-align: left; color: var(--faint); font-weight: 500; font-size: 13px;
  padding: 4px 6px; border-bottom: 1px solid var(--hair);
}
table.grid td { padding: 5px 6px; border-bottom: 1px solid var(--hair-soft); }
table.grid td.n, table.grid th.n { text-align: right; }
table.grid tr.tap { cursor: pointer; }
table.grid tr.tap:hover td { background: #17252f; }

.pill {
  display: inline-block; font-size: 11.5px; padding: 1px 6px; border-radius: 2px;
  background: var(--hair); color: var(--muted);
}
.pill.on  { background: #123528; color: var(--gain); }
.pill.off { background: #33201f; color: var(--loss); }
/* A carrier that opened a route at you. Marked rather than merely
   coloured: "came after you" is the point of the row. */
.pill.pill-warn { background: rgba(230, 163, 60, .14); color: var(--amber); }
.pill.wait{ background: #33291a; color: var(--amber); }

.gain { color: var(--gain); }
.loss { color: var(--loss); }
.muted { color: var(--muted); }

.plane-art {
  width: 100%; height: 72px; object-fit: contain; display: block;
  margin: 4px 0 8px;
}
.plane-art.small { height: 42px; width: 64px; margin: 0; }

.split { display: flex; gap: 8px; align-items: center; }
.split > * { flex: 1; }

.empty {
  border: 1px dashed var(--hair); border-radius: 4px;
  padding: 18px; color: var(--muted); font-size: 14px; line-height: 1.55;
}
.empty b { color: var(--text); font-weight: 600; }

.bar { height: 5px; background: var(--hair-soft); border-radius: 3px; overflow: hidden; }
.bar > i { display: block; height: 100%; background: var(--taxi); }
.bar.warn > i { background: var(--amber); }
.bar.bad  > i { background: var(--loss); }

.spark { width: 100%; height: 58px; display: block; }

.toast {
  position: fixed; bottom: 18px; left: 50%; transform: translateX(-50%);
  background: var(--raised); border: 1px solid var(--hair);
  border-left: 3px solid var(--amber);
  padding: 10px 16px; border-radius: 3px; z-index: 9000; max-width: 460px;
}
.toast.bad { border-left-color: var(--loss); }
.toast.good { border-left-color: var(--gain); }

/* ---------------------------------------------------------------- map ---- */

.leaflet-container { background: #0a1218; font-family: Barlow, sans-serif; }
.leaflet-control-attribution {
  background: #0a141ccc !important; color: var(--faint) !important; font-size: 10.5px;
}
.leaflet-control-attribution a { color: var(--muted) !important; }
.leaflet-bar a {
  background: var(--raised); color: var(--text); border-color: var(--hair);
}
.leaflet-bar a:hover { background: #1d2d3a; }

.leaflet-popup-content-wrapper, .leaflet-popup-tip {
  background: var(--raised); color: var(--text);
  border: 1px solid var(--hair); border-radius: 4px; box-shadow: none;
}
.leaflet-popup-content { margin: 10px 12px; font-size: 13.5px; line-height: 1.5; }
.leaflet-popup-content b { font-family: "Barlow Condensed", sans-serif; font-size: 16px; }

/* The marker is positioned every animation frame, so nothing here may carry a
   CSS transition: the browser would then be tweening towards a target that has
   already moved, and the result stutters instead of gliding. */
.ac-wrap { pointer-events: auto; }
.ac-marker {
  transform-origin: 50% 50%;
  /* The wrapper must be explicitly sized. Without it the img's width:100%
     resolves against an auto-height parent and the SVG renders at its
     intrinsic size — which for an A320 is 386x410px of aeroplane. */
  width: 100%; height: 100%;
  filter: drop-shadow(0 1px 2px rgba(0, 0, 0, .85));
  will-change: transform;
}
.ac-marker img {
  display: block; width: 100%; height: 100%;
  object-fit: contain;
}

/* Registration and load, level under the aircraft whatever its heading. */
.ac-tag {
  position: absolute; top: 100%; left: 50%; transform: translateX(-50%);
  margin-top: 2px; white-space: nowrap; text-align: center;
  font-family: "Barlow Condensed", sans-serif; font-size: 11px; line-height: 1.15;
  color: var(--text);
  /* A plate, not a glow. Pale text with a black shadow reads on a dark
     basemap and turns to a smudge on a light one, and on the satellite
     maps neither choice works: one view holds bright desert and dark
     ocean, so there is no text colour that survives both. Carrying its
     own background is the only thing that does.
     Kept tight and mostly transparent — there can be a hundred of these
     at once over a hub, and the map has to stay a map. */
  background: rgba(8, 14, 20, .74);
  border-radius: 4px;
  padding: 1px 5px 2px;
  text-shadow: 0 1px 2px rgba(0, 0, 0, .9);
  pointer-events: none;
}
.ac-tag b { display: block; letter-spacing: .04em; }
/* Lifted from --muted. That is a label colour against a panel, and on a
   74%-opaque plate over a bright map it was the least readable thing on
   the screen — which is a shame, because it is the line carrying the
   load factor. */
.ac-tag span { color: #a9bccd; font-size: 10.5px; }

/* -------------------------------------------------------------- popups -- */
.pop { font-size: 13px; line-height: 1.5; }
.pop-body { margin-top: 6px; }
.pop-hub { color: var(--amber); font-weight: 600; margin-bottom: 4px; }
.pop-link {
  border-top: 1px solid var(--hair); padding-top: 6px; margin-top: 6px;
}
.pop-link-head { display: flex; justify-content: space-between; gap: 8px; }

/* --------------------------------------------- aircraft and schedules -- */
.mini-h {
  margin: 12px 0 5px; font-family: "Barlow Condensed", sans-serif;
  font-size: 13px; font-weight: 600; letter-spacing: .08em;
  text-transform: uppercase; color: var(--faint);
}
.ac-card.is-focus { border-color: var(--amber); box-shadow: 0 0 0 1px var(--amber-dim); }

.flight-now {
  margin-top: 9px; padding: 8px 9px; border-radius: 3px;
  background: var(--panel); border: 1px solid var(--hair-soft);
  font-size: 13px;
}
.flight-leg { display: flex; gap: 6px; align-items: baseline; margin-bottom: 5px; }
.flight-arrow { color: var(--amber); }
.flight-now .bar { margin: 4px 0 5px; }

.sched { margin-top: 4px; }
.sched input[type=range] { width: 100%; margin-bottom: 6px; }
.sched .kv { margin-bottom: 8px; }

.ap-label.rival { color: #c79cf0; }
.row-clash td { background: rgba(239, 95, 107, .08); }
.ap-label {
  font-size: 11px; color: var(--muted); text-shadow: 0 0 4px #000, 0 0 4px #000;
  white-space: nowrap; font-variant-numeric: tabular-nums;
}

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

@media (max-width: 1180px) {
  .shell {
    grid-template-columns: var(--rail-w) 1fr;
    grid-template-rows: var(--top-h) 46vh 1fr;
    grid-template-areas: "top top" "rail stage" "panel panel";
  }
  .shell.has-ads {
    grid-template-rows: var(--top-h) 46vh 1fr auto;
    grid-template-areas: "top top" "rail stage" "panel panel" "adbar adbar";
  }
  .panel { border-right: 0; border-top: 1px solid var(--hair); }
  .readout { gap: 14px; }
}

.topbar-actions select {
  background: var(--raised); color: var(--text);
  border: 1px solid var(--hair); border-radius: 3px;
  padding: 3px 5px; font: inherit; font-size: 13px;
}

/* ------------------------------------------------------- tracking board -- */
.board-row {
  border: 1px solid var(--hair); border-left-width: 3px; border-radius: 3px;
  padding: 9px 10px; margin-bottom: 7px; background: var(--raised);
}
.board-row.is-en-route { border-left-color: var(--amber); }
.board-row.is-boarding { border-left-color: #4ea3dd; }
.board-row.is-landed   { border-left-color: var(--hair); opacity: .68; }
.board-row.is-scheduled{ border-left-color: var(--hair-soft); }

.board-head { display: flex; gap: 8px; align-items: baseline; }
.board-no {
  font-family: "Barlow Condensed", sans-serif; font-weight: 600;
  font-size: 15px; letter-spacing: .04em;
}
.board-pair { color: var(--muted); font-size: 13px; }
.board-state {
  margin-left: auto; font-size: 11px; letter-spacing: .07em;
  text-transform: uppercase; color: var(--faint);
}
.board-row.is-en-route .board-state { color: var(--amber); }
.board-times { font-size: 13.5px; margin: 4px 0 5px; font-variant-numeric: tabular-nums; }
.board-times .muted { margin-left: 6px; }
.board-foot { font-size: 12.5px; margin-top: 4px; }

/* A note about the figures rather than a problem to solve — the rebuild is
   waiting on the player, not the other way round — so it is stated quietly
   along the foot of the panel. Not in the panel bar: that is hidden in four of
   the five layouts, and this has to be visible in all of them. */
.panel-stale {
  flex: 0 0 auto; padding: 6px 14px;
  border-top: 1px solid var(--amber-dim);
  background: rgba(230, 163, 60, .09);
  color: var(--amber); font-size: 12px; line-height: 1.35;
}

/* ------------------------------------------------------------ adverts -- */
/* A strip along the bottom, inside the grid rather than floating over it. An
   advert that covers the map is an advert that gets the game closed, and the
   whole point of the paid tier is that it is worth paying to remove — which
   only works if the free version is still playable. */
.adbar {
  grid-area: adbar;
  display: flex; align-items: center; gap: 12px;
  padding: 4px 12px; min-height: 62px;
  background: var(--panel); border-top: 1px solid var(--hair);
}
.adbar-slot { flex: 1 1 auto; min-width: 0; overflow: hidden; text-align: center; }
.adbar-hide {
  flex: 0 0 auto; background: none; cursor: pointer;
  border: 1px solid var(--hair); border-radius: 3px;
  color: var(--muted); font-size: 11.5px; padding: 4px 9px;
  font-family: "Barlow Condensed", sans-serif; letter-spacing: .05em;
  text-transform: uppercase;
}
.adbar-hide:hover { border-color: var(--amber); color: var(--amber); }

/* The plans. Annual first and marked, because it is the one that leaves the
   most of the money with the game. */
.plan {
  display: block; border: 1px solid var(--hair); border-radius: 4px;
  background: var(--raised); padding: 10px 12px; margin-bottom: 8px;
}
.plan.is-best { border-color: var(--amber-dim); }
.plan-head { display: flex; align-items: baseline; gap: 8px; }
.plan-name { font-family: "Barlow Condensed", sans-serif; font-size: 16px; }
.plan-price { margin-left: auto; font-size: 18px; font-variant-numeric: tabular-nums; }
.plan-price small { color: var(--muted); font-size: 12px; }
.plan-note { color: var(--muted); font-size: 12.5px; margin-top: 4px; line-height: 1.45; }
.plan-tag {
  background: var(--amber); color: var(--ink); border-radius: 2px;
  font-size: 10px; letter-spacing: .07em; text-transform: uppercase;
  padding: 1px 5px;
}

/* --------------------------------------------------- route economics -- */
/* The findings, above the table. A table of twenty routes is a table nobody
   reads to the bottom of, so the three or four things worth acting on are
   lifted out of it and said in words. */
.findings { margin: 4px 0 14px; }
.finding {
  border: 1px solid var(--hair); border-left-width: 3px; border-radius: 3px;
  background: var(--raised); padding: 8px 11px; margin-bottom: 6px;
  font-size: 13px; line-height: 1.5; cursor: pointer;
}
.finding.is-good { border-left-color: var(--gain); }
.finding.is-warn { border-left-color: var(--amber); }
.finding.is-info { border-left-color: var(--taxi); }
.finding:hover { border-color: var(--taxi); }

/* Seven columns will not fit a narrow panel, so the table scrolls inside its
   own box rather than pushing the page sideways. */
.table-scroll { overflow-x: auto; }
.table-scroll .grid { min-width: 560px; }
table.econ th.tap { cursor: pointer; white-space: nowrap; }
table.econ th.tap:hover { color: var(--text); }
table.econ th.is-on { color: var(--amber); }
table.econ td .muted { display: block; font-size: 11.5px; }
/* An airframe working two sectors is the thing that makes "this route ties up
   six hours" misleading, so it is marked where the tail number is. */
.shares {
  display: inline-block; margin-left: 4px; padding: 0 4px;
  border: 1px solid var(--amber-dim); border-radius: 2px;
  color: var(--amber); font-size: 9.5px; letter-spacing: .05em;
  text-transform: uppercase; vertical-align: 1px; cursor: help;
}

/* ---------------------------------------------------------- slot desk -- */
table.slotdesk tr.is-full td { opacity: .5; }
table.slotdesk td.own { color: var(--amber); font-weight: 600; }
/* One hour you hold, as a chip, so a row of them reads as a holding rather
   than a list of numbers. */
.slot-chip {
  display: inline-block; margin: 2px 3px 0 0; padding: 1px 6px;
  border: 1px solid var(--hair); border-radius: 2px;
  background: var(--raised); font-size: 11.5px;
  font-variant-numeric: tabular-nums;
}
.slot-chip.is-idle { border-color: var(--amber-dim); color: var(--amber); }

/* ------------------------------------------------------------- worlds -- */
/* The code is the only way anybody else gets into a private world, so it is
   set out to be read off a screen and typed on another one. */
.joincode {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  border: 1px solid var(--amber-dim); border-radius: 4px;
  background: rgba(230, 163, 60, .08);
  padding: 8px 12px; margin: 10px 0 12px;
}
.joincode > span { color: var(--muted); font-size: 12.5px; }
.joincode > b {
  font-family: "Barlow Condensed", sans-serif; font-size: 22px;
  letter-spacing: .22em; color: var(--amber);
}
.joincode > .btn { margin-left: auto; flex: 0 0 auto; }

/* The industry's lines are dashed rather than coloured: they are one
   category, not four identities competing for hues. */
.chart-legend i.dashed {
  background: none;
  border-top: 2px dashed var(--muted);
  border-radius: 0;
}

/* The ranking reads down the bar column, so the bars get the room and the
   names get only what they need. */
.grid.rank td { vertical-align: middle; }
.rank-n { width: 26px; color: var(--muted); }
/* The name wraps rather than being clipped. Ellipsis looked tidier until a
   narrow panel rendered the whole column as "T…", which is a ranking you
   cannot read. */
.rank-name { min-width: 7em; }
.rank-bar { width: 22%; min-width: 44px; }
.rank-worth { white-space: nowrap; color: var(--muted); }
.grid.rank tr.is-you .rank-worth { color: var(--text); }
/* The asterisk marks a figure on the other basis — a carrier's fleet rather
   than a company — and the note under the table says which. */
.rank-basis { color: var(--faint); margin-left: 2px; }
.rank-bar .bar { margin: 0; }
/* Your own bar is the one the eye should find first without reading. */
.rank-bar .bar.is-mine > i { background: var(--amber); }

/* Founding and joining sit side by side because they are the same decision
   seen from two ends, and neither is worth a screen of its own. */
.alli-actions {
  display: flex; align-items: flex-end; gap: 10px; flex-wrap: wrap;
  margin-top: 10px;
}
.alli-actions .field { margin-bottom: 0; flex: 1 1 160px; }
.alli-actions .btn { flex: 0 0 auto; margin-bottom: 1px; }

/* A season ending is the one moment in this game with a result in it, so it
   is stated rather than left to be inferred from a frozen clock. */
.season-over {
  border: 1px solid var(--amber-dim); border-left-width: 3px;
  border-radius: 4px; background: rgba(230, 163, 60, .08);
  padding: 9px 12px; margin: 10px 0 12px; font-size: 13px; line-height: 1.5;
}
.season-over b { color: var(--amber); }
tr.is-winner > td { background: rgba(230, 163, 60, .16); }

/* Your own row in the standings, so a table of thirty-two is scannable. */
tr.is-you > td { background: rgba(230, 163, 60, .09); }
tr.is-you .pill {
  background: var(--amber); color: var(--ink); border: 0;
  font-size: 10px; letter-spacing: .06em; text-transform: uppercase;
}

/* ------------------------------------------------------------- slots -- */
/* Twenty-four columns against a capacity line. Read as a shape rather than as
   numbers: two humps means the banks are filling, a flat wall means the field
   is done and the only way to add a frequency is to buy a second hub. */
/* Held to a chart-like width. Stretched across a panel-focus layout the
   twenty-four columns become a thin wide band and the shape stops reading. */
.slots { margin: 2px 0 14px; max-width: 520px; }
.slots-head {
  display: flex; justify-content: space-between; gap: 10px;
  font-size: 11.5px; color: var(--muted); margin-bottom: 4px;
}
.slot-bars {
  display: flex; gap: 2px; align-items: flex-end;
  height: 52px; padding-top: 2px;
  border-top: 1px dashed var(--hair);        /* the capacity line */
  border-bottom: 1px solid var(--hair);
}
.slot-col { flex: 1 1 0; height: 100%; display: flex; align-items: flex-end; }
.slot-stack {
  display: flex; flex-direction: column; justify-content: flex-end;
  width: 100%; height: 100%; border-radius: 1px;
  /* A faint track, not a filled bar: the height of the *drawn* part is the
     whole message, so an empty hour has to look empty. */
  background: rgba(255, 255, 255, .045);
}
.slot-stack > b { display: block; width: 100%; }
.slot-others { background: var(--faint); }
.slot-mine { background: var(--amber); }
.slot-col.is-full .slot-mine { background: var(--loss); }
.slot-col.is-full .slot-others { background: #6d4147; }
.slot-col.is-now .slot-stack { outline: 1px solid var(--taxi); outline-offset: 1px; }
/* An hour you hold a slot in. Marked under the column rather than inside it,
   because a holding is not a movement — it is the right to make one, and it
   is still yours on a day you do not use it. */
.slot-col.is-held { border-bottom: 2px solid var(--amber); }
.slot-axis {
  display: flex; justify-content: space-between;
  font-size: 10px; color: var(--faint); margin-top: 3px;
  font-variant-numeric: tabular-nums;
}
.slots-note { margin: 6px 0 0 !important; font-size: 12px; }

/* ------------------------------------------------------- schedule board -- */
/* A departures board reads by column: the time first and always in the same
   place, the flight in the middle, the state on the right. Everything here is
   in service of that alignment, which is what makes forty movements scannable
   when a list of forty cards is not. */
.sc-chips {
  display: flex; flex-wrap: wrap; gap: 5px; margin: 10px 0 12px;
}
.sc-chip {
  display: flex; flex-direction: column; align-items: flex-start; gap: 1px;
  background: var(--raised); border: 1px solid var(--hair);
  border-radius: 3px; padding: 4px 8px; cursor: pointer;
  color: var(--muted); line-height: 1.15;
}
.sc-chip b {
  font-family: "Barlow Condensed", sans-serif; font-size: 15px;
  letter-spacing: .05em; color: var(--text);
}
.sc-chip small { font-size: 10.5px; color: var(--faint); }
.sc-chip.is-hub { border-color: var(--amber-dim); }
.sc-chip:hover { border-color: var(--taxi); }
.sc-chip.is-on {
  background: var(--amber); border-color: var(--amber);
}
.sc-chip.is-on b, .sc-chip.is-on small { color: var(--ink); }
/* Dimmed, not hidden: a strip that reflows while somebody is typing moves the
   chip they were about to click. */
.sc-chip.is-empty { opacity: .32; }
.sc-chip.is-empty.is-on { opacity: .7; }

.sc-tabs { display: flex; gap: 4px; margin: 10px 0 9px; }
.sc-tab {
  flex: 1 1 0; background: none; border: 1px solid var(--hair);
  border-radius: 3px; padding: 4px 6px; cursor: pointer;
  color: var(--muted); font-size: 12.5px;
  font-family: "Barlow Condensed", sans-serif; letter-spacing: .04em;
}
.sc-tab.is-on { background: var(--raised); color: var(--text); border-color: var(--taxi); }

.sc-row {
  border: 1px solid var(--hair); border-left-width: 3px; border-radius: 3px;
  margin-bottom: 5px; background: var(--raised);
}
.sc-row.is-en-route  { border-left-color: var(--amber); }
.sc-row.is-boarding  { border-left-color: var(--taxi); }
.sc-row.is-landed    { border-left-color: var(--hair); opacity: .62; }
.sc-row.is-scheduled { border-left-color: var(--hair-soft); }
.sc-row.is-open { opacity: 1; border-color: var(--taxi); }

.sc-line {
  display: flex; gap: 9px; align-items: flex-start;
  padding: 7px 9px; cursor: pointer;
}
.sc-line:hover { background: rgba(78, 163, 221, .07); }

.sc-time {
  flex: 0 0 46px; display: flex; flex-direction: column; line-height: 1.1;
}
.sc-time b { font-size: 16px; font-variant-numeric: tabular-nums; }
.sc-kind {
  font-size: 9.5px; letter-spacing: .09em; text-transform: uppercase;
  color: var(--faint);
}
.sc-mid { flex: 1 1 auto; min-width: 0; }
.sc-head-line { display: flex; gap: 7px; align-items: baseline; flex-wrap: wrap; }
.sc-pair { color: var(--muted); font-size: 12.5px; }
.sc-pair b { color: var(--text); }
.sc-sub {
  font-size: 11.5px; margin-top: 2px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.sc-right { flex: 0 0 auto; text-align: right; line-height: 1.25; }
.sc-state {
  display: block; font-size: 10.5px; letter-spacing: .07em;
  text-transform: uppercase; color: var(--faint);
}
.sc-row.is-en-route .sc-state { color: var(--amber); }
.sc-row.is-boarding .sc-state { color: var(--taxi); }
.sc-prog { display: block; font-size: 11px; font-variant-numeric: tabular-nums; }

.sc-edit {
  border-top: 1px solid var(--hair); padding: 9px; cursor: default;
}
.sc-edit .field { margin-bottom: 6px; }
.sc-edit .sub { margin: 0 0 8px; }

/* Three buttons across a phone-width panel become three words a line each.
   Stacked, they stay readable and the primary action stays first. */
@media (max-width: 430px) {
  .sc-line { gap: 7px; padding: 6px 7px; }
  .sc-time { flex-basis: 42px; }
  .sc-time b { font-size: 15px; }
  .sc-edit .split { flex-wrap: wrap; }
  .sc-edit .split > * { flex: 1 1 100%; }
}

/* -------------------------------------------------------- rotation rows -- */
.rot-row {
  border-top: 1px solid var(--hair); padding-top: 9px; margin-top: 9px;
}
.rot-head { display: flex; gap: 8px; align-items: center; margin-bottom: 5px; }
.rot-head b { font-family: "Barlow Condensed", sans-serif; font-size: 15px; }
.rot-head .muted { font-size: 12px; }
.rot-head button { margin-left: auto; flex: 0 0 auto; }
.rot-row input[type=range] { width: 100%; margin-bottom: 5px; }

.time-in {
  background: var(--panel); color: var(--text); border: 1px solid var(--hair);
  border-radius: 3px; padding: 2px 5px; font: inherit; font-size: 13px;
}

.split-wrap { display: flex; flex-wrap: wrap; gap: 6px; }
.split-wrap .btn { flex: 0 0 auto; }

/* A popup needs its own button style: Leaflet's container resets most of it. */
.pop-btn {
  display: block; width: 100%; margin-top: 8px; padding: 5px 8px;
  background: var(--amber); border: none; border-radius: 3px;
  color: #171103; font: inherit; font-size: 13px; font-weight: 600;
  cursor: pointer;
}
.pop-btn:hover { filter: brightness(1.08); }

#t-clock { font-variant-numeric: tabular-nums; }
#t-clock.muted { color: var(--faint); }

/* A statement the player needs to read, not another row in a table. */
.callout {
  border: 1px solid var(--amber-dim); border-left: 3px solid var(--amber);
  background: rgba(230, 163, 60, .07);
  border-radius: 3px; padding: 10px 12px; margin: 8px 0;
  font-size: 13.5px; line-height: 1.55;
}
.callout.bad {
  border-color: rgba(239, 95, 107, .45); border-left-color: var(--loss);
  background: rgba(239, 95, 107, .07);
}

/* --------------------------------------------------- route accordions -- */
/* Routes and aircraft collapse the same way — a dozen of either is an
   unreadable wall when every one takes a screen. */
.route-card, .ac-card { padding: 0; }
/* The route you arrived at from a click on the map, so it is findable in a
   list of a dozen. Matches the ring the fleet card already uses. */
.route-card.is-focus { border-color: var(--amber); box-shadow: 0 0 0 1px var(--amber-dim); }
.route-card > summary, .ac-card > summary {
  display: flex; align-items: baseline; gap: 9px;
  padding: 10px 12px 10px 26px; cursor: pointer; position: relative;
  list-style: none;
}
.route-card > summary::-webkit-details-marker,
.ac-card > summary::-webkit-details-marker { display: none; }
/* A disclosure triangle drawn rather than borrowed, so it matches the rest
   and rotates when the section opens. */
.route-card > summary::before, .ac-card > summary::before {
  content: ""; position: absolute; left: 11px; top: 50%;
  width: 0; height: 0; margin-top: -4px;
  border-left: 5px solid var(--faint);
  border-top: 4px solid transparent; border-bottom: 4px solid transparent;
  transition: transform .12s ease;
}
.route-card[open] > summary::before,
.ac-card[open] > summary::before { transform: rotate(90deg); }
.route-card > summary:hover, .ac-card > summary:hover {
  background: rgba(255, 255, 255, .025);
}
.route-card[open] > summary, .ac-card[open] > summary {
  border-bottom: 1px solid var(--hair-soft);
}
/* The status pill sits after the figures, so it must not be stretched by the
   baseline alignment the rest of the summary uses. */
.ac-card > summary > .pill { align-self: center; }

.rc-main { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.rc-top { display: flex; align-items: baseline; gap: 8px; }
.rc-pair {
  font-family: "Barlow Condensed", sans-serif; font-weight: 600; font-size: 16px;
}
.rc-cities {
  font-size: 11.5px; color: var(--muted); white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis;
}
.rc-no { color: var(--faint); font-size: 12px; letter-spacing: .04em; }
.rc-figures {
  margin-left: auto; align-self: center; font-size: 13.5px; display: flex; gap: 8px;
  font-variant-numeric: tabular-nums;
}
.rc-body { padding: 10px 12px 12px; }

/* ------------------------------------------------------- airport labels -- */
.ap-name {
  font-family: "Barlow Condensed", sans-serif; font-size: 11.5px;
  font-weight: 600; letter-spacing: .05em; color: #9fc6e4;
  white-space: nowrap; text-shadow: 0 1px 3px #000, 0 0 5px #000;
  pointer-events: none; line-height: 1.05;
}
.ap-name span {
  display: block; font-weight: 400; font-size: 10.5px;
  letter-spacing: .02em; color: var(--muted);
}
.ap-label span {
  display: block; font-weight: 400; font-size: 10.5px;
  letter-spacing: .02em; color: var(--muted);
}
/* Leaflet's default tooltip is white-on-white against a dark map. */
.leaflet-tooltip.ap-tip {
  background: var(--raised); border: 1px solid var(--hair);
  color: var(--text); font-size: 12.5px; line-height: 1.4;
  box-shadow: 0 2px 10px rgba(0, 0, 0, .5);
}
.leaflet-tooltip.ap-tip::before { border-top-color: var(--hair); }

.field-hint {
  display: block; margin-top: 3px; font-size: 12px; color: var(--muted);
  min-height: 1em;
}

/* ------------------------------------------------------------- routings -- */
.routing {
  margin: 9px 0; padding: 8px 10px; border-radius: 3px;
  background: var(--panel); border: 1px solid var(--hair-soft);
  font-size: 12.5px; line-height: 1.5;
}
.routing-line { display: flex; align-items: center; gap: 0; margin-bottom: 4px; }
.rt-stop {
  font-family: "Barlow Condensed", sans-serif; font-weight: 600; font-size: 14px;
  letter-spacing: .04em; color: var(--muted);
}
.rt-stop.rt-end { color: var(--text); }
.rt-dash {
  flex: 1; height: 1px; background: var(--hair); margin: 0 7px; min-width: 14px;
}

/* ------------------------------------------------------- ordering flow -- */
.maker-grid {
  display: grid; gap: 8px;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
}
.maker {
  display: flex; flex-direction: column; gap: 3px; align-items: flex-start;
  background: var(--raised); border: 1px solid var(--hair); border-radius: 4px;
  padding: 12px 12px 11px; cursor: pointer; text-align: left; color: inherit;
  font: inherit;
}
.maker:hover { border-color: var(--amber-dim); background: #1a2836; }
.maker-name {
  font-family: "Barlow Condensed", sans-serif; font-size: 16px; font-weight: 600;
}
.maker-meta { font-size: 12px; color: var(--muted); }

.model-row {
  display: flex; align-items: center; gap: 12px; width: 100%;
  background: var(--raised); border: 1px solid var(--hair); border-radius: 4px;
  padding: 8px 12px; margin-bottom: 7px; cursor: pointer; color: inherit;
  font: inherit; text-align: left;
}
.model-row:hover { border-color: var(--amber-dim); }
.model-row img { width: 88px; height: 34px; object-fit: contain; flex: 0 0 auto; }
.model-main { display: flex; flex-direction: column; gap: 2px; flex: 1; min-width: 0; }
.model-main b { font-family: "Barlow Condensed", sans-serif; font-size: 15px; }
.model-main .muted { font-size: 12px; }
.model-price { font-variant-numeric: tabular-nums; font-size: 13.5px; }

/* The specification, in the two-column grid the genre uses. */
.spec-grid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: 2px 18px; margin: 10px 0;
}
.spec {
  display: flex; justify-content: space-between; gap: 10px;
  padding: 4px 0; border-bottom: 1px solid var(--hair-soft); font-size: 13.5px;
}
.spec-k { color: var(--muted); }
.spec-v { font-variant-numeric: tabular-nums; }

.blueprint {
  display: block; width: 100%; max-height: 260px; object-fit: contain;
  margin: 6px 0 4px;
}

.maker.is-legacy .maker-name, .model-row.is-legacy .model-main b { color: var(--muted); }
.maker.is-legacy, .model-row.is-legacy { border-style: dashed; }

/* ------------------------------------------------------- panel chrome -- */
.panel-bar {
  align-items: center; gap: 10px; padding: 8px 10px 8px 14px;
  border-bottom: 1px solid var(--hair); flex: 0 0 auto;
  background: var(--raised); border-radius: 4px 4px 0 0;
}
.panel-title {
  font-family: "Barlow Condensed", sans-serif; font-size: 15px; font-weight: 600;
  letter-spacing: .06em; text-transform: uppercase; color: var(--muted);
}
.panel-close {
  margin-left: auto; background: none; border: 0; cursor: pointer;
  color: var(--muted); font-size: 15px; line-height: 1; padding: 4px 6px;
  border-radius: 3px;
}
.panel-close:hover { background: var(--hair); color: var(--text); }

/* ------------------------------------------------- fleet state filter -- */
.state-tabs {
  display: flex; flex-wrap: wrap; gap: 4px; margin: 4px 0 10px;
}
.state-tab {
  display: flex; align-items: center; gap: 5px;
  background: var(--raised); border: 1px solid var(--hair); border-radius: 3px;
  color: var(--muted); font: inherit; font-size: 12.5px; padding: 4px 8px;
  cursor: pointer;
}
.state-tab span {
  font-variant-numeric: tabular-nums; font-size: 11px; color: var(--faint);
  background: var(--panel); border-radius: 8px; padding: 0 5px;
}
.state-tab.is-on { color: var(--text); border-color: var(--amber-dim); }
.state-tab.is-on span { color: var(--amber); }

/* --------------------------------------------------- demand by cabin --- */
.demand-row { display: flex; gap: 8px; margin-bottom: 12px; }
.demand-cell {
  flex: 1; text-align: center; background: var(--panel);
  border: 1px solid var(--hair-soft); border-radius: 3px; padding: 7px 4px;
}
.demand-cell b { display: block; font-size: 16px; font-variant-numeric: tabular-nums; }
.demand-cell span { font-size: 11px; color: var(--muted); }
.demand-icon {
  width: 16px; height: 16px; margin: 0 auto 3px; border-radius: 2px;
  background: var(--hair);
}
/* Seat-shaped blocks: economy narrow, business wider, first widest, cargo a
   flat box. Enough to tell the rows apart at a glance without an icon font. */
.demand-y { width: 9px; background: #4ea3dd; }
.demand-j { width: 13px; background: #8fb6d4; }
.demand-f { width: 16px; background: var(--amber); }
.demand-cargo { width: 18px; height: 11px; margin-top: 3px; background: #7b8b9b; }

/* ---------------------------------------------------------------- goals -- */
.goal {
  border: 1px solid var(--hair); border-radius: 4px; padding: 11px 12px;
  margin-bottom: 8px; background: var(--raised);
}
.goal-head {
  display: flex; align-items: baseline; gap: 10px; margin-bottom: 6px;
}
.goal-head b { font-family: "Barlow Condensed", sans-serif; font-size: 15.5px; }
.goal-head span { margin-left: auto; font-size: 12.5px; white-space: nowrap; }
.goal-foot { font-size: 12.5px; margin-top: 4px; }
.goal .sub { margin: 0 0 8px; }

/* The board's target is the one that is not optional, so it looks it. */
.goal-board { border-left: 3px solid var(--amber); }
.goal-offer { border-style: dashed; }
.goal.is-urgent { border-color: rgba(239, 95, 107, .5); }
.goal.is-urgent .goal-head b { color: var(--loss); }
.goal.is-done { opacity: .55; }

/* ------------------------------------------------- end of a playthrough -- */
.final-wrap {
  position: fixed; inset: 0; z-index: 2000;
  background: rgba(4, 8, 12, .82);
  display: flex; align-items: center; justify-content: center; padding: 24px;
}
.final-card {
  background: var(--panel); border: 1px solid var(--hair); border-radius: 6px;
  padding: 26px 28px; width: min(440px, 100%);
  box-shadow: 0 24px 60px rgba(0, 0, 0, .6);
  /* A report has to fit on the screen to be a report. Eight months away
     settles enough to run off the bottom of it, taking the "Carry on"
     button with it — which leaves the game behind a card that cannot be
     dismissed. */
  max-height: calc(100vh - 48px); overflow-y: auto;
}
.final-card h2 { margin: 0 0 4px; }

/* ------------------------------------------------------- the work queue -- */
.needs { display: flex; flex-direction: column; gap: 5px; margin-bottom: 4px; }
.need {
  display: flex; gap: 9px; align-items: flex-start; text-align: left;
  background: var(--raised); border: 1px solid var(--hair); border-radius: 3px;
  border-left-width: 3px; padding: 8px 10px; cursor: pointer;
  color: var(--text); font: inherit; font-size: 13px; line-height: 1.45;
}
.need:hover { border-color: var(--amber-dim); }
.need-dot {
  width: 7px; height: 7px; border-radius: 50%; margin-top: 5px;
  flex: 0 0 auto; background: var(--muted);
}
/* Severity 0 is money running out; 4 is merely worth knowing. */
.need-0 { border-left-color: var(--loss); }
.need-0 .need-dot { background: var(--loss); }
.need-1 { border-left-color: #e07a3c; }
.need-1 .need-dot { background: #e07a3c; }
.need-2 { border-left-color: var(--amber); }
.need-2 .need-dot { background: var(--amber); }
.need-3 { border-left-color: #4ea3dd; }
.need-3 .need-dot { background: #4ea3dd; }
.need-4 { border-left-color: var(--hair); }

.away-list { margin: 4px 0 0; padding-left: 18px; font-size: 13px; line-height: 1.6; }
.away-list li.ev-bad, .away-list li.loss { color: var(--loss); }
.away-list li.ev-good, .away-list li.gain { color: var(--gain); }
.away-list li.ev-warn { color: var(--amber); }

.boot-item.is-past { cursor: default; opacity: .8; gap: 10px; }
.boot-item.is-past:hover { border-color: var(--hair); }
.boot-item.is-past small { margin-left: auto; }
/* Recessive until wanted: taking a run off the record is a thing you go
   looking for, not a thing that should sit there inviting a mis-click
   beside the figures it would delete. */
.boot-forget {
  flex: none; background: none; border: 1px solid transparent;
  border-radius: 3px; padding: 2px 8px; font: inherit; font-size: 12px;
  color: var(--faint); cursor: pointer;
}
.boot-item.is-past:hover .boot-forget { color: var(--muted); }
.boot-forget:hover { color: var(--loss); border-color: var(--loss); }
.boot-forget:disabled { opacity: .4; cursor: default; }

/* ------------------------------------------------------- player code -- */
.recovery { margin-top: 14px; }
.recovery > summary {
  cursor: pointer; font-size: 12.5px; color: var(--faint);
  list-style: none; padding: 4px 0;
}
.recovery > summary::-webkit-details-marker { display: none; }
.recovery > summary:hover { color: var(--muted); }
.recovery input { font-family: ui-monospace, monospace; font-size: 12px; }
.boot-item small.claim { color: var(--amber); font-size: 11px; }

/* The away report's cash bridge: every line between what you left with and
   what you have now, so the trading result and the balance visibly reconcile
   instead of looking like two numbers that disagree. */
.kv.recon dd { font-variant-numeric: tabular-nums; }
.kv.recon .recon-total {
  border-top: 1px solid var(--hair);
  padding-top: 6px;
  margin-top: 2px;
}

/* ------------------------------------------------ the flight in focus -- */
/* Deliberately the loudest thing on the Track panel: opening a route is
   asking "what is happening on it", and this answers it before any of the
   numbers below do. */
.flight-hero {
  border: 1px solid var(--hair); border-radius: 8px; background: var(--raised);
  padding: 12px 14px; margin-bottom: 14px;
}
.fh-no {
  font-family: "Barlow Condensed", sans-serif;
  font-size: 25px; font-weight: 600; line-height: 1.05;
}
.fh-sub { color: var(--faint); font-size: 11.5px; margin-top: 2px; }
.fh-status { margin-top: 6px; font-size: 12.5px; font-weight: 600;
  letter-spacing: .04em; }
.fh-live { color: var(--gain); }
.fh-soon { color: var(--amber); }
.fh-sched { color: var(--taxi); }
.fh-done { color: var(--muted); }

.fh-ends { display: flex; gap: 14px; margin-top: 14px; }
.fh-end { flex: 1 1 0; min-width: 0; }
.fh-right { text-align: right; }
.fh-code {
  font-family: "Barlow Condensed", sans-serif; font-size: 34px;
  line-height: .95; color: var(--text);
}
.fh-city {
  font-size: 12.5px; font-weight: 600; margin-top: 1px;
  text-transform: uppercase;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.fh-lbl { color: var(--faint); font-size: 11px; margin-top: 7px; }
.fh-field {
  font-size: 12px; color: var(--taxi);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.fh-date { color: var(--muted); font-size: 11px; margin-top: 7px;
  letter-spacing: .03em; text-transform: uppercase; }
.fh-time { font-size: 16px; font-variant-numeric: tabular-nums; }
.fh-zone { color: var(--muted); font-size: 11px; }

.fh-track { display: flex; align-items: center; gap: 6px; margin-top: 12px; }
.fh-dot {
  width: 9px; height: 9px; border-radius: 50%; background: var(--gain);
  flex: 0 0 auto;
}
.fh-rail {
  position: relative; flex: 1 1 auto; height: 2px; background: var(--hair);
  border-radius: 2px;
}
.fh-rail > i {
  position: absolute; inset: 0 auto 0 0; background: var(--gain);
  border-radius: 2px;
}
.fh-plane {
  position: absolute; top: 50%; transform: translate(-50%, -50%);
  font-size: 13px; color: var(--gain); line-height: 1;
}
.is-scheduled .fh-plane, .is-boarding .fh-plane { color: var(--faint); }
.fh-foot { text-align: center; color: var(--muted); font-size: 12px;
  margin-top: 7px; }

@media (max-width: 430px) {
  .fh-code { font-size: 27px; }
  .fh-field { font-size: 11px; }
}

/* ------------------------------------------------------------- accounts -- */
/* The account is offered, never imposed: guest play is the default and this
   sits folded away above it until someone wants it. */
.boot-account { margin-bottom: 14px; }
.acct-fold > summary { color: var(--taxi); }
.acct-line {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 8px 10px; border: 1px solid var(--hair); border-radius: 6px;
  background: var(--raised); font-size: 13px;
}
.auth { margin-top: 10px; }
.auth .field { display: block; margin-bottom: 8px; }
.auth .state-tabs { margin-bottom: 10px; }
.note {
  border: 1px solid var(--amber-dim); border-radius: 6px;
  background: rgba(230, 163, 60, .08);
  padding: 9px 11px; font-size: 13px; margin: 10px 0;
}
/* Deletion is not styled like the rest. It should not be reachable by
   momentum from the form above it. */
.danger-h { color: var(--loss); margin-top: 26px; }

/* ------------------------------------------------- the flight, clicked -- */
/* Over the map rather than in the panel: the aeroplane was on the map, and
   sending the player to a different tab to find out about it loses the thing
   they were looking at. */
.flight-pop {
  position: fixed; inset: 0; z-index: 1200;
  background: rgba(4, 8, 12, .55);
  display: flex; align-items: center; justify-content: center;
  padding: 18px;
}
.flight-pop-card {
  position: relative;
  width: min(560px, 100%); max-height: 90vh; overflow-y: auto;
  background: var(--panel); border: 1px solid var(--hair);
  border-radius: 10px; padding: 14px;
  box-shadow: 0 18px 48px rgba(0, 0, 0, .55);
}
.flight-pop-card .flight-hero { margin-bottom: 12px; }
.flight-pop-x {
  position: absolute; top: 8px; right: 10px; z-index: 1;
  background: none; border: 0; color: var(--muted);
  font-size: 20px; line-height: 1; cursor: pointer; padding: 2px 6px;
}
.flight-pop-x:hover { color: var(--text); }

/* Where the aeroplane is at this second, under the flight card. */
.fp-live { margin-bottom: 12px; }
.fp-live-row {
  display: flex; justify-content: space-between; align-items: baseline;
  gap: 10px; font-size: 13px; margin-bottom: 5px;
  font-variant-numeric: tabular-nums;
}
.fp-live-foot { font-size: 11px; margin-top: 4px; text-align: center; }

/* A schedule edit that has not been sent yet. The button changes rather than
   the field, because the button is the thing that has to be pressed and the
   field already shows what was typed. */
.rot-row .btn-primary { font-weight: 600; }

/* The search and collapse controls above a list of cards. */
.list-tools {
  display: flex; gap: 8px; align-items: center; margin: 0 0 8px;
}
.list-find { flex: 1 1 auto; }
.list-tools .btn { flex: 0 0 auto; white-space: nowrap; }
.list-count { margin: -4px 0 8px; }
/* Search inputs get a type-specific appearance that fights the dark theme. */
input[type=search] {
  width: 100%; background: #0a141c; color: var(--text);
  border: 1px solid var(--hair); border-radius: 3px; padding: 8px 10px;
  -webkit-appearance: none; appearance: none;
}
input[type=search]::-webkit-search-cancel-button { filter: invert(.6); }

/* Progress to the next level, beside it in the top bar. Dimmed, because the
   level is the number being read and the percentage is the context. */
.lvl-xp { color: var(--muted); font-size: 11px; margin-left: 3px; }

/* First steps, and the help topics. The list is a progress track rather than
   a checklist to tick by hand — every line is read from the game — so the
   done state is quiet and only the next step gets the explanation. */
.steps { margin: 8px 0 2px; padding-left: 20px; }
.steps li { margin: 3px 0; line-height: 1.5; }
.steps li.done { color: var(--muted, #7c8b99); }
.steps li.now { color: inherit; font-weight: 500; }
.steps li.now > span { color: var(--accent, #e6a33c); }
.steps a { color: var(--accent, #e6a33c); }

.help-body p:first-child { margin-top: 4px; }
.help-body ol, .help-body ul { margin: 6px 0; padding-left: 20px; }
.help-body li { margin: 3px 0; line-height: 1.55; }
.help-body a[data-topic] { color: var(--accent, #e6a33c); cursor: pointer; }

/* ---------------------------------------------------------- growth chart -- */
.seg { display: flex; gap: 4px; flex-wrap: wrap; margin-bottom: 8px; }
.seg .btn.is-on { border-color: var(--amber); color: var(--amber); }
.chart { position: relative; }
.growth-chart { width: 100%; height: auto; display: block; }
.growth-chart .chart-axis { font-size: 10px; fill: var(--muted); }
.growth-chart .chart-label { font-size: 10px; fill: var(--text); font-weight: 600; }
.chart-legend { display: flex; gap: 12px; flex-wrap: wrap; font-size: 12px;
  color: var(--muted); margin-bottom: 4px; }
.chart-legend i, .chart-tip i { display: inline-block; width: 12px; height: 3px;
  border-radius: 2px; margin-right: 5px; vertical-align: middle; }
.chart-tip { position: absolute; top: 4px; min-width: 120px; pointer-events: none;
  background: var(--raised); border: 1px solid var(--hair); border-radius: 3px;
  padding: 5px 8px; font-size: 12px; color: var(--text); }
.chart-tip div { display: flex; align-items: center; gap: 2px; }
.chart-tip span { margin-left: auto; padding-left: 10px; font-variant-numeric: tabular-nums; }
.chart-table summary { cursor: pointer; font-size: 12px; color: var(--muted); margin-top: 6px; }

/* -------------------------------------------------------------- journeys -- */
.jr-card .grid td { padding: 4px 6px; }
.jr-change td { color: var(--amber); font-size: 12px; padding-top: 2px; padding-bottom: 2px; }
#jr-status {
  background: var(--raised); border: 1px solid var(--hair); border-left: 3px solid var(--amber);
  border-radius: 3px; padding: 8px 10px; margin: 10px 0;
}
#jr-status .jr-now { margin: 6px 0 4px; }
.jr-steps { margin: 4px 0 0; padding-left: 18px; font-size: 12px; }
.jr-steps li { padding: 1px 0; color: var(--muted); }
.jr-steps li.done { text-decoration: line-through; }
.jr-steps li.now { color: var(--text); font-weight: 600; }
#jr-status .jr-only { display: flex; align-items: center; gap: 6px; font-size: 12px; color: var(--muted); margin-top: 4px; }

/* ------------------------------------------------------------ trip video -- */
.tv-card { width: min(600px, 100%); max-height: calc(100vh - 48px); overflow-y: auto; }
.tv-formats { display: flex; gap: 8px; flex-wrap: wrap; margin: 10px 0; }
.tv-format { flex: 1 1 140px; display: flex; gap: 8px; align-items: flex-start;
  border: 1px solid var(--hair); border-radius: 4px; padding: 8px; cursor: pointer; font-size: 13px; }
.tv-format:has(input:checked) { border-color: var(--amber); }
.tv-stage { background: var(--ink); border: 1px solid var(--hair); border-radius: 4px;
  display: flex; justify-content: center; align-items: center; min-height: 120px; }
.tv-stage canvas, .tv-stage video { display: block; max-width: 100%; max-height: 52vh; }
.tv-stage canvas:not([data-format]) { display: none; }
.tv-actions { margin-top: 10px; gap: 8px; flex-wrap: wrap; }
#tv-caption { width: 100%; resize: vertical; font: inherit; }

/* The one-click start offered inside a portal's frame (offerQuickStart). */
.boot-quick { margin: 4px 0 18px; }
.boot-quick .sub { margin: 8px 0 0; }

/* Inside a portal's frame (embed.js sets the class before anything paints):
   whatever the markup marks as not for a portal is not drawn at all. */
html.embedded [data-hide-embedded] { display: none !important; }
