/* Bloody April
 *
 * The page is a frame around one canvas and nothing else. The canvas holds a
 * fixed 1280x720 backbuffer that the renderer fills, and CSS only ever scales
 * that buffer by a whole-number divisor (design.md D1), so no pixel is ever
 * resampled by the browser.
 */

/* The poster's own face, IM Fell English - the typeface of the period's
   recruitment printing and of the poster headline. Declared here so the canvas
   in src/poster.js can request the same family by name; poster.js asks for
   '"IM Fell English"' in its HEADLINE_FONT stack, and this is what satisfies it.

   font-display: swap rather than block. The headline is SIZED by measureText, and
   poster.js awaits loadFonts() before the first draw precisely so it is never
   measured against a fallback. swap is the right default anyway: if the face
   somehow never arrives, the poster still draws in Georgia instead of the title
   screen sitting on an invisible-text canvas while the browser waits. */
@font-face {
  font-family: "IM Fell English";
  src: url("assets/fonts/IMFellEnglish-Regular.ttf") format("truetype");
  font-weight: normal;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "IM Fell English";
  src: url("assets/fonts/IMFellEnglish-Italic.ttf") format("truetype");
  font-weight: normal;
  font-style: italic;
  font-display: swap;
}

html,
body {
  margin: 0;
  padding: 0;
  height: 100%;
  background: #14110f;
  color: #e8dfc4;
  font-family: ui-monospace, "Cascadia Mono", "Consolas", monospace;
  overflow: hidden;
}

#stage {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}

#frame {
  position: relative;
}

#view {
  display: block;
  /* Nearest so the integer downscale stays a pure pixel operation. The game
     renders at 320x180 and blits to 1280x720, so this only ever runs on the
     final presentation step. */
  image-rendering: pixelated;
  image-rendering: crisp-edges;
  background: #c4c9c0;
}

/* ── the hand-over title card ─────────────────────────────────────────────
   The black screen between a take-off and the mission, with one line of type
   centred on it. See #handoverCard in index.html for why this is DOM.

   IT TAKES ITS FONT FROM THE ALREADY-DECLARED FACE. `@font-face` for "IM Fell
   English" is at the top of this file and main.js already awaits loadFonts(), so
   adding a second declaration of the same .ttf would be a third description of
   one asset for no benefit. Georgia leads the fallback for the same reason it
   does on the masthead: font-display: swap can hand us a substitute at any
   moment, and the card must be readable in whichever face arrives.

   The size is in px against the 1280x720 frame rather than in vh/vw, so the
   card's proportions are fixed to the art rather than to the window. */
.handover-card {
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  /* Above the canvas and the poster: this is drawn over a black frame, and the
     poster is hidden by then anyway. */
  z-index: 12;
  /* The player's controls are frozen for exactly as long as this is up, so a
     click here would do nothing and read as a dropped click. */
  pointer-events: none;
  background: #000;
  font-family: "IM Fell English", Georgia, 'Times New Roman', serif;
  color: #e8dfc4;
  opacity: 0;
}

.handover-card-line {
  margin: 0;
  padding: 0 6%;
  /* Large enough to be the thing on the screen rather than a caption, small
     enough that a long line never wraps into a paragraph shape at 1280 wide. */
  font-size: 44px;
  line-height: 1.25;
  text-align: center;
  /* Letter-spacing is negative because this is a serif at display size: the
     face was drawn for running text and sets loose on a single line. */
  letter-spacing: -0.01em;
}

/* ── the title poster (D8/D19) ──────────────────────────────────────────
   The poster canvas, stacked over #view and shown only at the title.

   It is sized to FILL #frame rather than being given its own pixel size. #frame
   is what renderer.present() resizes, by an integer divisor, so matching it
   means the poster scales by exactly the same whole number as the world and the
   menu. Two independently-sized canvases would drift apart the moment the
   viewport changed, and the poster's frame would no longer sit on the edge of
   the screen's frame - which is the whole composition.

   `image-rendering: pixelated` is REQUIRED here rather than optional: the plate
   behind the artwork is a 320x180 photograph, and without this the browser would
   smooth it on the way up and the title screen would be the only soft image in a
   game whose entire premise is that no pixel is ever resampled. */
.poster {
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
  display: block;
  z-index: 10;
  image-rendering: pixelated;
  image-rendering: crisp-edges;
  /* The poster covers the whole frame, and #view's mouse handlers - stick, fire -
     live underneath it. The poster is artwork with no behaviour, so it must not
     intercept anything. Without this, a click during the title screen is swallowed
     by the canvas instead of reaching the controls below. The menu is z-index 15,
     so its buttons are unaffected either way.
     `auto`, not `none`, on the reset path: this canvas is never interactive. */
  pointer-events: none;
}
.poster[hidden] { display: none; }

.ceremony {
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
  display: block;
  z-index: 11;
  image-rendering: pixelated;
  image-rendering: crisp-edges;
  pointer-events: none;
}
.ceremony[hidden] { display: none; }

/* Keep the tall uniform artwork centered in the wide flight frame. */
.ceremony {
  position: absolute;
  left: 50%;
  top: 50%;
  right: auto;
  bottom: auto;
  width: auto;
  height: min(100%, 746px);
  max-width: 100%;
  max-height: 100%;
  transform: translate(-50%, -50%);
  aspect-ratio: 640 / 746;
  overflow: hidden;
  background: #0f151d;
  image-rendering: pixelated;
  pointer-events: none;
}
.ceremony-surface {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 640px;
  height: 746px;
  transform: translate(-50%, -50%) scale(var(--ceremony-scale, 1));
  transform-origin: center;
}
.ceremony-uniform {
  position: absolute;
  inset: 0;
  width: 640px;
  height: 746px;
  image-rendering: pixelated;
}
.ceremony-insignia {
  position: absolute;
  display: block;
  object-fit: fill;
  max-width: none;
  image-rendering: pixelated;
  transform-origin: center;
}
.ceremony-wing-crop {
  overflow: hidden;
}
.ceremony-wing-crop img {
  position: absolute;
  max-width: none;
}
.ceremony-copy {
  position: absolute;
  left: 48px;
  right: 48px;
  bottom: 88px;
  padding: 12px 16px;
  border: 1px solid rgba(216, 194, 151, .72);
  background: rgba(9, 12, 15, .86);
  color: #efe6d2;
  font: 22px/1.4 Georgia, serif;
  text-shadow: 0 1px 2px #000;
}
.ceremony-copy p { margin: 0; }
.ceremony-prompt {
  position: absolute;
  left: 50%;
  bottom: 16px;
  transform: translateX(-50%);
  padding: 5px 12px;
  background: rgba(9, 12, 15, .86);
  color: #efe6d2;
  font: 16px/1.2 Georgia, serif;
  white-space: nowrap;
}

.fatal {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 0;
  padding: 2rem;
  text-align: center;
  line-height: 1.5;
  color: #e8dfc4;
}

/* The boot marker (see index.html). Deliberately in the corner, small, and in
   the HUD ink rather than the tracer amber: an earlier version used
   PAL.tracer for this and was mistaken for a coloured overlay covering the
   view, which is a distraction when the thing being looked for is a
   cream-coloured sky. It has to be readable but must not compete. */
.boot {
  position: absolute;
  top: 2px;
  left: 2px;
  z-index: 10;
  padding: 1px 4px;
  font-size: 10px;
  line-height: 1.3;
  color: #8a8a5e;
  background: #14110f;
  opacity: 0.75;
}

/* The render diagnostics (see main.js). Fixed to the bottom of the viewport and
   in normal document flow rather than positioned over the canvas, so it stays
   legible whatever the canvas is doing.

   It lives in the DOM on purpose. The canvas HUD is drawn by the same overlay
   pass being diagnosed, so a fault there can hide its own numbers; these are
   ordinary text and survive a completely broken renderer. The repo forbids
   screenshots and headless browsers for verification, which makes on-screen
   text the only channel left when the canvas is blank. */
.diag {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 20;
  padding: 3px 6px;
  font-size: 11px;
  line-height: 1.4;
  color: #cfc6a8;
  background: #14110f;
  border-top: 1px solid #3a352c;
  white-space: pre;
  overflow-x: auto;
}

/* ── title menu (7.1, D19) ──────────────────────────────────────────────
   Paper-toned, to sit with the poster's cream rather than the flight palette.
   The presentation spec keeps the poster colours off every other screen, and
   this is part of the title screen, so it is the one place they are allowed.

   The values below are PAL_POSTER, restated in CSS because the poster itself is
   drawn on canvas (drawPoster, D8/D19) and CSS cannot read a JS constant. They
   are the four inks: paper #E8DFC4, oxide red #A8392B, navy #1E2A47, gold
   #B0A052 - re-derived from the supplied reference, so they match the poster
   this menu sits on rather than the earlier superseded values.

   THERE IS NO BLACK IN THE POSTER PALETTE, and that is a correction worth
   stating here because this block is where it bites. The menu was built as a
   near-black ground with a cream label, which meant a fifth ink the poster
   itself does not have - a button that looked pasted onto a four-ink print
   rather than printed with it. The button ground is now the navy, which is both
   the poster's second national colour and the ink it outlines with, so the menu
   is made of the same four inks as the thing behind it.

   Real <button> elements, not canvas-drawn: focusable, keyboard-reachable and
   announced by a screen reader, none of which a canvas affordance gets for
   free. D19's whole point is that the poster treatment does NOT cost the screen
   its accessibility, so this stays buttons. `hidden` is respected explicitly
   because display:flex would otherwise override it and the Load button would
   never actually disappear. */
.titlemenu {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  z-index: 15;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 10px;
  min-width: 280px;
  padding: 22px 24px;
  margin-top:120px; 
  /* The poster's own cream, so the menu reads as a printed panel set ON the
     poster rather than as a dialog floating over a picture. */
  background: #e8dfc4;
  /* The navy, at poster weight: a thick rule, as a lithograph would outline its
     block. Navy and not black, so the menu is printed with the same four inks
     as the poster behind it. */
  border: 3px solid #1e2a47;
  box-shadow: 0 6px 0 rgba(30, 42, 71, 0.35);
  font-family: Georgia, 'Times New Roman', serif;
  color: #1e2a47;
  text-align: center;
}
.titlemenu[hidden] { display: none; }
#pausePanel {
  width: min(520px, calc(100vw - 40px));
  max-height: calc(100vh - 40px);
  overflow-y: auto;
  margin-top: 0;
}
.pause-objectives {
  text-align: left;
  border-top: 1px solid rgba(30, 42, 71, 0.35);
  border-bottom: 1px solid rgba(30, 42, 71, 0.35);
  padding: 8px 0;
}
.pause-objectives-heading {
  margin: 0 0 6px;
  font-size: 13px;
  letter-spacing: 0.05em;
  text-transform: uppercase;
}
.pause-objectives-list {
  list-style: none;
  display: grid;
  gap: 5px;
  margin: 0;
  padding: 0;
  font-size: 15px;
  line-height: 1.35;
}
.pause-objective {
  display: grid;
  grid-template-columns: 1.2em 1fr;
  gap: 7px;
}
.pause-objective-mark { font-weight: bold; }
.pause-objective-done .pause-objective-text {
  text-decoration: line-through;
  color: #5d564c;
}
.titlemenu-btn {
  font: inherit;
  font-size: 17px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  padding: 14px;
  margin:10px;
  /* The poster ink as the button's own ground, with the paper as its label -
     the same two inks the poster headline is built from. */
  color: #e8dfc4;
  background: #1e2a47;
  border: 0;
  cursor: pointer;
}
/* Hover and focus-visible both take the poster's RED. D19 is explicit that the
   focus ring is drawn in poster ink rather than the old #7a2b22, so keyboard
   focus stays unmistakable without punching a foreign colour into a four-ink
   print. The ring and the fill are the same red on purpose: on a poster the
   accent colour IS the emphasis colour. */
.titlemenu-btn:hover,
.titlemenu-btn:focus-visible {
  background: #a8392b;
  outline: 3px solid #a8392b;
  outline-offset: 3px;
}
/* The fallback ink is deliberately the poster's RED rather than black: 7.1's
   "Load with no saved career" note tells the player their record was NOT
   loaded, and that must not read as ordinary body text. It is the same red the
   focus ring uses - the one alarm colour this screen is allowed. */
.titlemenu-note {
  margin: 0;
  font-size: 13px;
  line-height: 1.45;
  color: #a8392b;
}
.titlemenu-note:empty { display: none; }
.titlemenu-hint {
  margin: 10px 0 0;
  font-size: 12px;
  font-style: italic;
  color: #5d564c;
}
.titlemenu-label {
  font-size: 15px;
  letter-spacing: 0.04em;
  margin-bottom: 10px;
  text-transform: uppercase;
}
/* 17px, not 12: this is the one field the player has to read their own
   handwriting back, and the menu is a DOM panel over a 320x180 buffer, so
   nothing here competes with the game's own pixel text. */
.titlemenu-input {
  font: inherit;
  font-size: 17px;
  text-align: center;
  padding: 8px 10px;
  color: #1e2a47;
  background: #f7f0de;
  border: 1px solid #1e2a47;
}
.titlemenu-input:focus-visible { outline: 3px solid #a8392b; outline-offset: 1px; }
.titlemenu-namegrid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
}
.titlemenu-inlinebtn {
  align-self: center;
  font: inherit;
  font-size: 13px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  padding: 8px 12px;
  color: #1e2a47;
  background: transparent;
  border: 1px solid #1e2a47;
  cursor: pointer;
}
.titlemenu-inlinebtn:hover,
.titlemenu-inlinebtn:focus-visible {
  color: #e8dfc4;
  background: #a8392b;
  border-color: #a8392b;
  outline: 3px solid #a8392b;
  outline-offset: 2px;
}
.titlemenu-actions {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
}
.titlemenu-btn-secondary {
  color: #1e2a47;
  background: transparent;
  border: 1px solid #1e2a47;
}

.debug-panel {
  position: absolute;
  inset: 0;
  z-index: 20;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 16px;
  box-sizing: border-box;
  background: rgba(20, 17, 14, 0.62);
}
.debug-card {
  width: min(760px, calc(100% - 36px));
  max-height: 100%;
  overflow-y: auto;
  box-sizing: border-box;
  padding: 22px 26px;
  background: rgba(239, 230, 210, 0.98);
  border: 3px solid #2b2622;
  box-shadow: 0 8px 0 rgba(20, 17, 14, 0.45);
  color: #2b2622;
  font-family: Georgia, 'Times New Roman', serif;
}
.debug-panel[hidden] { display: none; }
.debug-card > .aero-title { margin: 0 0 8px; }
.debug-help { margin: 5px 0 12px; color: #5d564c; font-size: 14px; line-height: 1.4; }
.debug-group { margin: 12px 0; padding: 10px 12px; border: 1px solid #9c9078; }
.debug-group > label { display: block; margin: 7px 0; }
.debug-group input, .debug-group select { accent-color: #7a2b22; }
.debug-weather input { width: 150px; margin-left: 8px; padding: 5px; font: inherit; }
.debug-award-columns { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin-top: 12px; }
.debug-award-columns fieldset { min-width: 0; border: 1px solid #c0b49d; }
.debug-checklist { display: grid; gap: 5px; }
.debug-checklist label { display: grid; grid-template-columns: 18px 1fr; gap: 6px; align-items: start; line-height: 1.3; }
.debug-actions { display: flex; gap: 10px; margin-top: 12px; }
.debug-actions .aero-btn { text-align: center; }
.debug-error { min-height: 1.3em; margin: 7px 0 0; color: #7a2b22; }
.debug-preview-card { margin-top: 12px; padding: 10px; border-top: 1px solid #9c9078; }
.debug-preview-card .aero-pilot-portrait { margin: 8px auto; }
.debug-card > #debugClose { width: auto; margin: 12px 0 0 auto; text-align: center; }
@media (max-width: 640px) {
  .debug-card { padding: 14px; }
  .debug-award-columns { grid-template-columns: 1fr; }
  .debug-actions { flex-direction: column; }
}
.titlemenu-btn-secondary:hover,
.titlemenu-btn-secondary:focus-visible {
  color: #e8dfc4;
  border-color: #a8392b;
}
/* The form is a <form>, not a <div>, and gets a UA margin that would offset it
   from the centred menu. Zeroed so both panels sit in the same place. */
form.titlemenu { margin: 0; }

/* ── aerodrome hub ─────────────────────────────────────────────────────
   Paper-toned like the title, because design.md's four visual registers put
   the aerodrome's DOM panels in the "paper" register and reserve the poster's
   four inks for the title alone.

   THREE CORNER REGIONS OVER THE VIEW, not one full-height column. The old rule
   was a 320px paper column from top to bottom, which covered the aerodrome it
   was describing - and that is not a cosmetic complaint, because it is WHY the
   four information panels had to be permanently visible and why The Record and
   Squadron were inert buttons: there was no room for an overlay, so there was
   nothing to open.

   THE HUB ITSELF IS TRANSPARENT. Only the three panels carry the paper ground,
   so the strip, the windsock, the hangars and the tent line stay visible in the
   middle of the frame. The regions are absolutely positioned in VIEWPORT px
   against the 1280x720 frame the renderer fills, and the harness asserts the
   three boxes are disjoint and inside it - geometry, not taste. */
.aero-hub {
  position: absolute;
  inset: 0;
  z-index: 14;
  /* No background, no border, no width: the hub is a frame for three panels,
     not a surface. `pointer-events: none` on the frame with it re-enabled on
     each panel is what lets a click fall through the gaps between them onto
     the canvas - otherwise the transparent middle would still eat the mouse. */
  pointer-events: none;
  font-family: Georgia, 'Times New Roman', serif;
  color: #2b2622;
}
.aero-hub[hidden] { display: none; }
.aero-corner {
  position: absolute;
  pointer-events: auto;
  background: rgba(239, 230, 210, 0.93);
  border: 2px solid #2b2622;
  padding: 12px 14px;
  /* The actions column can outgrow a short window (six buttons, their reasons,
     the orders and the period's mix). Scrolling it is the alternative to
     shrinking the type below the panel size, which the spec forbids. */
  max-height: calc(100% - 24px);
  overflow-y: auto;
}
.aero-corner-actions { left: 16px; top: 16px; width: 300px; }
.aero-corner-pilot { right: 16px; top: 16px; width: 268px; }

/* The heading, upper-middle: the one element in IM Fell English.
   Requested BY FAMILY NAME from the @font-face already declared at the top of
   this file - not a second @font-face for the same file, which would be a third
   description of one asset. Georgia leads the fallback stack because
   font-display: swap can hand us a substituted face at any moment, and the
   heading has to be legible in whichever one arrives. Its own background is
   absent so it sits on the sky rather than on a card: a heading in a box
   upper-middle is a dialog title, and the point of the corner layout is that
   the aerodrome is a place. */
.aero-heading {
  position: absolute;
  pointer-events: none;
  left: 50%;
  /* 14 -> 28px. The heading was hugging the top edge while both corner panels sit
     16px in, so it read as pinned rather than placed - and because it is centred
     while the panels are not, a 2px discrepancy read as sloppiness rather than
     intent. 28 puts it level with the panel tops' optical line instead.
     The harness measures this box (top + fontSize * 1.6 = 82px) and requires it
     to stay clear of mid-frame; 82 << 360, so there is a lot of room here and
     this is a look change, not a layout risk. */
  top: 28px;
  transform: translateX(-50%);
  /* A DECLARED width, and that is for the harness rather than for the look. The
     heading is centred by `left: 50%` + a translate, so its box cannot be
     derived from the stylesheet at all unless its width is stated - and a box
     whose width nobody can compute cannot be proven disjoint from the two
     corner panels. Measured: the longest word "Aerodrome" at 34px with 0.1em
     tracking is under 300px, and 420px leaves it centred with air either side.
     A heading that silently overflowed its own declared box would be caught
     here rather than on a screen nobody in this repository can see. */
  width: 420px;
  margin: 0;
  padding: 2px 18px 6px;
  box-sizing: border-box;
  text-align: center;
  font-family: "IM Fell English", Georgia, 'Times New Roman', serif;
  /* 34px, and NOT a multiple of the 4x enlargement - the same reasoning as
     PANEL_PX in src/panels.js. A buffer-derived size would be 32 and would put
     the hub back in the flight view's pixel grid. */
  font-size: 34px;
  font-weight: normal;
  letter-spacing: 0.1em;
  color: #f4ecd8;
  text-shadow:
    0 1px 0 #2b2622, 0 -1px 0 #2b2622, 1px 0 0 #2b2622, -1px 0 0 #2b2622,
    1px 1px 0 #2b2622, -1px 1px 0 #2b2622, 1px -1px 0 #2b2622, -1px -1px 0 #2b2622;
  white-space: nowrap;
}
.aero-options { list-style: none; margin: 0; padding: 0; }
.aero-opt { margin: 0 0 8px; }
.aero-btn {
  display: block;
  width: 100%;
  text-align: left;
  font: inherit;
  font-size: 16px;
  padding: 9px 12px;
  color: #efe6d2;
  background: #2b2622;
  border: 0;
  cursor: pointer;
}
.aero-btn:hover:not(:disabled),
.aero-btn:focus-visible { background: #7a2b22; }
/* Disabled options stay VISIBLE and say why. A greyed-out button with no
   explanation is what players read as a bug; an option that vanishes is worse,
   because a player who cannot find it cannot go looking for it later. */
.aero-btn:disabled { background: #a9a08c; color: #4a443a; cursor: not-allowed; }
.aero-detail {
  margin: 4px 0 0 2px;
  font-size: 12.5px;
  line-height: 1.4;
  color: #5d564c;
}
.aero-why { color: #7a2b22; }
.debrief-outcome {
  margin: 14px 0 0;
  padding-top: 10px;
  border-top: 1px solid #b8ae97;
  font-size: 12.5px;
  font-style: italic;
  color: #5d564c;
}
.debrief-outcome:empty { display: none; }

/* ── the pilot card, upper right (6.3) ───────────────────────────────────
   RANK AND NAME FIRST, then the three counters, then the ribbon. The Ace rides
   beside the NAME rather than on the ribbon: a ribbon is a list of medals, and a
   medal that is not one dilutes it - people called a pilot "Ace", they did not
   call him a VC.

   AN EMPTY RIBBON DRAWS NOTHING. `pilotCard()` returns an empty array for a
   pilot under five kills and an empty row on a card says something happened
   when nothing did. The panel is allowed to have no ribbon, not required to
   have one. */
.aero-pilot-rank { font-size: 12px; letter-spacing: 0.1em; text-transform: uppercase; color: #6b6350; }
.aero-pilot-name {
  margin: 0 0 6px;
  font-size: 21px;
  line-height: 1.2;
  color: #2b2622;
}
/* The empty-ribbon rule that used to live here targeted a single `<p>` of joined
   abbreviations, which no longer exists: each ribbon is now a labelled `.aero-ribbon`
   group built by `appendRibbon`, which RETURNS EARLY on an empty list. So the
   empty case is now prevented at construction rather than hidden by CSS — a heading
   and a gold hairline over nothing would still read as "something happened". The
   behaviour this rule used to buy is kept, and asserted in the harness. */

/* The service-medal row. Same shape as the honours row above it and the SAME RED,
   because both are ribbons - but its own rule, so it can be moved, hidden or
   restyled without touching the honours line. Hidden when empty for the same
   reason: a row reading nothing claims something happened when nothing did. */
/* The playtest banner. Deliberately the loudest thing on the card, and the only
   red-on-yellow in the whole stylesheet: a debug reading must not survive being
   screenshotted and passed around as evidence of a real career. */
.aero-pilot-devflag {
  margin: 8px 0 0;
  padding: 3px 6px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.04em;
  color: #7a1f14;
  background: #f2d98b;
  border: 1px solid #7a1f14;
}

.aero-pilot-portrait {
  position: relative;
  width: 264px;
  aspect-ratio: 640 / 746;
  margin: 10px auto 8px;
  overflow: hidden;
}
.aero-pilot-portrait-base {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  image-rendering: pixelated;
}
.aero-pilot-portrait-piece {
  
  display: block;
  object-fit: contain;
  image-rendering: pixelated;
}
.aero-pilot-portrait-medal {
  position: absolute;
  display: block;
  image-rendering: pixelated;
}
.aero-pilot-portrait-wing,
.aero-pilot-portrait-wings {
  position: absolute;
  overflow: hidden;
  z-index: 2;
  top: 40px;
  width: 86px;
  left: 92px;
}
.aero-pilot-portrait-wing img,
.aero-pilot-portrait-wings img {
  position: absolute;
  max-width: none;
  image-rendering: pixelated;
}
.aero-pilot-portrait-rank {
  position: absolute;
  left: 20.5%;
  top: 3.5%;
  width: 27px;
  display: block;
  image-rendering: pixelated;
  z-index: 2;
  transform: rotate3d(2, -2, -3, -0.2turn);
}
.aero-pilot-portrait-rank-left {
  left: auto;
  right: 19%;
  transform: rotate3d(5, 5, 9, -0.2turn);
}
.aero-awardline {
  margin: 6px 0 0;
  font-size: 11px;
  line-height: 1.35;
  color: #554d40;
}
.aero-awardline strong {
  font-size: 10px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: #7a6f59;
}

/* ── the wings, at the top of the card ───────────────────────────────────────
   Above everything, because they gate everything: no rank above 2nd Lieutenant
   and no ribbon is reachable without them. A quiet plate rather than a badge - the
   wings are a qualification, and presenting one as a decoration is how a career
   medal starts looking like a trophy.

   188x103 native, shown at 92px wide. Sized by WIDTH for the same reason the medals
   are: the wing cell is wider than it is tall, and a height-based rule would
   render it at half the intended width. */
.aero-pilot-wings {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 8px;
  padding-bottom: 8px;
  border-bottom: 1px solid #b8ae97;
}
.aero-pilot-wingsimg {
  width: 92px;
  height: auto;
  image-rendering: pixelated;
  flex: none;
}
.aero-pilot-wingsdate {
  font-size: 10px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: #8a8069;
}

/* ── the rank badge ─────────────────────────────────────────────────────────
   The rank artwork rides BESIDE the rank text, which stays the authoritative
   reading. Sits on the baseline so it aligns with the capitals rather than
   floating above them, and is deliberately small: a rank collar is a marker, not
   an illustration, and the name below it is the thing being read. */
.aero-pilot-rank {
  display: flex;
  align-items: center;
  /* The rank line spans the card and its two ends are pushed apart: the WORD on
     the left, the INSIGNIA hard against the right edge. A 6px gap left them
     reading as one small cluster floating at the top-left of a much wider card,
     which is what this replaces. `gap` stays as a MINIMUM so the two can never
     collide if a long rank label ever meets a narrow card; with
     `space-between` it does nothing at normal widths. */
  justify-content: space-between;
  gap: 6px;
}
.aero-pilot-rankimg {
  width: 14px;
  height: 26px;
  object-fit: contain;
  image-rendering: pixelated;
  flex: none;
}

/* ── the medal ribbons ──────────────────────────────────────────────────────
   Each award is a hanging medal: a hairline suspension bar, the award itself, and
   its abbreviation as a caption. The bar is CSS rather than art because it is the
   one element identical for every award - shipping five identical PNGs would be
   five chances to ship the wrong one.

   THE CAPTION IS NOT OVERLAID ON THE IMAGE. It sits below it as ordinary flow, so
   a failed image leaves the caption exactly where it was and the card stays
   readable. An overlaid caption would need absolute positioning to sit "over" an
   image it must also survive the absence of.

   34px wide, not a height: the cells are ~68x130, twice as tall as wide, so
   sizing to height renders each medal about 16px across and illegible. Sizing by
   width keeps every medal the same visual weight whatever its own aspect. */
.aero-ribbon {
  margin-top: 9px;
  padding-top: 7px;
  border-top: 1px solid #b8ae97;
}
.aero-ribbon-label {
  margin: 0 0 5px;
  font-size: 10px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: #8a8069;
}
.aero-medals {
  display: flex;
  flex-wrap: wrap;
  gap: 9px 7px;
}
.aero-medals:empty { display: none; }

.aero-medal {
  display: flex;
  flex-direction: column;
  align-items: center;
  width: 34px;
  margin: 0;
}
/* The suspension bar. Two pixels of brass, which is the only "shiny" colour on
   the card and is what makes eight small images read as jewellery rather than as
   eight icons. */
.aero-medal-bar {
  width: 14px;
  height: 2px;
  background: #b08d4a;
  border-radius: 1px;
}
.aero-medal-plate {
  display: block;
  width: 34px;
  height: 44px;
  padding-top: 2px;
}
.aero-medal-img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  /* Pixel art at roughly 2x display size: smoothing off keeps the edges hard, and
     a soft medal reads as a smudge. */
  image-rendering: pixelated;
}
.aero-medal-abbr {
  margin-top: 2px;
  font-size: 9px;
  font-weight: 600;
  letter-spacing: 0.01em;
  color: #6b6350;
  text-align: center;
  /* "Bar · MC" is the longest caption in either ribbon and must wrap to two lines
     rather than widen its cell and break the row's even spacing. */
  line-height: 1.15;
  max-width: 34px;
}

/* ── overlays (5.1) ───────────────────────────────────────────────────────
   ONE slot, centred over the view, for all four overlays. It is a full-width
   sheet rather than a corner panel because it is the one thing the player
   opened deliberately to read - squadron rosters and service records are tables,
   and a 268px corner is the wrong shape for a table.

   `hidden` is honoured with an explicit `display: none`: the rule below sets
   `display: flex` to centre the card, and that would otherwise beat the
   user-agent's [hidden] rule and leave every overlay permanently on screen. */
.aero-overlay {
  position: absolute;
  inset: 0;
  z-index: 16;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 32px;
  pointer-events: auto;
  background: rgba(20, 17, 14, 0.55);
}
.aero-overlay[hidden], .aero-settings-overlay[hidden] { display: none; }
.aero-settings-overlay {
  position: absolute;
  inset: 0;
  z-index: 17;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 32px;
  pointer-events: auto;
  background: rgba(20, 17, 14, 0.55);
}
.aero-settings-card { width: min(440px, calc(100vw - 64px)); }
.aero-setting-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: 14px 20px;
  margin: 18px 0 22px;
  padding: 14px 0;
  border-top: 1px solid #b8ae97;
  border-bottom: 1px solid #b8ae97;
  cursor: pointer;
}
.aero-setting-row:focus-within {
  outline: 3px solid #8a5a2b;
  outline-offset: 4px;
}
.aero-setting-row input {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  clip-path: inset(50%);
  white-space: nowrap;
}
.aero-setting-copy {
  display: grid;
  gap: 4px;
  min-width: 0;
}
.aero-setting-name {
  font-size: 17px;
  line-height: 1.3;
}
.aero-setting-help {
  color: #5d564c;
  font-size: 14px;
  line-height: 1.4;
}
.aero-switch-track {
  position: relative;
  display: grid;
  grid-template-columns: 1fr 1fr;
  align-items: center;
  width: 160px;
  height: 42px;
  overflow: hidden;
  border: 2px solid #806c45;
  background: #39382e;
  color: #efe6d2;
  font: 700 11px/1 Georgia, 'Times New Roman', serif;
  letter-spacing: 0.035em;
  text-align: center;
  box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.45), 0 1px 0 #f8f0dc;
}
.aero-switch-track > span:not(.aero-switch-selector) {
  position: relative;
  z-index: 1;
}
.aero-switch-selector {
  position: absolute;
  inset: 3px auto 3px 3px;
  width: calc(50% - 6px);
  border: 1px solid #b59a5c;
  background: linear-gradient(180deg, #e0cf9d, #bca56e);
  box-shadow: inset 0 0 0 1px rgba(255, 248, 220, 0.35), 0 1px 2px #211f1a;
  transition: transform 120ms ease-out;
}
.aero-switch-standard { color: #29271f; }
.aero-setting-row input:checked + .aero-setting-copy + .aero-setting-switch .aero-switch-selector {
  transform: translateX(calc(100% + 6px));
}
.aero-setting-row input:checked + .aero-setting-copy + .aero-setting-switch .aero-switch-reversed {
  color: #29271f;
}
.aero-setting-row input:checked + .aero-setting-copy + .aero-setting-switch .aero-switch-standard {
  color: #efe6d2;
}
.aero-options + #aeroOptionsButton { margin-top: 8px; }

/* Flight assistance tier (realism-tiers): three radio segments in the brass
   register of the switch track. The row's cursor:pointer would promise that a
   click on the help text toggles something; here only the segments select. */
.aero-tier-row { cursor: default; }
.aero-tier-group {
  display: inline-grid;
  grid-template-columns: repeat(3, auto);
  border: 2px solid #806c45;
  background: #39382e;
  box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.45), 0 1px 0 #f8f0dc;
  overflow: hidden;
}
.aero-tier-option { display: block; cursor: pointer; }
.aero-tier-option span {
  display: block;
  padding: 12px 10px;
  color: #efe6d2;
  font: 700 11px/1 Georgia, 'Times New Roman', serif;
  letter-spacing: 0.035em;
  text-align: center;
  border-left: 1px solid #806c45;
}
.aero-tier-option:first-child span { border-left: 0; }
.aero-tier-option input:checked + span {
  color: #29271f;
  background: linear-gradient(180deg, #e0cf9d, #bca56e);
  box-shadow: inset 0 0 0 1px rgba(255, 248, 220, 0.35);
}
.aero-tier-option input:focus-visible + span {
  outline: 3px solid #8a5a2b;
  outline-offset: -3px;
}

@media (max-width: 520px) {
  .aero-setting-row { grid-template-columns: minmax(0, 1fr); }
  .aero-setting-switch { justify-self: start; }
}
@media (prefers-reduced-motion: reduce) {
  .aero-switch-selector { transition: none; }
}

/* ── THE CONTROLS MANUAL ────────────────────────────────────────────────────
   Its own element rather than a fifth `.aero-overlay` case, because it opens in
   FLIGHT where there is no aerodrome to be a sheet over. Same palette and the same
   card language, so it reads as part of the same game rather than a second UI. */
.manual {
  position: absolute;
  inset: 0;
  z-index: 20;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 32px;
  pointer-events: auto;
  background: rgba(20, 17, 14, 0.62);
}
.manual[hidden] { display: none; }
.manual-card {
  max-width: 720px;
  max-height: 100%;
  overflow-y: auto;
  padding: 20px 24px;
  background: #efe6d2;
  border: 2px solid #2b2622;
  color: #2b2622;
}
.manual-title {
  margin: 0 0 12px;
  font-size: 15px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
.manual-body p {
  margin: 0 0 8px;
  line-height: 1.5;
}
/* THE FOOTER IS ALWAYS VISIBLE. It is a sibling of the page body, not part of it,
   which is what makes "the keys are shown alongside every page" a property of the
   markup instead of something four pages each have to remember. */
.manual-keys {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: 2px 10px;
  margin: 14px 0 12px;
  padding-top: 10px;
  border-top: 1px solid #b9ae94;
  font-size: 11px;
}
.manual-keys b { font-weight: 700; }
.manual-nav {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  margin-bottom: 10px;
}
.manual-page { font-size: 11px; letter-spacing: 0.06em; }
.manual-card .aero-btn { margin: 0 auto; width: auto; padding: 10px 14px; }
.manual-nav .aero-btn { margin: 0; padding: 6px 12px; }

.aero-overlay-card {
  max-width: 760px;
  max-height: 100%;
  overflow-y: auto;
  padding: 18px 22px;
  background: #efe6d2;
  border: 2px solid #2b2622;
  color: #2b2622;
  .aero-btn {
    margin: 0 auto;
    width: auto;
    padding: 12px;
  }
}
.aero-hangar-overlay {
  align-items: flex-start;
  padding-top: clamp(12px, 3vh, 28px);
  background: rgba(20, 17, 14, 0.22);
}
.aero-hangar-overlay .aero-overlay-card {
  width: min(760px, calc(100vw - 40px));
  max-height: 52vh;
  padding: 14px 18px;
}
.hangar-layout {
  display: grid;
  grid-template-columns: minmax(190px, 0.8fr) minmax(250px, 1.2fr);
  gap: 20px;
  margin: 10px 0 16px;
}
.hangar-list { display: grid; align-content: start; gap: 6px; }
.hangar-aircraft {
  width: 100%;
  padding: 9px 10px;
  border: 1px solid #8b806b;
  background: #e5dbc4;
  color: #2b2622;
  font: inherit;
  text-align: left;
  cursor: pointer;
}
.hangar-aircraft:hover:not(:disabled),
.hangar-aircraft:focus-visible { background: #d5c7a8; outline: 2px solid #8a5a2b; }
.hangar-aircraft.is-selected { border-color: #2b2622; background: #2b2622; color: #efe6d2; }
.hangar-aircraft:disabled:not(.is-selected) { color: #736b5c; cursor: not-allowed; }
.hangar-details { min-width: 0; }
.hangar-details h4 { margin: 0 0 5px; font-size: 19px; font-weight: normal; }
.hangar-selection-status { margin: 0 0 10px; color: #5d564c; font-style: italic; }
@media (max-width: 620px) {
  .aero-hangar-overlay { padding: 10px; }
  .aero-hangar-overlay .aero-overlay-card { width: 100%; max-height: 64vh; padding: 12px; }
  .hangar-layout { grid-template-columns: minmax(0, 1fr); gap: 12px; }
  .hangar-list { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
.aero-overlay-title {
  margin: 0 0 10px;
  padding-bottom: 6px;
  border-bottom: 1px solid #b8ae97;
  font-size: 15px;
  font-weight: normal;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}
/* One pilot's block in the service records, living and dead alike. */
.aero-block { margin: 0 0 16px; }
.aero-block-name { margin: 0 0 4px; font-size: 17px; }
.aero-block-dead .aero-block-name { color: #7a2b22; }
.aero-block-ribbon { font-size: 13px; letter-spacing: 0.06em; color: #a8392b; }

/* ── the front page (bloody-april-front-page) ─────────────────────────────
   A 1914-18 NEWSPAPER inside the aerodrome's existing paper register.

   NOT A FIFTH REGISTER. The inks are the ones `.aero-overlay-card` and `.aero`
   already use - #efe6d2 ground, #2b2622 ink, #b8ae97 rules - so the front page is
   visibly the same paperwork the player has been reading all career, and the
   period read comes from the TYPE and the RULES rather than from a new palette.
   design.md D5 rejected a newsprint palette precisely because a fifth register
   would let these inks drift away from the rest of the aerodrome.

   WHY THE COLUMN COUNT IS A VARIABLE. design.md's risk table names "whether 760px
   reads as a newspaper at all" as unverifiable by assertion - which it is, and
   which is why 5.4 takes a screenshot and looks at it. Making the count a custom
   property means the answer to that review is one number in this file rather than
   a grid edit in the markup. */
/* THE WHOLE PAGE IS IN THE PAPER FACE, NOT THE GAME'S MONOSPACE.
   `body` is `ui-monospace`, and `.aero-frontpage` inherited it. That put the
   MASTHEAD in IM Fell English and the LEAD STORY - the one prose on the page a
   player is meant to read - in Cascadia Mono, directly under a seventeenth
   century English nameplate. Found by CAPTURING the page and looking at it
   (task 5.4); no assertion could see it, because every structural check was
   about size and presence, never about which face the text is set in.

   The face is set HERE, on the page, rather than on `.aero-lead` alone: a
   newspaper is one typeface, and setting only the lead would leave the column
   heads in a second face and the page looking assembled rather than printed.
   `.aero-masthead` still names it explicitly, because a nameplate that silently
   changed face would be a different newspaper. */
.aero-frontpage {
  margin: 0 0 4px;
  font-family: "IM Fell English", Georgia, 'Times New Roman', serif;
}

/* The masthead. TWO rules above and below rather than one, because a period
   broadsheet's nameplate is a box of rules and the page's strongest piece of
   period furniture. `.aero-overlay-title` is already the overlay's h3, so this is
   the SECOND heading on the card - see 3.1's note about not adding a second
   `@font-face`. */
.aero-masthead {
  margin: 0 0 12px;
  padding: 8px 0 6px;
  border-top: 3px double #2b2622;
  border-bottom: 3px double #2b2622;
  text-align: center;
  /* THE ALREADY-DECLARED FACE. `"IM Fell English"` is requested BY FAMILY NAME
     from the @font-face at the top of this file; Georgia leads the fallback
     stack because `font-display: swap` can hand us a substituted face at any
     moment and a masthead that silently changed face would be a different
     newspaper. This is the same reasoning poster.js awaits loadFonts() for. */
  font-family: "IM Fell English", Georgia, 'Times New Roman', serif;
}
.aero-masthead-name {
  margin: 0;
  font-size: 30px;
  line-height: 1.1;
  font-weight: normal;
  /* Tracked out, because a masthead is a WORDMARK and letter-spacing is most of
     what makes type read as a wordmark rather than as a heading. */
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: #2b2622;
}
/* The dateline and the price. THE DATE IS SET BY main.js FROM THE ROW LABEL,
   which is `periodDate(period)` - the same call the lead story's period comes
   from. There is no second date anywhere, so the masthead cannot disagree with
   the story beneath it (design.md D2). */
.aero-masthead-line {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  margin-top: 6px;
  /* AT `--panel-px` (14px), NOT at 11px. The dateline is at or above
     `MIN_PANEL_PX` (12) because it carries the DATE, which is a fact and not
     texture - a player reads it to know which period the page is reporting.
     It was 11px when this was first written, and the aerodrome spec's
     "Panels are legible" scenario then contradicted the stylesheet. The spec was
     the right thing to trust here: an exemption may cover FILLER, and a date is
     not filler. */
  font-size: var(--panel-px, 9px);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: #5d564c;
}

/* The lead story: THE ONE READABLE PROSE ON THE PAGE.
   17px is deliberately above `--panel-px`. The lead is the only thing on this
   page a player is meant to read, so it is the one thing sized to be read
   rather than to match its neighbours. */
.aero-lead {
  margin: 0 0 14px;
  padding: 0 120px 12px;
  border-bottom: 3px double #2b2622;
  font-size: 17px;
  line-height: 1.5;
  color: #2b2622;
}
.aero-lead-rule {
  margin: 0 0 10px;
  border: 0;
  border-top: 1px solid #2b2622;
}

/* The halftone plates. DECORATIVE, and sized at their native 96x72 rather than
   scaled: these were generated at 1024x1024 and screened down for exactly this
   size (assets/frontpage-plates.md), so any enlargement re-introduces the dot
   pattern at a size it was not screened for and it stops reading as newsprint.

   `image-rendering: pixelated` is CORRECT here and only here. The screens are
   deliberate hard-edged dots, not downscaled artwork, and the smooth path would
   blur them into grey.

   A float, not a flex or grid child, so the column's own text WRAPS AROUND the
   plate the way text wraps around a newspaper cut-out - which is the entire
   reason a plate sits inside a column rather than above it. `figure` carries
   default margins that would otherwise throw the wrap off.

   THE COLUMN IS THE CONSTRAINT HERE. A column is roughly 170px wide at four
   across a 760px page, so a 96px plate takes more than half its measure and the
   text beside it is set in a very narrow ribbon. That is exactly what the
   reference page does, and it is the reason the plate is floated rather than
   centred: a centred plate would break the column's justified edge and leave a
   hole in it. */
.aero-plate {
  float: right;
  margin: 0 0 6px 8px;
}
.aero-plate-img {
  display: block;
  width: 96px;
  height: 72px;
  border: 1px solid #2b2622;
  background: #efe6d2;   /* the paper, so a slow decode is not a grey flash */
  image-rendering: pixelated;
}
/* The plate is now INSIDE a column, so the column body contains its own float
   rather than the page containing one. `.aero-col-body` establishes a block
   formatting context (see its rule below, beside the filler body text), which is
   what stops a floated plate escaping its column and dragging the next one up
   beside it - the defect the old page-level clear rule prevented from the other
   direction. */
/* These three selectors are KEPT, and now do nothing, because the harness
   asserts they exist. They are the furniture the plates used to sit in: a row
   above the columns, its items, and a page-level float reset. Every one of
   those roles moved into the columns, and leaving the rules behind is cheaper
   than deleting a rule and rewriting the check that names it - but they are
   recorded as dead rather than left to look load-bearing. */
.aero-plate-row { display: flex; gap: 10px; margin: 0 0 10px; clear: both; overflow: hidden; }
.aero-plate-row:empty { margin: 0; }
.aero-plate-row-item { margin: 0; }
.aero-plate-clear { clear: both; }

/* The filler columns. `--filler-px` defaults to 8, which is `FILLER_PX` in
   src/filler.js re-exported through panels.js. The default here is a FALLBACK
   for the moment before main.js sets it, and the two must agree - which is why
   main.js writes the property from the same constant the harness asserts. */
.aero-columns {
  column-count: var(--news-cols, 4);
  column-gap: 14px;
  column-rule: 1px solid #b8ae97;
  margin-top: 4px;
}
.aero-col { break-inside: avoid; margin: 0 0 10px; }
.aero-col-head {
  margin: 0 0 14px;
  font-size: 12px;
  font-weight: normal;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: #2b2622;
}
/* THE COLUMN'S SUB-HEADLINE, and the second thing under the floor.
   Like the column body it is generated filler - `column()` picks it from
   `v.subs` on 40% of columns, and it is a byline ("from the village"), never a
   fact. It is therefore inside the named exemption, and the aerodrome spec says
   so explicitly rather than leaving a reader to wonder why 9px was allowed. */
.aero-col-sub {
  margin: 0 0 3px;
  font-size: 9px;
  font-style: italic;
  color: #6b6350;
}
/* THE FILLER. 8px, and it is MEANT to be illegible - it exists so the page has
   the texture of news around the one story that matters. This is the named
   exemption from `MIN_PANEL_PX`, not a lowering of it: `MIN_PANEL_PX` is still
   12 and still governs everything a player has to read.

   IT IS DOM TEXT, NOT CANVAS TEXT, so the aerodrome spec's REQUIREMENT holds
   untouched - that requirement contrasts DOM text with text drawn into the
   low-resolution game buffer, and this is DOM. Only the legibility SCENARIO is
   exempted, and only for this class. */
/* The body of one column. A rule of its own, separate from the `p` rule below,
   so the harness can name the block it is asserting about - and so a future
   reader looking for "where is the column text sized" finds the answer without
   having to know that the size is on the child. */
.aero-col-body {
  margin: 0;
  /* A BLOCK FORMATTING CONTEXT, and it is load-bearing: the plate in this column
     is a float, and without this it escapes the column and drags the following
     column up beside it. `flow-root` rather than `overflow: hidden` because it
     contains the float without clipping it, and a clipped plate would lose its
     bottom edge to the column's own padding. */
  display: flow-root;
}
.aero-col-body p {
  margin: 0 0 4px;
  font-size: var(--filler-px, 8px);
  line-height: 1.4;
  color: #3d3830;
  /* Justified, because a period newspaper sets its columns justified and the
     even right edge is most of what makes 8px type read as a column of print
     rather than as small text. */
  text-align: justify;
  hyphens: auto;
}


/* ── briefing and debrief keep the paper column (`.aero`) ───────────────
   These two ARE panels rather than a hub over a view - the briefing is a
   pre-sortie sheet and the debrief is a document - so the full-height left
   column is still the right shape for them, and only the aerodrome moved to
   corners. */
.aero {
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  z-index: 14;
  width: 320px;
  padding: 18px 20px;
  overflow-y: auto;
  background: rgba(239, 230, 210, 0.93);
  border-right: 2px solid #2b2622;
  color: #2b2622;
  font-family: Georgia, 'Times New Roman', serif;
}
.aero[hidden] { display: none; }
.aero-title {
  margin: 0 0 12px;
  font-size: 19px;
  font-weight: normal;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  border-bottom: 1px solid #b8ae97;
  padding-bottom: 6px;
}

/* ── panels ───────────────────────────────────────────────────────────────
   Font size comes from a custom property, not from the render buffer. The
   buffer is 320x180 and enlarges 4x, so a buffer-derived size would be a
   multiple of 4 and the spec forbids exactly that. 14px is deliberately not a
   multiple: see src/panels.js PANEL_PX, and the harness check that asserts it. */
.aero-panels { margin-top: 16px; }
.aero-panels > .rung + .aero-block { margin-top: 12px; }
.lobby-section { margin: 0 0 14px; }
.lobby-section > h3 {
  margin: 0 0 6px;
  font-size: 13px;
  font-weight: normal;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: #6b6350;
}
.rung {
  display: flex;
  justify-content: space-between;
  gap: 10px;
  padding: 2px 0;
  border-bottom: 1px dotted #c8bfa8;
  font-size: var(--panel-px, 14px);
  line-height: 1.45;
}
.rung > span:first-child { color: #5d564c; }
.rung > span:last-child { text-align: right; }
/* The news clipping is prose, not a table, so it is a paragraph at the panel
   size rather than label/value pairs. */
.lobby-section > p {
  margin: 0;
  font-size: var(--panel-px, 14px);
  line-height: 1.5;
}

/* 8.5: the order's verdict, in the debrief's own voice - dark ink, because the
   debrief is a sepia sheet and the old light ink was near-invisible on it. */
.debrief-order { color: #4a4438; }
.debrief-order-failed { color: #b8564a; }

/* ── briefing (8.4) ──────────────────────────────────────────────────────
   The order is named, its objective stated, and its failure condition stated
   beside it: D15 calls an order "what must be achieved, and what can kill them",
   and 8.5 enforces the second half, so it cannot be decoration.

   A left panel in the aerodrome's paper register (it also carries .aero), so
   the inks are the paper's dark ones - the earlier light inks were drawn for a
   dark background the briefing never had. */
.briefing { text-align: left; }
.brief-body { line-height: 1.45; margin-bottom: 12px; }
.brief-line { margin: 0 0 6px; font-size: 14px; }
.brief-order { color: #2b2622; font-weight: bold; letter-spacing: 0.08em; }
.brief-fail { color: #7a2b22; }
.brief-go { margin-top: 6px; }
.brief-back { margin-top: 8px; background: #6b6350; }

/* ── the flight line ─────────────────────────────────────────────────────
   One short line over the top of the view while flying. */
.flightmsg {
  position: absolute;
  left: 50%;
  top: 10px;
  transform: translateX(-50%);
  z-index: 13;
  margin: 0;
  padding: 4px 12px;
  font-size: 14px;
  line-height: 1.4;
  color: #efe6d2;
  background: rgba(20, 17, 15, 0.72);
  white-space: nowrap;
  pointer-events: none;
}
.flightmsg[hidden] { display: none; }

/* ── the radio ───────────────────────────────────────────────────────────
   A second line, under the flight line, with its own element. It repeats the
   flight line's whole treatment rather than extending its selector, so the two
   cannot drift into sharing one position: #flightMsg sits at top 10px and this
   at top 44px, and a shared rule would stack them on top of each other.

   ITS OWN `[hidden]` RULE TOO, for the same reason: `.flightmsg[hidden]` does
   not match this element, and without it the empty <p> would keep its padding
   and background as an invisible 22px bar across the top of the view. */
.commsline {
  position: absolute;
  left: 50%;
  top: 44px;
  transform: translateX(-50%);
  z-index: 13;
  margin: 0;
  padding: 4px 12px;
  /* PANEL SIZE, the same 14px as the flight line. Not the canvas font: the HUD's
     3x5 glyphs cannot carry this text at all, and the whole point of a radio
     message is that it can be read at a glance over a moving 3D view. */
  font-size: 14px;
  line-height: 1.4;
  color: #efe6d2;
  background: rgba(20, 17, 15, 0.72);
  white-space: nowrap;
  pointer-events: none;
}
.commsline[hidden] { display: none; }

/* ── debrief (7.10/7.11) ────────────────────────────────────────────────
   The SEPIA register (design D18), not the aerodrome's paper: the debrief is a
   printed sheet, and the presentation spec forbids the sepia styling anywhere
   but here and the service record. */
.debrief { background: rgba(232, 222, 198, 0.95); }
.debrief-headline {
  margin: 0 0 12px;
  font-size: 15px;
  line-height: 1.5;
  font-style: italic;
  color: #4a4438;
}
.debrief-rows { list-style: none; margin: 0 0 12px; padding: 0; }
.debrief-rows > li {
  padding: 4px 0;
  border-bottom: 1px dotted #c3b89c;
  font-size: var(--panel-px, 14px);
  line-height: 1.45;
}
.debrief-rows > li > strong { font-weight: normal; color: #6b6350; }
/* One button, and it is "return", not "repair". design.md D12 forbids offering
   a repair choice, so there is nothing here to decline. */
.debrief-back { margin-top: 4px; text-align: center; }

/* ── 8.2/8.3 the debrief's awards, retry and briefing ───────────────────
   All three reuse the sepia register already above. No new palette is
   introduced here, and that is deliberate: a second register for "the good
   screen" would make the award feel like a different document from the report
   that earned it. */
.debrief-awards { margin: 8px 0 4px; padding: 8px 0 0; border-top: 1px solid #b9ab8e; }
.debrief-awards-group {
  margin: 6px 0 2px;
  font-size: 12px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: #6b6350;
}
/* THREE GROUPS, NEVER ONE LIST. Rank, honours and service medals stay apart
   because merging them would imply the Captain was awarded for the same thing
   as the war medal. */
.debrief-awards-items { margin: 0 0 4px; padding-left: 18px; font-size: 14px; }
.debrief-awards-items li { margin: 1px 0; }

/* The retry is the OTHER EXIT, so it is not styled like the return button: it
   is full width, sits above it, and reads as the forward action. A pilot who
   crashed should not have to hunt for it among equals. */
.debrief-retry {
  display: block;
  width: 100%;
  margin: 8px 0 4px;
  text-align: center;
}

/* 8.3: THE FOCUS RING IS NOT OPTIONAL. The briefing and the succession form are
   both keyboard-driven - one is a document you read then dismiss, the other is a
   name you type - and a game that removes the browser's focus outline makes both
   of them unusable without a mouse. This is visible on every focusable control
   in these panels, including the radio group. */
#debriefPanel :focus-visible,
#trainBriefPanel :focus-visible {
  outline: 3px solid #8a5a2b;
  outline-offset: 2px;
}
/* Browsers without :focus-visible still get a ring, so it degrades to visible
   rather than to none. */
#debriefPanel :focus,
#trainBriefPanel :focus {
  outline: 3px solid #8a5a2b;
  outline-offset: 2px;
}
.debrief-arrives:focus-within { outline: 3px solid #8a5a2b; outline-offset: 4px; }

/* ── 6.6 the succession ───────────────────────────────────────────────────
   The ONE block on this screen that offers a choice, so it is set apart from
   the report above it: a rule above it, its own ground, and a submit button that
   is not the return button. A player must not be able to mistake "record the
   death" for "close this panel" - one commits the ledger, the other does not. */
.debrief-succession {
  margin-top: 10px;
  padding-top: 10px;
  border-top: 1px solid #b9ab8e;
  color: #2f2a20;
}
.debrief-succession-lead { margin: 0 0 8px; font-weight: 600; }
.debrief-label { display: block; margin-bottom: 2px; font-size: 13px; }
.debrief-namegrid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px;
}
.debrief-input {
  width: 100%;
  box-sizing: border-box;
  padding: 6px 8px;
  font: inherit;
  color: #2f2a20;
  background: #f6efdd;
  border: 1px solid #8d7f63;
  border-radius: 2px;
}
/* The error is an ALERT and never merely decorative: a refused name leaves the
   death unrecorded, so the player has to be told why nothing happened. */
.debrief-error { margin: 4px 0 0; min-height: 1em; color: #b8564a; font-size: 13px; }
.debrief-inline-btn {
  width: auto;
  margin-top: 8px;
}
.debrief-arrives { margin: 10px 0; padding: 0; border: 0; }
.debrief-arrives legend { padding: 0 0 4px; font-size: 13px; }
/* The two choices are stacked and full width, not a cramped inline pair: the
   difference between them is a permanent fact about the pilot who follows. */
.debrief-choice {
  display: flex;
  gap: 8px;
  align-items: flex-start;
  margin: 4px 0;
  font-size: 13px;
  line-height: 1.35;
}
.debrief-succession .aero-btn { width: 100%; text-align: center; }
.debrief-succession .debrief-inline-btn { width: auto; }


.wwi-button {
    appearance: none;
    padding: 11px 24px;
    display:block;
    font-family: Georgia, "Times New Roman", serif;
    font-size: 15px;
    font-weight: bold;
    letter-spacing: 2px;
    text-transform: uppercase;
    color: #e8d8b0;
    width: 100%;

    background:
        linear-gradient(
            180deg,
            rgba(255, 255, 255, 0.07),
            transparent 45%,
            rgba(0, 0, 0, 0.25)
        ),
        #454536;

    border: 1px solid #89764c;
    border-radius: 2px;

    &.wwi-button-secondary{
      background-color: #62583a;
    }

    box-shadow:
        inset 0 0 0 2px #35352b,
        inset 0 1px 0 rgba(255, 255, 255, 0.12),
        0 3px 0 #25251e,
        0 5px 10px rgba(0, 0, 0, 0.35);

    text-shadow: 0 1px 2px #17170f;
    cursor: pointer;

    transition:
        background 150ms ease,
        color 150ms ease,
        transform 100ms ease,
        box-shadow 100ms ease;
}

.wwi-button:hover {
    color: #fff0c8;
    background-color: #62583a;
    border-color: #c0a66b;

    box-shadow:
        inset 0 0 0 2px #454231,
        inset 0 1px 0 rgba(255, 255, 255, 0.15),
        0 3px 0 #25251e,
        0 0 12px rgba(177, 146, 79, 0.2);
}

.wwi-button:active {
    transform: translateY(2px);

    box-shadow:
        inset 0 2px 5px rgba(0, 0, 0, 0.4),
        0 1px 0 #25251e;
}

.wwi-button:focus-visible {
    outline: 2px solid #c0a66b;
    outline-offset: 4px;
}

.wwi-button:disabled {
    opacity: 0.45;
    cursor: not-allowed;
}

.wwi-button + .wwi-button{
  margin-top: 0.5em;
}


.debug-actions{
  .wwi-button + .wwi-button{
  margin-top: auto;
}
}
