/*
 * One rule for every icon there will ever be, so a drawing added to icons.mjs
 * needs nothing here to go with it.
 *
 * The size is in em and the colour is currentColor, which puts both in the hands
 * of whatever the icon sits in: a control sets its own font-size and colour, and
 * the glyph follows, including through a hover or a disabled state. The stroke
 * width is in the viewBox's own units, so it thickens with the drawing rather
 * than thinning out as the icon is set larger.
 */
.icon {
  width:1em;
  height:1em;

  fill:none;
  stroke:currentColor;
  stroke-width:2;
  stroke-linecap:round;
  stroke-linejoin:round;
}

/*
 * A shape that has to be read as a thing rather than as a line says so on
 * itself, and the two can be mixed inside one drawing.
 *
 * An aircraft is the case this exists for. Traced in line at this size it is
 * a fuselage two strokes wide with a gap down the middle, which is a paper
 * dart -- what makes it an aeroplane is the silhouette, so it is filled.
 */
.icon .solid {
  fill:currentColor;
  stroke:none;
}

/*
 * The widgets shared.mjs draws: the condition word and its bar, the rating
 * bars, the band colours behind both, and the aeroplane drawn as a way into
 * its own screen.
 *
 * Scoped to the game layout rather than to one screen's root id, which is the
 * exception CLAUDE.md allows: the same bar is read on the fleet hub, on both
 * aircraft lists and on an aircraft, so a copy under each of those roots would
 * be four places to change one band colour. This is the narrowest hand-written
 * id above all four.
 */

#layout_game-layout .cond_word {
  flex:none;
  font-variant-numeric:tabular-nums;
}

#layout_game-layout .cond_bar {
  height:0.4rem;
  border-radius:3px;
  background-color:var(--color-disabled);
  overflow:hidden;
  margin:0.25rem 0;
}

/* The width is the condition and is written on the element. Zero here because a
   block box with no width of its own fills its parent, so a fill that lost its
   width would read as a perfect aeroplane rather than as the bug it is. */
#layout_game-layout .cond_bar>span {
  display:block;
  width:0;
  height:100%;
  border-radius:3px;
  background-color:currentColor;
}

/* The band colours the word and the bar out of the one rule, so the two can
   never disagree about which band an airframe is in. */
#layout_game-layout .band_needs-work { color:var(--color-error); }
#layout_game-layout .band_tired { color:var(--color-warning); }
#layout_game-layout .band_well-kept { color:var(--color-text); }
#layout_game-layout .band_as-new { color:var(--color-success); }

/* Three bars rather than the personnel stripes' five, and otherwise the same
   drawing: a rating is 1-3 where a competence is 1-5. */
#layout_game-layout .bars {
  display:inline-flex;
  gap:0.125em;
  vertical-align:-0.05em;
  flex:none;
}

/* Sized in em so a bar stands as tall as the text it sits beside, whatever the
   line it is drawn on is set in. */
#layout_game-layout .bars i {
  display:block;
  width:0.25em;
  height:0.75em;
  border-radius:1px;
  background-color:rgba(221, 221, 221, 0.16);
}

#layout_game-layout .bars i.on {
  background-color:var(--color-primary-action);
}

#layout_game-layout .rating {
  display:inline-flex;
  align-items:center;
  gap:0.25rem;
  flex:none;
}

#layout_game-layout .rating>span {
  color:var(--color-disabled-text);
}

/* The crew line, drawn on the fleet row and in the head of an aircraft's own
   screen. The line is muted by whatever it sits in, so all this says is that
   the icon stays beside the counts and what colour a job that is not filled
   is drawn in. */
#layout_game-layout .crew_count {
  display:flex;
  align-items:center;
  gap:0.35rem;
  flex:none;

  color:var(--color-disabled-text);
  font-variant-numeric:tabular-nums;
}

/* A count and its requirement are one figure and never read across a line
   break. */
#layout_game-layout .crew_job {
  white-space:nowrap;
}

/* On the count alone, and red rather than the hub's amber: a job one person
   short grounds the aeroplane exactly as an empty one does, and amber would
   offer a middle the aeroplane does not have. The requirement after the slash
   stays as muted as the rest of the line. */
#layout_game-layout .crew_short {
  color:var(--color-error);
  font-weight:bold;
}

/* The aeroplane as a way into its own screen, set the way a tail number is set
   and underlined as the tap it is. A button so it answers the keyboard, drawn
   back down to the line of prose it sits in. */
#layout_game-layout .aircraft {
  padding:0;
  border:none;
  background:none;
  font:inherit;
  font-weight:bold;
  letter-spacing:0.08em;
  color:var(--color-text);
  text-decoration:underline;
  cursor:pointer;
}

/*
 * The walk sibling-nav.mjs draws, on whichever screen drew it.
 *
 * Scoped to the game layout rather than to one screen's root id, which is the
 * exception CLAUDE.md allows for markup a module several screens import emits:
 * the person screen and the airframe screen both draw this, so a copy under
 * each root would be two places to change one border.
 */

/* Three fixed columns rather than a flex row: an end of the list disables its
   button instead of dropping it, so the count would slide sideways on the first
   row and the last one. */
#layout_game-layout .sibling_nav {
  display:grid;
  grid-template-columns:auto 1fr auto;
  align-items:center;

  padding-top:0.6rem;
  border-top:1px solid var(--color-border-deep);
}

#layout_game-layout .sibling_nav .icon_button {
  font-size:1.25rem;
}

/* .icon_button:hover outweighs button:disabled on its own, which would light up
   an end of the list under the pointer as though it could still be pressed. */
#layout_game-layout .sibling_nav .icon_button:disabled:hover {
  color:var(--color-disabled-text);
}

#layout_game-layout .sibling_nav span {
  text-align:center;
  font-variant-numeric:tabular-nums;
  color:var(--color-disabled-text);
}

/*
 * The widgets shared.mjs draws, and the verdict a screen puts its own words in.
 *
 * Scoped to the game layout rather than to one screen's root id, which is the
 * exception CLAUDE.md allows: the same stripes and the same mood word are read
 * on the personnel hub, on both of its lists and on a person, so a copy under
 * each of those roots would be four places to change one colour. This is the
 * narrowest hand-written id above all four.
 */

#layout_game-layout .stripes {
  display:inline-flex;
  gap:2px;
  vertical-align:-1px;
}

#layout_game-layout .stripes i {
  display:block;
  width:4px;
  height:10px;
  border-radius:1px;
  background-color:rgba(221, 221, 221, 0.16);
}

#layout_game-layout .stripes i.on {
  background-color:var(--color-primary-action);
}

/* The break sits at Okay on purpose -- see shared.mjs and personnel_ui_combined.md §3. */
#layout_game-layout .mood--miserable,
#layout_game-layout .mood--unhappy {
  color:var(--color-error);
}

#layout_game-layout .mood--okay {
  color:var(--color-warning);
}

#layout_game-layout .verdict {
  padding:0 0.35rem;
  border-radius:3px;
  font-weight:bold;
  letter-spacing:0.04em;
  text-transform:uppercase;
}

#layout_game-layout .verdict--cheap {
  background-color:rgba(4, 155, 74, 0.2);
  color:#3fce7f;
}

#layout_game-layout .verdict--fair {
  background-color:rgba(139, 152, 162, 0.18);
  color:var(--color-text);
}

#layout_game-layout .verdict--steep {
  background-color:rgba(247, 148, 29, 0.16);
  color:var(--color-warning);
}

/* The advisor's read on the clock. The calm band says nothing the row does not
   already say, so it keeps the muted colour it inherits and only the two bands
   worth acting on carry one of their own. */
#layout_game-layout .pool_urgency--soon {
  color:var(--color-warning);
}

#layout_game-layout .pool_urgency--urgent {
  color:var(--color-error);
}

/*
 * The element is 1em square and the drawing fills it, the way .icon does: a crew
 * row sets 2.5rem and a person screen 3.6rem, and neither passes a pixel count.
 */
pt-face {
  display:inline-block;
  width:1em;
  height:1em;
  flex:none;
  line-height:0;
}

pt-face>.face {
  display:block;
  width:100%;
  height:100%;
}

/* The two placeholder drawings, and the disc every face sits on. Only these are
   themed -- skin, hair and outfit are the tables' own colours, since a portrait
   the palette could recolour would stop identifying anybody. */
pt-face {
  --face-ground:#0b2531;
  --face-figure:#33505e;
}

pt-face .face_ground {
  fill:var(--face-ground);
}

pt-face .face_figure {
  fill:var(--face-figure);
}

pt-face .face_empty {
  stroke:var(--color-disabled-border);
}

pt-rate {
  display:inline-block;
}

/* The amount is read as part of the line it sits in, and carries no mark of its
   own saying it can be clicked -- the cursor is the whole of the invitation, on
   purpose. Everything here is undoing what application.css gives a button. */
pt-rate button {
  font:inherit;
  color:inherit;
  padding:0;
  border:none;
  background:none;
  appearance:none;
  cursor:pointer;
}

pt-rate .unit {
  margin-left:0.25em;
  font-size:0.8em;
  font-weight:normal;
  color:var(--color-disabled-text);
}

pt-rate .unit:empty {
  display:none;
}

pt-runway {
  /* The tarmac's speed, and the scenery's, are one number: a stripe and its gap
     go by in --runway-roll, and everything standing beside the runway covers
     that same ground in that same time. Nothing on the ground can do otherwise
     -- a hangar going by at any other rate is a hangar sliding along the tarmac
     it is built on. Written as plain counts of rem because CSS will not divide
     a length by a length, and the timing below is exactly that division. */
  --runway-dash-rem:1.75; /* one centreline stripe and the gap that follows it */
  --runway-run-rem:60; /* the stretch of country that comes round again */
  --runway-roll:600ms; /* how long a stripe takes to travel its own run */

  --runway-dash:calc(var(--runway-dash-rem) * 1rem);
  --runway-run:calc(var(--runway-run-rem) * 1rem);
  /* Once round, taken at the centreline's own speed. Four times the width the
     panel gives the runway, so most of that stretch is out of frame and what
     is seen is a landmark now and then rather than all of them at once. */
  --runway-loop:calc(var(--runway-run-rem) / var(--runway-dash-rem) * var(--runway-roll));

  /* The room kept above the asphalt. Nothing below may stand taller than this,
     or it is cut off at the top. */
  --runway-skyline:2.75rem;

  /* Drawn from the palette in colors.css: the deepest background is the asphalt,
     and the amber that edges a primary action is the amber that edges a runway */
  --runway-asphalt:var(--color-background-deep);
  --runway-centreline:var(--color-text);
  --runway-edge:var(--color-primary-action-border);
  --runway-scenery:var(--color-primary-action);

  display:flex;
  flex-direction:column;
  align-items:center;
  gap:0.75rem;

  /* The framing belongs to the runway rather than to whatever it is dropped
     into. Everything beside the tarmac is spaced and timed in fixed counts of
     rem, so a strip left to grow with its container shows more of the stretch
     at once in a wide panel than in a narrow one, and the same flight past the
     same hangar reads differently from one screen to the next. The width is
     taken where it is offered, up to that limit, and the room the limit leaves
     over is split evenly rather than left wherever the container puts it. */
  width:100%;
  max-width:15rem;
  margin-inline:auto;

  /* The scenery stands far taller than the runway is deep, so the room it needs
     is kept here rather than taken out of whatever sits above. Clipping is this
     element's job too: the scenery rises off the strip, so the strip cannot
     both hold it in at the ends and let it stand up. */
  position:relative;
  overflow:hidden;
  padding-top:var(--runway-skyline);
}

pt-runway>.strip {
  /* What the scenery is footed against */
  position:relative;

  width:100%;
  height:0.875rem;
  border-radius:2px;
  background-color:var(--runway-asphalt);
  /* The lights that edge a runway, one run of them down each long side */
  border-top:1px solid var(--runway-edge);
  border-bottom:1px solid var(--runway-edge);

  /* The centreline. The gradient paints one stripe and the gap after it, and
     background-size lays that down as a 2px band through the middle of the
     asphalt rather than over the whole of its height. */
  background-image:linear-gradient(
    to right,
    var(--runway-centreline) 0,
    var(--runway-centreline) 50%,
    transparent 50%,
    transparent 100%
  );
  background-size:var(--runway-dash) 2px;
  background-position:0 50%;
  background-repeat:repeat-x;

  /* Linear, and over exactly one stripe and gap, so the loop closes with no seam */
  animation:runway-roll var(--runway-roll) linear infinite;
}

/* Every piece runs the same loop at the same speed, laid off the near end and
   moved by a transform rather than run from one `left` to another: the distance
   has to be a length this element sets for itself, because it is the distance
   the speed above is worked out from.

   What sets them apart is the delay each is given below. A negative one starts
   the piece part-way round, which is how far along the stretch it stands -- so
   the delays are the map, and there is no waiting at the start of the first
   loop for the country to arrive. */
pt-runway .scenery {
  position:absolute;
  /* Footed on the near edge of the asphalt, standing off it rather than lying
     on it: the scenery is what is not seen from above here */
  bottom:100%;
  left:calc(-1 * var(--scenery-width));

  width:var(--scenery-width);
  height:var(--scenery-height);

  fill:var(--runway-scenery);

  animation:runway-passing var(--runway-loop) linear infinite;
}

/* One rule per piece: how tall it stands, its own drawing's proportions worked
   through to a width, and where along the stretch it is planted. The numbers in
   each width are that piece's viewBox, so the two have to be changed together. */
pt-runway .scenery-tower {
  --scenery-height:var(--runway-skyline);
  --scenery-width:calc(var(--scenery-height) * 12 / 26);
  animation-delay:calc(-0.02 * var(--runway-loop));
}

pt-runway .scenery-hangar {
  --scenery-height:1.5rem;
  --scenery-width:calc(var(--scenery-height) * 34 / 14);
  animation-delay:calc(-0.24 * var(--runway-loop));
}

/* The two trees stand close enough together to read as one stand of them */
pt-runway .scenery-conifers {
  --scenery-height:1.35rem;
  --scenery-width:calc(var(--scenery-height) * 20 / 16);
  animation-delay:calc(-0.4 * var(--runway-loop));
}

pt-runway .scenery-tree {
  --scenery-height:1.1rem;
  --scenery-width:calc(var(--scenery-height) * 14 / 16);
  animation-delay:calc(-0.47 * var(--runway-loop));
}

pt-runway .scenery-windsock {
  --scenery-height:1.9rem;
  --scenery-width:calc(var(--scenery-height) * 16 / 18);
  animation-delay:calc(-0.71 * var(--runway-loop));
}

pt-runway>.message {
  font-family:var(--font-regular);
  font-weight:bold;
  text-align:center;
}

@keyframes runway-roll {
  to { background-position:var(--runway-dash) 50%; }
}

@keyframes runway-passing {
  to { transform:translateX(var(--runway-run)); }
}

@media (prefers-reduced-motion: reduce) {
  pt-runway>.strip,
  pt-runway .scenery {
    animation:none;
  }

  pt-runway .scenery {
    display:none;
  }

  pt-runway .scenery-tower {
    display:block;
    left:0;
  }
}

/* Every rule scoped under #person-screen per CLAUDE.md. */

/* Fills the screen body rather than sizing to the person, so the nav below is
   at the foot of the screen and not at the end of whatever was read. */
#person-screen {
  height:100%;

  display:flex;
  flex-direction:column;
  gap:1rem;
}

/* The person is what scrolls, which is what leaves the nav where it was put. */
#person-screen>.body {
  flex:1;
  min-height:0;
  overflow:auto;
}

/* What the body scrolls under: the one action and the walk through the list,
   which are both about the person rather than part of reading them. */
#person-screen>.foot {
  display:flex;
  flex-direction:column;
  gap:0.6rem;
}

#person-screen>.foot>.action {
  display:flex;
  justify-content:center;
}

/* Nobody to act on -- the person is still loading, or gone. Left in flow the
   empty box would still spend the gap above the nav. */
#person-screen>.foot>.action:empty {
  display:none;
}

/* The walk itself is sibling-nav.css's, drawn the same on both screens that
   page. What stays here is the one thing this screen says about it: the count
   reads a size down, as the sub-lines around it do. */
#person-screen .sibling_nav span {
  font-size:0.8125rem;
}

#person-screen .screen_personnel_person-head {
  display:flex;
  align-items:center;
  gap:0.7rem;
}

#person-screen .screen_personnel_person-head pt-face {
  font-size:3.6rem;
}

#person-screen .screen_personnel_person-head h3 {
  margin:0 0 0.2rem;
}

#person-screen .screen_personnel_person-sub {
  display:block;
  color:var(--color-disabled-text);
  line-height:1.35;
}

#person-screen .screen_personnel_person-grade {
  display:flex;
  align-items:center;
  gap:0.4rem;
  margin-top:0.2rem;
}

#person-screen .screen_personnel_person-blurb {
  font-style:italic;
  line-height:1.55;
  padding-left:0.75rem;
  border-left:2px solid var(--color-border-deep);
}

#person-screen .screen_personnel_person-facts {
  display:grid;
  grid-template-columns:auto 1fr;
  gap:0.3rem 1rem;
}

#person-screen .screen_personnel_person-facts dt {
  font-weight:normal;
  color:var(--color-disabled-text);
}

#person-screen .screen_personnel_person-facts dd {
  text-align:right;
  font-variant-numeric:tabular-nums;
}

#person-screen .screen_personnel_person-facts dd b {
  font-weight:bold;
}

/* Reuses screen_personnel_person-sub for the "signed for" line, one size
   smaller than the head's own use of the same class. */
#person-screen .screen_personnel_person-facts .screen_personnel_person-sub {
  font-size:0.6875rem;
}

/* The aeroplane in front of "Assigned to", sitting on the words rather than
   floating above them. */
#person-screen .screen_personnel_person-facts .icon {
  vertical-align:-0.1em;
  margin-right:0.15rem;
}

/* The aeroplane is a way into it as well as a fact about the person, so it is a
   button left reading as the line of text it replaced: the underline is what
   says it can be tapped, and the action on this screen is still the one in the
   foot. Named rather than matched on `button`, which would also take the one
   pt-rate draws for the pay. */
#person-screen .screen_personnel_person-aircraft {
  font:inherit;
  color:inherit;
  background:none;
  border:none;
  padding:0;
  text-decoration:underline;
  cursor:pointer;
}

/* The value and the one control that changes it, on the line the facts give
   them. Wrapping rather than shrinking: the dropdown carries every aeroplane in
   the fleet, and a name squeezed into what is left of a two-column row is not
   worth reading. */
#person-screen .screen_personnel_person-assignment {
  display:flex;
  align-items:center;
  justify-content:flex-end;
  flex-wrap:wrap;
  gap:0.4rem;
}

#person-screen .screen_personnel_person-unassigned {
  color:var(--color-disabled-text);
  font-style:italic;
}

/* Smaller than a button standing on its own: this sits inside a line of facts,
   and the action of the screen is still the one in the foot. The size the crew
   rows give Assign and Unassign, for the same reason. */
#person-screen .screen_personnel_person-assign_action {
  flex:none;
  padding:0.05rem 0.7rem;
}

/* Cancel is the one with no chrome to pad out: the wording is the whole button,
   and what holds it off the dropdown is the line's own gap. */
#person-screen .screen_personnel_person-assign_action.quiet {
  padding:0;
}

/* Outlined rather than filled, and the red only under the pointer: taking
   somebody off costs nothing and is put back from the control beside it, so it
   does not want the whole colour the firing is drawn in. */
#person-screen .screen_personnel_person-take_off {
  border-color:var(--color-border-deep);
  background-color:transparent;
  color:var(--color-disabled-text);
}

#person-screen .screen_personnel_person-take_off:hover {
  border-color:var(--color-destructive-action-border);
  color:var(--color-error);
}

/* Kept to the width of the line it is borrowing, whatever the longest aeroplane
   in it is called. */
#person-screen .screen_personnel_person-assignment select {
  min-width:0;
  max-width:100%;
  height:1.9rem;
  padding:0.05rem 0.35rem;
  font-size:0.8125rem;
}

/* Under the facts, against the line that failed to change. */
#person-screen .screen_personnel_person-assignment_note {
  margin:0.4rem 0 0;
  color:var(--color-error);
  font-size:0.8125rem;
  text-align:right;
}

#person-screen .screen_personnel_person-advisor_block {
  display:flex;
  flex-direction:column;
  gap:0.5rem;
  padding:0.7rem 0.8rem;
  border:1px solid var(--color-border-deep);
  border-radius:5px;
  background-color:var(--color-background-deep);
}

#person-screen .screen_personnel_person-advisor_block h3 {
  font-family:var(--font-airport);
  letter-spacing:0.14em;
  font-weight:bold;
  color:var(--color-primary-action);
  margin:0;
  line-height:1;
}

#person-screen .screen_personnel_person-opinion {
  text-align:right;
}

#person-screen .screen_personnel_person-note {
  padding:0.6rem 0.75rem;
  border-radius:5px;
  font-size:0.8125rem;
  line-height:1.5;
  text-align:center;
}

/* The one outcome that is actually good news gets the green box; gone/taken/
   failed stay as plain muted text. */
#person-screen .screen_personnel_person-note[data-test-key="hired"] {
  border:1px solid var(--color-success);
  background-color:rgba(4, 155, 74, 0.12);
}

#person-screen [data-test="personnel-fire-note"] {
  color:var(--color-error);
  margin-top:0.5rem;
}

/*
 * The controls CabinControls draws: the floor meter, the bar the cabin is split
 * on, the seat fields and the star pickers.
 *
 * Scoped to the game layout rather than to one screen's root id, which is the
 * exception CLAUDE.md allows: the same controls are drawn on the refit screen
 * and on the catalogue's spec sheet, so a copy under each of those roots would
 * be two places to change one bar. This is the narrowest hand-written id above
 * both, the same standing shared.css is on.
 */

#layout_game-layout .cabin_controls {
  display:flex;
  flex-direction:column;
  gap:0.8rem;
}

/* --------------------------------------------------------- the floor meter */

#layout_game-layout .cabin_controls .floor {
  display:flex;
  flex-direction:column;
  gap:0.3rem;
}

#layout_game-layout .cabin_controls .floor_line {
  display:flex;
  flex-wrap:wrap;
  align-items:baseline;
  justify-content:space-between;
  gap:0.25rem 0.65rem;
  color:var(--color-disabled-text);
  font-variant-numeric:tabular-nums;
}

#layout_game-layout .cabin_controls .floor_line b {
  color:var(--color-text);
}

#layout_game-layout .cabin_controls .meter {
  height:0.55rem;
  border-radius:3px;
  background-color:var(--color-disabled);
  overflow:hidden;
}

/* Empty in the stylesheet, filled through the CSSOM: a block box with no width
   of its own fills its parent, so a meter that lost its width would read as a
   spent floor rather than as the bug it is. */
#layout_game-layout .cabin_controls .meter>span {
  display:block;
  width:0;
  height:100%;
  border-radius:3px;
  background-color:var(--color-primary-action);
}

#layout_game-layout .cabin_controls .free {
  color:var(--color-disabled-text);
  font-variant-numeric:tabular-nums;
}

#layout_game-layout .cabin_controls .floor[data-test-key=exact] .free {
  color:var(--color-success);
}

#layout_game-layout .cabin_controls .floor[data-test-key=over] .free {
  color:var(--color-error);
}

#layout_game-layout .cabin_controls .floor[data-test-key=over] .meter>span {
  background-color:var(--color-error);
}

/* ------------------------------------------------------------- the bar */

#layout_game-layout .cabin_controls .split_wrap {
  position:relative;

  /* The sides clear half a handle and a little over: a boundary at the end of
     the floor centres a thumb on the bar's last pixel, and half of it hangs
     past. Exactly half would leave the body that scrolls with no slack at all. */
  margin:1.1rem 0.9rem 1.5rem;

  /* The handles are dragged, so the browser must not read the gesture as a
     scroll of the panel underneath them. */
  touch-action:none;
}

#layout_game-layout .cabin_controls .split {
  display:flex;
  height:2.4rem;
  border-radius:5px;
  overflow:hidden;
  background-color:var(--color-disabled);
}

/* Widths through the CSSOM for the meter's reason, so a bar that never got its
   numbers draws empty rather than drawing one class across the whole floor. */
#layout_game-layout .cabin_controls .seg {
  width:0;

  display:flex;
  align-items:center;
  justify-content:center;
  overflow:hidden;
  white-space:nowrap;
  font-variant-numeric:tabular-nums;
}

/* One rule per class, so a swatch under the bar and the segment it names cannot
   end up different colours. */
#layout_game-layout .cabin_controls .seg_f,
#layout_game-layout .cabin_controls .sw_f {
  background-color:var(--color-primary-action);
}

#layout_game-layout .cabin_controls .seg_j,
#layout_game-layout .cabin_controls .sw_j {
  background-color:var(--color-action-border);
}

#layout_game-layout .cabin_controls .seg_y,
#layout_game-layout .cabin_controls .sw_y {
  background-color:var(--color-action);
}

#layout_game-layout .cabin_controls .seg_f {
  color:var(--color-primary-action-text);
}

/* The two boundaries ride separate rails because they land on the same unit
   whenever business is empty -- a cabin of nothing but first class, and an
   empty one. Stacked on one rail the thumb drawn second takes every press and
   the other can never be grabbed back, which is a layout the control can reach
   and then cannot leave. */
#layout_game-layout .cabin_controls .handle {
  position:absolute;
  left:0;
  width:1.4rem;
  height:1rem;
  margin-left:-0.7rem;
  padding:0;

  border:1px solid var(--color-background-deep);
  border-radius:4px;
  background-color:var(--color-text);
  cursor:ew-resize;
}

#layout_game-layout .cabin_controls .handle[data-handle="1"] {
  top:-0.7rem;
}

#layout_game-layout .cabin_controls .handle[data-handle="2"] {
  bottom:-0.7rem;
}

/* The stem says where the boundary actually falls. It takes no pointer, so two
   boundaries on the same unit still leave two thumbs to grab, and the two
   half-height stems draw as one line where they meet. */
#layout_game-layout .cabin_controls .handle::after {
  content:"";
  position:absolute;
  left:50%;
  width:2px;
  height:1.3rem;
  margin-left:-1px;
  background-color:var(--color-text);
  pointer-events:none;
}

#layout_game-layout .cabin_controls .handle[data-handle="1"]::after {
  top:100%;
}

#layout_game-layout .cabin_controls .handle[data-handle="2"]::after {
  bottom:100%;
}

/* Where the floor runs out, drawn only past it -- which under a compensating
   control is only ever a layout the screen was handed. */
#layout_game-layout .cabin_controls .tick {
  position:absolute;
  top:-0.2rem;
  bottom:-0.2rem;
  left:0;
  width:2px;
  background-color:var(--color-error);
}

#layout_game-layout .cabin_controls .tick[hidden] {
  display:none;
}

/* ------------------------------------------------- the key and the fields */

/* Even thirds rather than columns tracking their segments: a tracked column
   would collide with its neighbour the moment a class is emptied, which is the
   one time both still have to be readable. */
#layout_game-layout .cabin_controls .legend {
  display:grid;
  grid-template-columns:repeat(3, minmax(0, 1fr));
  gap:0.5rem;
}

#layout_game-layout .cabin_controls .legend .item {
  display:flex;
  flex-direction:column;
  gap:0.25rem;
  min-width:0;
}

/* Centred over the field, which is centred text of its own, so a column reads
   as one class from the swatch down. */
#layout_game-layout .cabin_controls .legend .name {
  display:flex;
  align-items:center;
  justify-content:center;
  color:var(--color-disabled-text);
}

#layout_game-layout .cabin_controls .swatch {
  display:inline-block;
  width:0.5rem;
  height:0.5rem;
  margin-right:0.3rem;
  border-radius:2px;
}

#layout_game-layout .cabin_controls .legend input {
  width:100%;
  min-width:0;
  text-align:center;
  font-variant-numeric:tabular-nums;

  /* The spinner is dropped rather than left in. It takes about a fifth of a
     column that is already a third of a phone, and it takes it from one side,
     which pulls the digits off the centre the label above them is centred on.
     Stepping a count is what the bar and the arrow keys are for; this field is
     for typing one, and arrow keys still step it with the spinner gone. */
  appearance:textfield;
}

#layout_game-layout .cabin_controls .legend input::-webkit-outer-spin-button,
#layout_game-layout .cabin_controls .legend input::-webkit-inner-spin-button {
  appearance:none;
  margin:0;
}

/* What the count above it is as a share of the passengers, which the bar cannot
   say: its segments are floor space, and a first seat is five economy ones.
   Kept on one line -- three columns of a phone, and a wrapped "of seats" would
   push the star pickers down as the reader drags. */
#layout_game-layout .cabin_controls .legend .share {
  text-align:center;
  white-space:nowrap;
  font-size:0.75rem;
  font-variant-numeric:tabular-nums;
  color:var(--color-disabled-text);
}

#layout_game-layout .cabin_controls .hint {
  color:var(--color-disabled-text);
  line-height:1.45;
}

/* Sized to its label and centred, the shape the overhaul button already has:
   full width belongs to a screen's own action, and this is help with the
   arithmetic rather than the thing the screen is for. */
#layout_game-layout .cabin_controls .fill {
  align-self:center;
}

/* ---------------------------------------------------------- the pickers */

#layout_game-layout .cabin_controls .stars {
  display:flex;
  flex-direction:column;
  gap:0.55rem;
}

#layout_game-layout .cabin_controls .star_row {
  display:flex;
  align-items:center;
  justify-content:space-between;
  gap:0.5rem;
}

#layout_game-layout .cabin_controls .picker {
  display:flex;
  gap:3px;
  flex:none;
}

/* Three bars rather than the personnel stripes' five, at a size a thumb can
   hit: the read-only pair on the aircraft screen is 4px wide, which is a
   drawing and not a control. */
#layout_game-layout .cabin_controls .picker button {
  width:1.9rem;
  height:1.15rem;
  padding:0;
  border:1px solid transparent;
  border-radius:2px;
  background-color:rgba(221, 221, 221, 0.16);
}

#layout_game-layout .cabin_controls .picker button.on {
  background-color:var(--color-primary-action);
}

/* Every rule scoped under #cabin-screen per CLAUDE.md. The controls themselves
   are not here -- CabinControls draws those and cabin-controls.css is where
   they live, since the catalogue's spec sheet draws the same ones. */

/* Fills the screen body rather than sizing to the controls, so the price and
   Apply stay at the foot however far down the pickers the reader is. */
#cabin-screen {
  height:100%;

  display:flex;
  flex-direction:column;
  gap:0.8rem;
}

#cabin-screen>.sub {
  flex:none;
  color:var(--color-disabled-text);
}

/* Letter-spaced the way the aircraft screen sets a tail number, which is the
   same registration on the screen that opened this one. */
#cabin-screen>.sub b {
  letter-spacing:0.08em;
  color:var(--color-text);
}

/* The controls are what scrolls: the floor is spent at the top and the price is
   read at the foot, and a budget you have to scroll back to is a budget nobody
   watches while they spend it. */
#cabin-screen>.body {
  flex:1;
  min-height:0;
  overflow:auto;

  display:flex;
  flex-direction:column;
  gap:0.8rem;
}

#cabin-screen .totals {
  display:flex;
  flex-wrap:wrap;
  justify-content:space-between;
  gap:0.25rem 0.65rem;
  color:var(--color-disabled-text);
  font-variant-numeric:tabular-nums;
}

#cabin-screen .totals b {
  color:var(--color-text);
}

#cabin-screen>.foot {
  flex:none;

  display:flex;
  flex-direction:column;
  gap:0.6rem;
  padding-top:0.6rem;
  border-top:1px solid var(--color-border-deep);
}

#cabin-screen .price {
  display:flex;
  align-items:baseline;
  justify-content:space-between;
  gap:0.5rem;
  color:var(--color-disabled-text);
}

#cabin-screen .price b {
  color:var(--color-text);
  font-variant-numeric:tabular-nums;
}

/* Buying a fitting is sixteen times the hangar time on an A320neo, so it is
   marked rather than only named: a number that jumps that far with no label on
   it reads as a bug. */
#cabin-screen .price[data-test-key=fitting] span,
#cabin-screen .price[data-test-key=fitting] b {
  color:var(--color-warning);
}

/* Sized to its label and centred under the price it is agreeing to, the shape
   every action on the aircraft screen has. */
#cabin-screen>.foot button {
  align-self:center;
}

#cabin-screen .layout {
  display:flex;
  flex-wrap:wrap;
  align-items:center;
  justify-content:space-between;
  gap:0.25rem 0.65rem;
  margin-bottom:0.75rem;
}

#cabin-screen .ratings {
  display:flex;
  gap:0.65rem;
}

#cabin-screen .facts {
  display:grid;
  grid-template-columns:auto 1fr;
  gap:0.3rem 1rem;
}

#cabin-screen .facts dt {
  color:var(--color-disabled-text);
}

#cabin-screen .facts dd {
  text-align:right;
  font-variant-numeric:tabular-nums;
}

/* The three lines above sum to this one, so it is ruled off from them: a total
   that sits flush with its own parts reads as a fourth part. */
#cabin-screen .facts .total {
  padding-top:0.3rem;
  border-top:1px solid var(--color-border-deep);
  font-weight:600;
}

#cabin-screen [data-test=aircraft-cabin-note] {
  color:var(--color-error);
  margin-top:0.5rem;
}

/*
 * The strip tabs.mjs draws. Scoped to the game layout, which is the narrowest
 * hand-written id above both screens drawing one: an aircraft and a route.
 */

#layout_game-layout .phone_tabs {
  display:grid;
  grid-auto-flow:column;
  grid-auto-columns:minmax(0, 1fr);
  gap:0.35rem;
}

#layout_game-layout .phone_tabs button {
  padding:0.3rem 0.25rem;
  background-color:transparent;
  border-color:var(--color-border-deep);
  color:var(--color-disabled-text);
}

#layout_game-layout .phone_tabs button[aria-selected=true] {
  background-color:var(--color-action);
  border-color:var(--color-action-border);
  color:var(--color-text);
}

/* Every rule scoped under #airframe-screen per CLAUDE.md. The condition bar,
   the rating bars and the band colours are not here -- shared.mjs draws those
   and shared.css is where they live, as does the walk through the list. */

/* Fills the screen body rather than sizing to the airframe, so the tabs stay
   under the identity and the sale stays at the foot. */
#airframe-screen {
  height:100%;

  display:flex;
  flex-direction:column;
  gap:0.8rem;
}

/* The identity and the tabs are fixed: only the panel scrolls, so the tail
   number is still readable at the bottom of the spec sheet. */
#airframe-screen>.head {
  display:flex;
  flex-direction:column;
  gap:0.5rem;
}

#airframe-screen .name_form {
  display:flex;
  flex-direction:column;
  gap:0.5rem;
}

/* The tail the API gave the aeroplane beside the name the player gave it, which
   is the pairing the list row draws as "Spirit of Dundee (G-CLOUD)". Every
   registration in a game is the same width -- one country prefix, a fixed
   suffix -- so the field starts in the same place on every airframe paged to. */
#airframe-screen .identity {
  display:flex;
  align-items:center;
  gap:0.5rem;
}

/* Letter-spaced rather than set in another face: nothing else in the app draws
   a monospace anything, and one font more for one line of one screen is a
   typeface to keep in step for the sake of seven characters. The spacing is
   what makes a tail number read as one. */
#airframe-screen .identity .reg {
  flex:none;
  letter-spacing:0.08em;
}

#airframe-screen .identity .name {
  flex:1;
  min-width:0;
}

/* Drawn only while what is typed differs from what was loaded, so the identity
   line keeps its width whenever nobody is editing. */
#airframe-screen .name_actions {
  display:flex;
  justify-content:flex-end;
  gap:0.4rem;
}

#airframe-screen .name_actions[hidden] {
  display:none;
}

#airframe-screen .sub {
  color:var(--color-disabled-text);
}

#airframe-screen .sub .aside {
  font-weight:normal;
}

/* The panel is what scrolls, which is what leaves the sale and the walk where
   they were put. */
#airframe-screen>.body {
  flex:1;
  min-height:0;
  overflow:auto;
}

#airframe-screen .panel {
  display:flex;
  flex-direction:column;
  gap:0.6rem;
}

/* The tab that is not open is `hidden`, and the rule above would otherwise
   outrank what `hidden` means: an author `display` beats the browser's own
   `[hidden] { display:none }`, so every panel drawn would be drawn at once. */
#airframe-screen .panel[hidden] {
  display:none;
}

#airframe-screen>.foot {
  display:flex;
  flex-direction:column;
  gap:0.6rem;
}

#airframe-screen>.foot>.action {
  display:flex;
  justify-content:center;
}

/* Nobody to act on -- the aeroplane is still loading, or gone. Left in flow the
   empty box would still spend the gap above the walk. */
#airframe-screen>.foot>.action:empty {
  display:none;
}

#airframe-screen .note {
  padding:1.5rem 0;
  text-align:center;
  color:var(--color-disabled-text);
}

/* The one note that is not the whole screen: a name that would not save, under
   the field it was typed in. */
#airframe-screen .name_form .note {
  padding:0;
  text-align:left;
  color:var(--color-error);
}

#airframe-screen .overhaul {
  display:flex;
  flex-direction:column;
  gap:0.45rem;
}

#airframe-screen .overhaul_row {
  display:flex;
  align-items:center;
  gap:0.6rem;
  color:var(--color-disabled-text);
}

#airframe-screen .overhaul_row input[type=range] {
  flex:1;
  min-width:0;
  accent-color:var(--color-primary-action);
}

#airframe-screen .overhaul_row output {
  min-width:2.5em;
  text-align:right;
  color:var(--color-text);
  font-variant-numeric:tabular-nums;
}

#airframe-screen .estimate {
  display:flex;
  align-items:baseline;
  justify-content:space-between;
  gap:0.5rem;
  color:var(--color-disabled-text);
}

#airframe-screen .estimate b {
  color:var(--color-text);
  font-variant-numeric:tabular-nums;
}

/* Every action on this screen is sized to its label and centred, and none of
   them spans the panel: a button stretched across the width reads as the
   screen's own action, and on this screen that is Sell, which the foot centres
   for itself. The overhaul is the last word of a sentence its slider started;
   Configure cabin is a route out of the panel rather than a thing done in it. */
#airframe-screen .overhaul button,
#airframe-screen .configure {
  align-self:center;
}

#airframe-screen .hint {
  color:var(--color-disabled-text);
  line-height:1.45;
}

#airframe-screen .cabin_line,
#airframe-screen .meter_line {
  display:flex;
  flex-wrap:wrap;
  align-items:baseline;
  justify-content:space-between;
  gap:0.25rem 0.65rem;
  color:var(--color-disabled-text);
  font-variant-numeric:tabular-nums;
}

#airframe-screen .cabin_line .layout {
  color:var(--color-text);
}

#airframe-screen .ratings {
  display:flex;
  gap:0.65rem;
}

/*
 * The crew, drawn as the roster draws a person: the same portrait beside the
 * same bold name, with the competence under it. The rules are here rather than
 * lifted beside person-list.css's because `.row` is already two different
 * things -- an aeroplane's box on one list and a person's line on the other --
 * so a rule let out to the layout would reach a screen it was not written for.
 * What is shared is the drawing, which is faceHtml and stripesHtml.
 *
 * The three lists this panel holds -- the pilots, the attendants, then who is
 * free to join either -- are drawn alike and headed alike, since on one panel
 * they are three of the same thing.
 */
#airframe-screen .section_head {
  display:flex;
  align-items:baseline;
  justify-content:space-between;
  gap:0.5rem;

  padding-top:0.3rem;
  border-top:1px solid var(--color-border-deep);

  color:var(--color-disabled-text);
  text-transform:uppercase;
  letter-spacing:0.06em;
}

/* The rule above the first would read as a line under the tab strip. The note
   it follows is the panel's first element whether or not it is being shown. */
#airframe-screen .crew_note+.section_head {
  padding-top:0;
  border-top:none;
}

#airframe-screen .section_head .count {
  font-variant-numeric:tabular-nums;
}

#airframe-screen .crew_list {
  list-style:none;
}

#airframe-screen .crew_list .row {
  display:flex;
  align-items:center;
  gap:0.6rem;
  padding:0.6rem 0.15rem;
  border-bottom:1px solid var(--color-border-deep);
  cursor:pointer;
}

#airframe-screen .crew_list .row:last-child {
  border-bottom:none;
}

#airframe-screen .crew_list .row pt-face {
  font-size:2.5rem;
}

#airframe-screen .crew_list .row:hover {
  background-color:rgba(37, 112, 121, 0.18);
}

#airframe-screen .crew_list .row_body {
  flex:1;
  min-width:0;
}

#airframe-screen .crew_list .row_name {
  font-weight:bold;
  white-space:nowrap;
  overflow:hidden;
  text-overflow:ellipsis;
}

/* Nobody to open, so nothing about the row offers to be pressed. */
#airframe-screen .crew_list .row_empty {
  cursor:default;
}

#airframe-screen .crew_list .row_empty:hover {
  background-color:transparent;
}

#airframe-screen .crew_list .row_empty .row_name {
  font-weight:normal;
  font-style:italic;
  color:var(--color-disabled-text);
}

/* Assign and Unassign are the one write in its two directions, so they are the
   same button in the same place on the row. Smaller than a button that stands
   on its own: a row is not the screen's own action, which is the sale in the
   foot, and the row still has to hold a portrait and a name beside it. */
#airframe-screen .crew_list .row button {
  flex:none;
  padding:0.05rem 0.7rem;
}

/* Outlined rather than filled, and the red only under the pointer: somebody
   taken off lands in the list below and is put back from there, so this does
   not want the whole colour the sale and the firing are drawn in. */
#airframe-screen .crew_list .row .unassign {
  border-color:var(--color-border-deep);
  background-color:transparent;
  color:var(--color-disabled-text);
}

#airframe-screen .crew_list .row .unassign:hover {
  border-color:var(--color-destructive-action-border);
  color:var(--color-error);
}

/* What the available list says while it is being read, when it could not be,
   and when nobody is free -- in the space the rows would have taken. */
#airframe-screen .list_note {
  margin:0;
  padding:0.6rem 0.15rem;
  color:var(--color-disabled-text);
  font-style:italic;
}

/* A refused write, at the top of the panel that asked for it. Not drawn as the
   screen's own `.note`, which is the one that stands in place of an aeroplane
   that would not load: this one has the whole crew underneath it. */
#airframe-screen .crew_note {
  margin:0;
  color:var(--color-error);
}

#airframe-screen .facts {
  display:grid;
  grid-template-columns:auto 1fr;
  gap:0.3rem 1rem;
}

#airframe-screen .facts dt {
  font-weight:normal;
  color:var(--color-disabled-text);
}

#airframe-screen .facts dd {
  text-align:right;
  font-variant-numeric:tabular-nums;
}

#airframe-screen .facts dd b {
  font-weight:bold;
}

#airframe-screen [data-test=fleet-aircraft-overhaul-note],
#airframe-screen [data-test=fleet-aircraft-sell-note] {
  color:var(--color-error);
  margin-top:0.5rem;
}

/*
 * The box switch.mjs draws. Scoped to the game layout, which is the narrowest
 * hand-written id above every screen drawing one -- the same standing tabs.css
 * is on, and for the same reason.
 */

/* Kept to its contents rather than filling the line: a switch shares its row
   with whatever it qualifies. */
#layout_game-layout .switch {
  flex:none;

  display:inline-flex;
  border:1px solid var(--color-border-deep);
  border-radius:5px;
  overflow:hidden;
}

#layout_game-layout .switch button {
  padding:0.25rem 0.7rem;
  border:0;
  border-radius:0;
  background-color:transparent;
  color:var(--color-disabled-text);
  font-size:0.875rem;
}

#layout_game-layout .switch button[aria-pressed=true] {
  background-color:var(--color-action);
  box-shadow:inset 0 0 0 1px var(--color-action-border);
  color:var(--color-text);
}

/* Stated rather than offered: the box says which side it is on and refuses to
   be moved, so the border goes and only the fill is left saying it. */
#layout_game-layout .switch button:disabled {
  background-color:transparent;
  border-color:transparent;
  cursor:default;
}

#layout_game-layout .switch button[aria-pressed=true]:disabled {
  background-color:var(--color-disabled);
  box-shadow:inset 0 0 0 1px var(--color-disabled-border);
  color:var(--color-text);
}

/* Every rule scoped under #flight-screen per CLAUDE.md: what one departure did,
   over the board that opened it. */

#flight-screen {
  height:100%;

  display:flex;
  flex-direction:column;
  gap:0.8rem;
}

#flight-screen>.sub {
  flex:none;
  margin:0;
  color:var(--color-disabled-text);
}

#flight-screen>.body {
  flex:1;
  min-height:0;
  overflow:auto;

  display:flex;
  flex-direction:column;
  gap:1rem;
}

#flight-screen .times {
  margin:0;
  font-family:var(--font-airport);
  font-size:1.125rem;
  letter-spacing:0.06em;
}

#flight-screen .times b {
  font-variant-numeric:tabular-nums;
}

/* The `+1` beside an arrival, raised and small: it is a mark on the time rather
   than part of it. */
#flight-screen .times sup {
  font-size:0.6875rem;
  color:var(--color-primary-action);
}

#flight-screen .hint {
  margin:0.2rem 0 0;
  font-size:0.8125rem;
  line-height:1.4;
  color:var(--color-disabled-text);
}

/* One sentence saying what became of the departure, boxed so it reads as the
   answer to the question the screen was opened with rather than as a caption
   under the times. */
#flight-screen .outcome {
  margin:0;
  padding:0.5rem 0.7rem;
  border-left:3px solid var(--color-action-border);
  border-radius:0 5px 5px 0;
  background-color:var(--color-background-main);
  font-size:0.875rem;
  line-height:1.45;
}

#flight-screen .outcome[data-test-key=cancelled] {
  border-left-color:var(--color-error);
}

#flight-screen .outcome[data-test-key=out-of-position] {
  border-left-color:var(--color-error);
}

#flight-screen .outcome[data-test-key=ferry] {
  border-left-color:var(--color-warning);
}

/* --- the cabins ----------------------------------------------------------- */

/* A grid rather than a table: the row is a name and then one figure per column,
   and the column count moves with what the night has written -- three for a
   flown departure, two for one still to happen. route.css's arrangement, and for
   its reason. */
#flight-screen .cabins {
  display:grid;
  align-items:baseline;
  gap:0.35rem 0.6rem;
  font-size:0.8125rem;
}

#flight-screen .cabins_2 {
  grid-template-columns:1fr auto auto;
}

#flight-screen .cabins_3 {
  grid-template-columns:1fr auto auto auto;
}

#flight-screen .cabins>.column {
  color:var(--color-disabled-text);
  font-size:0.75rem;
  text-align:right;
}

#flight-screen .cabins>.cabin_name {
  display:flex;
  align-items:baseline;
  flex-wrap:wrap;
  gap:0.3rem;
}

#flight-screen .cabins>.figure {
  text-align:right;
  font-variant-numeric:tabular-nums;
}

/* What the fare did, on the cabin it did it to. Never the only mark: the same
   two words are spelled out under the table, and this is only the pointer from
   the figures to the sentence. */
#flight-screen .flag {
  padding:0 0.3rem;
  border-radius:3px;
  font-size:0.6875rem;
  font-style:normal;
  line-height:1.5;
  white-space:nowrap;
  background-color:var(--color-disabled);
  color:var(--color-disabled-text);
}

#flight-screen .flag[data-test-key=sold_out] {
  background-color:color-mix(in srgb, var(--color-success) 25%, transparent);
  color:var(--color-text);
}

#flight-screen .flag[data-test-key=priced_out] {
  background-color:color-mix(in srgb, var(--color-warning) 25%, transparent);
  color:var(--color-text);
}

/* --- what it came to ------------------------------------------------------ */

/* Read down the amount column, the way the morning briefing's statement is, and
   ruled off at the profit for the same reason: a total flush with the two rows
   above it reads as a third one of them. */
#flight-screen .money {
  display:flex;
  flex-direction:column;
  gap:0.25rem;
}

#flight-screen .money .row {
  display:flex;
  align-items:baseline;
  gap:0.75rem;
}

#flight-screen .money .label {
  flex:1;
  min-width:0;
}

#flight-screen .money .amount {
  font-variant-numeric:tabular-nums;
}

#flight-screen .money .row_in .amount {
  color:var(--color-success);
}

#flight-screen .money .row_out .amount {
  color:var(--color-error);
}

#flight-screen .money .row[data-test-key=profit] {
  margin-top:0.3rem;
  padding-top:0.4rem;
  border-top:1px solid var(--color-border-deep);
  font-weight:bold;
}

/* --- the signals ---------------------------------------------------------- */

/* The reason the gap between what was offered and what went is what it is, which
   is what the whole pricing decision rests on -- so it is prose at the foot of
   the screen and not a badge the reader has to already know the meaning of. */
#flight-screen .signals {
  margin:0;
  padding:0;
  list-style:none;

  display:flex;
  flex-direction:column;
  gap:0.4rem;
}

#flight-screen .signals li {
  padding:0.4rem 0.6rem;
  border-left:3px solid var(--color-primary-action);
  border-radius:0 5px 5px 0;
  background-color:var(--color-background-main);
  font-size:0.8125rem;
  line-height:1.45;
}

pt-shape {
  display:block;
}

pt-shape svg {
  display:block;
  width:100%;
  height:auto;
  overflow:visible;
}

pt-shape .bar {
  fill:var(--color-primary-action);
}

pt-shape .base {
  stroke:var(--color-disabled-border);
  stroke-width:1;
}

pt-shape .grid {
  stroke:var(--color-border-deep);
  stroke-width:1;
}

pt-shape .average {
  stroke:var(--color-text);
  stroke-opacity:0.7;
  stroke-width:1;
}

pt-shape text {
  font-family:var(--font-regular);
  font-size:11px;
  fill:var(--color-disabled-text);
}

pt-shape .average_label {
  fill:var(--color-text);
}

pt-shape .label {
  font-family:var(--font-airport);
  font-size:13px;
  letter-spacing:0.6px;
}

/* Today's month or day of the week, so the bar the game is on can be found. */
pt-shape .label.current {
  font-weight:600;
  fill:var(--color-text);
}

/* The one bar a chart is being chosen from is set to, which has to be told
   apart at a glance from forty-seven neighbours in the same colour -- so it
   changes colour rather than shade. */
pt-shape .bar.mark {
  fill:var(--color-text);
}

/* Every rule scoped under #flight_editor-screen per CLAUDE.md. The chart is
   <pt-shape>'s own and the dialog is the application stylesheet's; what is here
   is the form over them. */

/* Fills the screen body rather than sizing to the form, so Save and Stop stay
   at the foot however far down the fares the reader has scrolled -- cabin.css's
   arrangement, and for its reason. */
#flight_editor-screen {
  height:100%;

  display:flex;
  flex-direction:column;
  gap:0.8rem;
}

#flight_editor-screen>.sub {
  flex:none;
  margin:0;
  color:var(--color-disabled-text);
}

#flight_editor-screen>.body {
  flex:1;
  min-height:0;
  overflow:auto;

  display:flex;
  flex-direction:column;
  gap:1rem;
}

/* Everything the route governs, in the body's own arrangement: hidden as one
   until a route is chosen. */
#flight_editor-screen .rest {
  display:flex;
  flex-direction:column;
  gap:1rem;
}

#flight_editor-screen .field {
  display:flex;
  flex-direction:column;
  gap:0.35rem;
}

/* `label` is bold already; a span naming a group of controls rather than one
   of them has to be told to match it. */
#flight_editor-screen .label {
  font-weight:bold;
  padding:0;
}

#flight_editor-screen .row {
  display:flex;
  gap:0.5rem;
}

#flight_editor-screen .row select {
  flex:1;
  min-width:0;
}

#flight_editor-screen .hint {
  margin:0;
  font-size:0.8125rem;
  color:var(--color-disabled-text);
}

#flight_editor-screen .hint b {
  color:var(--color-text);
  font-variant-numeric:tabular-nums;
}

/* Centred on the slider that sets it: it reads as the thumb's own label
   rather than as another line of the form. */
#flight_editor-screen .hint_departure {
  text-align:center;
}

/* The durations before the route and the spec sheet have landed, and for a read
   that never did. Collapsed rather than left holding a blank line, which is
   what the chart above does for the same reason. */
#flight_editor-screen .hint_departure:empty {
  display:none;
}

/*
 * What the API makes of the departure the slider is on, under the departure it
 * names. Coloured rather than iconed: it changes on every half hour of a drag,
 * and a colour is read without being looked at.
 */
#flight_editor-screen .hint_slot {
  text-align:center;
}

#flight_editor-screen .hint_slot.free {
  color:var(--color-success);
}

/* A ferry is a bill, not a refusal: the flight can be written, and the warning
   is what it will cost to write it. */
#flight_editor-screen .hint_slot.ferry {
  color:var(--color-warning);
}

#flight_editor-screen .hint_slot.blocked {
  color:var(--color-error);
}

/* The refusal that stands in place of the form rather than beside it: the pair
   is out of the aeroplane's reach, so it is said under the select that chose it
   and there is nothing below to draw. */
#flight_editor-screen .hint_route {
  color:var(--color-error);
}

#flight_editor-screen .pair {
  margin:0;
  font-family:var(--font-airport);
  font-size:1.125rem;
  letter-spacing:0.08em;
}

/* Empty below Impressive, where the API sends no curve: the box collapses
   rather than holding a gap where a chart would have been. */
#flight_editor-screen .chart:empty {
  display:none;
}

/* Edge to edge under the chart, and no gutter between them: the slider picks
   one of the bars above it, so the two have to be measured in the same width. */
#flight_editor-screen input[type=range] {
  width:100%;
  margin:0;
  accent-color:var(--color-primary-action);
}

/*
 * Mobile -> the three lines the departure is read from move above the slider,
 * which on a phone is dragged under a thumb that would otherwise cover them.
 * The slider is ordered last instead of the lines first so the chart stays
 * where it is, directly over the bar the slider picks.
 *
 * game-phone.css's breakpoint, repeated here rather than shared since a media
 * block cannot be split across files.
 */
@media (max-width: 40rem), (max-height: 30rem) {
  #flight_editor-screen .field_departure input[type=range] {
    order:1;
  }
}

/* The name, what the cabin holds, the currency and the fare -- four columns so
   the three cabins read as a table rather than as three sentences. */
#flight_editor-screen .fares {
  display:grid;
  grid-template-columns:auto 1fr auto auto;
  align-items:center;
  gap:0.4rem 0.6rem;
}

#flight_editor-screen .fares legend {
  margin-bottom:0.35rem;
}

/* The row IS the label, so the control is inside it and needs no id to be
   named by one. `display:contents` keeps its three parts in the grid above. */
#flight_editor-screen .fare {
  display:contents;
}

#flight_editor-screen .fare .seats {
  font-weight:normal;
  font-size:0.8125rem;
  color:var(--color-disabled-text);
}

/* A bare number in a column of its own says nothing about what it counts, and
   the legend two rows up cannot say it for each of them. */
#flight_editor-screen .fare .unit {
  font-weight:normal;
  color:var(--color-disabled-text);
}

#flight_editor-screen .fare input {
  width:6.5rem;
  text-align:right;
  font-variant-numeric:tabular-nums;
}

/* The one note this screen has is always a refusal, its own or the API's. */
#flight_editor-screen>.note {
  flex:none;
  margin:0;
  color:var(--color-error);
  font-size:0.875rem;
}

#flight_editor-screen>.note[hidden] {
  display:none;
}

#flight_editor-screen>.foot {
  flex:none;

  display:flex;
  align-items:center;
  justify-content:flex-end;
  gap:0.5rem;
  padding-top:0.6rem;
  border-top:1px solid var(--color-border-deep);
}

/* Stop sits at the far edge from Save, not beside it: they are opposite
   answers and a mis-tap between them cannot be taken back. */
#flight_editor-screen>.foot .destructive {
  margin-right:auto;
}

/* Both are laid out as flex above, which outranks the browser's own rule for
   `hidden`, so the two the route hides say it here. */
#flight_editor-screen .rest[hidden],
#flight_editor-screen>.foot[hidden] {
  display:none;
}

/* --- the confirmation ----------------------------------------------------- */

/*
 * The dialog is drawn into this screen, so its rules are this screen's. The
 * box, the backdrop and the actions row are the application stylesheet's.
 */
#flight_editor-screen .ferries {
  margin:0.75rem 0 0;
  padding:0;
  list-style:none;

  display:flex;
  flex-direction:column;
  gap:0.3rem;
}

#flight_editor-screen .ferries li {
  padding:0.35rem 0.5rem;
  border-left:3px solid var(--color-warning);
  background-color:var(--color-background-main);
  border-radius:0 5px 5px 0;
  font-size:0.8125rem;
  font-variant-numeric:tabular-nums;
}

#flight_editor-screen .total {
  display:flex;
  justify-content:space-between;
  gap:0.5rem;
  margin:0.6rem 0 0;
  color:var(--color-disabled-text);
}

#flight_editor-screen .total b {
  color:var(--color-warning);
  font-variant-numeric:tabular-nums;
}

/* Every rule scoped under #flights-screen per CLAUDE.md, except the view switch,
   which the phone's own header draws and which is scoped under the game layout
   at the foot of this file. */

#flights-screen {
  /* The week and the day are one geometry at two scales, and this is the whole
     of the difference: how tall an hour is, and how many columns fit. */
  --hour-height:20px;
  --columns-in-view:3;
  --column-gap:2px;
  --head-height:2.4rem;

  /* Fills the screen body, hub.css's arrangement and for its reason: a 24-hour
     grid is taller than a handset whatever is done to it, so the GRID is what
     scrolls and everything under it -- the position bar above all -- stays
     where the reader can see it. Letting the whole screen scroll instead puts
     the bar below the fold, where it says nothing about a strip nobody can see
     it beside. */
  height:100%;

  display:flex;
  flex-direction:column;
  gap:0.6rem;
}

/* Nothing here flexes: a column flex box shorter than its contents shrinks
   them, and the one that loses is the position bar -- four pixels is the whole
   of it, with nothing inside to hold it open. The grid below takes the rest. */
#flights-screen>* {
  flex:none;
}

/* ‹ › page a whole week in the week and a day in the day. They sit at the foot
   with the key between them, thumbs' width apart at either edge, rather than
   beside the airframe select they do not change. */
#flights-screen .pagers {
  display:flex;
  align-items:center;
  gap:0.5rem;
}

#flights-screen[data-view=day] {
  --hour-height:21px;
  --columns-in-view:1;
}

#flights-screen .controls {
  display:flex;
  gap:0.5rem;
  align-items:center;
}

#flights-screen .controls select {
  flex:1;
  min-width:0;
}

#flights-screen .pager {
  width:2.75rem;
  padding:0;
  text-align:center;
}

/* The note above the grid: hub.css's shape, which is not reachable from here --
   that one is fenced under `.hub` and this screen is not one. */
#flights-screen .note {
  display:flex;
  flex-direction:column;
  gap:0.1rem;
  margin:0;
  padding:0.5rem 0.75rem;
  border:1px solid var(--color-border-deep);
  border-left-width:3px;
  border-radius:5px;
  background-color:var(--color-background-deep);
  color:var(--color-disabled-text);
  font-size:0.875rem;
}

/* The rule above would otherwise beat the hidden attribute's own display:none */
#flights-screen .note[hidden] {
  display:none;
}

#flights-screen .note strong {
  color:var(--color-text);
}

#flights-screen .note_error {
  border-left-color:var(--color-error);
}

#flights-screen .note_warn {
  border-left-color:var(--color-warning);
}

/* The note while a read is in flight: the runway draws its own box, so this one
   gets out of its way rather than framing it. */
#flights-screen .note_runway {
  padding:0;
  border:0;
  background-color:transparent;
}

/* --- the grid ------------------------------------------------------------ */

/* The hour axis is its own column and does not scroll; everything to its right
   does. That is what puts three days in view with the other four a swipe away
   while the hours stay where they were read. */
#flights-screen .board {
  /* The one part of the screen that scrolls vertically, and it takes whatever
     height is left after the rest. 24 hours at a legible scale does not fit a
     handset, and the alternative -- scrolling the whole screen -- carries the
     position bar, the range and the key off the bottom of it. */
  flex:1;
  min-height:0;
  overflow-y:auto;

  display:grid;
  grid-template-columns:1.5rem minmax(0, 1fr);
  gap:0 0.25rem;
  align-content:start;
}

/* A spacer the height of a column's head, then eight equal stretches of three
   hours -- so a label sits at the head of the one it names and nothing in here
   has to be placed. */
#flights-screen .axis {
  height:calc(var(--head-height) + 24 * var(--hour-height));

  display:grid;
  grid-template-rows:var(--head-height) repeat(8, 1fr);

  font-size:0.625rem;
  line-height:1;
  color:var(--color-disabled-text);
  font-variant-numeric:tabular-nums;
}

/* The spacer sticks with the column heads beside it, and is opaque for the
   same reason they are: without it the hours scroll up into the band the dates
   are pinned in and sit alongside them. */
#flights-screen .axis_head {
  position:sticky;
  top:0;
  z-index:1;
  background-color:var(--color-background-main);
}

/* A grid scroll container: a percentage track resolves against the width on
   screen and not against the strip that overflows it, so the columns in view
   are what --columns-in-view says however many days are on the strip. */
#flights-screen .scroller {
  /* Sideways only. A horizontal scrollbar is laid inside the box and takes its
     height out of the grid, which leaves the columns half a slot taller than
     what is left to draw them in -- and the browser answers that with a SECOND
     vertical scrollbar, beside the board's own. Hiding the bar is what the
     position bar under the grid is for: it says where in the seven days the
     three in view are, which is the one thing a scrollbar was telling anybody.
     The strip still moves to a swipe, to shift-wheel, and to the arrow keys
     once it has focus; the pagers beside the key move a whole week. */
  overflow:auto hidden;
  scrollbar-width:none;
  overscroll-behavior-x:contain;
  scroll-snap-type:x proximity;

  display:grid;
  grid-auto-flow:column;
  grid-auto-columns:calc((100% - (var(--columns-in-view) - 1) * var(--column-gap)) / var(--columns-in-view));
  gap:var(--column-gap);
}

#flights-screen .scroller::-webkit-scrollbar {
  display:none;
}

#flights-screen .column {
  scroll-snap-align:start;

  display:grid;
  grid-template-rows:var(--head-height) calc(24 * var(--hour-height));
}

#flights-screen .head {
  /* Stays put while the hours scroll under it: a 24-hour grid is read by
     scrolling, and a column whose date has gone off the top is a column of
     blocks belonging to nobody. Opaque, for the same reason. */
  position:sticky;
  top:0;
  z-index:1;
  background-color:var(--color-background-main);

  /* The row is a fixed height so that the hour axis beside it lines up with
     the grid; a long date name is cut rather than pushing the two apart. */
  overflow:hidden;

  text-align:center;
  font-size:0.8125rem;
  line-height:1.2;
  color:var(--color-disabled-text);
}

#flights-screen .column[data-state=today] .head,
#flights-screen .column[data-state=open] .head {
  color:var(--color-text);
}

#flights-screen .head span {
  display:flex;
  align-items:center;
  justify-content:center;
  gap:0.2rem;
  font-size:0.6875rem;
  color:var(--color-disabled-text);
}

#flights-screen .head .icon {
  font-size:0.75rem;
}

/* The hour lines are the column's own background rather than seven elements a
   piece: three hours light, three hours nothing, all the way down. */
#flights-screen .slots {
  position:relative;
  border-radius:3px;
  background-image:repeating-linear-gradient(
    to bottom,
    rgba(20, 65, 83, 0.6) 0 1px,
    transparent 1px calc(3 * var(--hour-height))
  );
}

#flights-screen .column[data-state=flown] .slots {
  background-color:rgba(8, 31, 43, 0.45);
}

#flights-screen .column[data-state=open] .slots {
  background-color:var(--color-background-deep);
}

/* Today is the one column that is neither history nor open, and it is outlined
   rather than merely tinted: the lock is the point of the whole board. */
#flights-screen .column[data-state=today] .slots {
  background-color:#0b2733;
  box-shadow:inset 0 0 0 1px var(--color-action-border);
}

/* Past the far edge, and hatched so that colour is not the only thing saying
   so -- the words in the middle of it say the rest. */
#flights-screen .column[data-state=closed] .slots {
  background-image:
    repeating-linear-gradient(135deg, rgba(139, 152, 162, 0.10) 0 4px, transparent 4px 10px),
    repeating-linear-gradient(
      to bottom,
      rgba(20, 65, 83, 0.6) 0 1px,
      transparent 1px calc(3 * var(--hour-height))
    );
}

/* The last day that can still be changed, marked on the edge itself. */
#flights-screen .column[data-edge=true] .slots {
  box-shadow:inset -2px 0 0 var(--color-primary-action);
}

/* Where a tap would put a departure: the half hour under the pointer, placed
   by _drawMarker. A line rather than a slot-tall shadow -- anything with a box
   reads as a block that is already on the grid. */
#flights-screen .marker {
  position:absolute;
  left:2px;
  right:2px;
  height:2px;
  border-radius:1px;
  background-color:var(--color-primary-action);

  /* The time below is centred on the line, so half of it hangs above: at the
     midnight end that is into the column head, which is sticky and opaque and
     would otherwise cut the time in two. */
  z-index:2;

  /* The marker follows the pointer, so it must never be what the pointer is
     over: taking the events would leave it flickering under its own hover and
     swallowing the tap it is there to promise. */
  pointer-events:none;
}

/* The half hour the line stands for. The axis is three hours a label, which
   says which stretch the pointer is in and not which departure it would make,
   and the time is what the editor opens on. Centred on the line at the column's
   edge, where it covers no block the reader is aiming between. */
#flights-screen .marker b {
  position:absolute;
  top:0;
  left:0;
  transform:translateY(-50%);
  padding:0 3px;
  border-radius:2px;
  background-color:var(--color-primary-action);
  color:var(--color-primary-action-text);
  font-size:0.625rem;
  font-weight:normal;
  line-height:1.5;
  font-variant-numeric:tabular-nums;
}

/*
 * Mobile -> no marker. The screen is being held rather than pointed at, so the
 * only thing that could raise one is a stylus or a mouse plugged into a phone,
 * and a line drawn under a finger is drawn under what the finger already hides.
 * Tapping bare grid still adds a flight there; it is the promise of one that
 * goes.
 *
 * game-phone.css's breakpoint, repeated here rather than shared since a media
 * block cannot be split across files.
 */
@media (max-width: 40rem), (max-height: 30rem) {
  #flights-screen .marker {
    display:none;
  }
}

/* Only the columns that can be changed answer a tap on bare grid, so they are
   the only ones that say so. */
#flights-screen .column[data-editable=true] .slots {
  cursor:pointer;
}

#flights-screen .shut {
  position:absolute;
  top:33%;
  left:0;
  right:0;
  text-align:center;
  font-size:0.6875rem;
  line-height:1.3;
  color:var(--color-disabled-text);
}

/* --- the blocks ---------------------------------------------------------- */

/* Placed by fillBoard, which sets top and height from the data attributes: the
   page's CSP drops an inline style attribute, so neither can be written out in
   the markup. */
#flights-screen .block {
  position:absolute;
  left:2px;
  right:2px;
  box-sizing:border-box;
  overflow:hidden;
  border-radius:3px;
  padding:1px 4px;
  font-size:0.6875rem;
  line-height:1.15;
  white-space:nowrap;

  display:flex;
  align-items:baseline;
  justify-content:space-between;
  gap:0.25rem;
}

/* A piece of a flight that began yesterday, or one that runs off the foot of
   its day: the dotted edge is where the block was cut, and the ↳ in its own
   text says which end it is. */
#flights-screen .block[data-continuation=true] {
  border-top-style:dotted;
  border-top-left-radius:0;
  border-top-right-radius:0;
}

#flights-screen .block[data-cut=true] {
  border-bottom-style:dotted;
  border-bottom-left-radius:0;
  border-bottom-right-radius:0;
}

#flights-screen .block .when {
  flex:none;
  opacity:0.85;
}

#flights-screen .block .pair {
  overflow:hidden;
  text-overflow:ellipsis;
}

/* The day has the width the week does not, so the block carries two lines of
   it: the leg with a local time at each end, and then what it did or will cost. */
#flights-screen[data-view=day] .block {
  left:4px;
  right:4px;
  padding:2px 8px;
  font-size:0.75rem;
  line-height:1.4;
  gap:0.5rem;
}

#flights-screen[data-view=day] .block .facts {
  flex:none;
  display:inline-flex;
  align-items:baseline;
  gap:0.25rem;
}

#flights-screen[data-view=day] .block .leg {
  overflow:hidden;
  text-overflow:ellipsis;
}

#flights-screen .repeat {
  color:var(--color-disabled-text);
}

/* What the night said about the fare, on the flown block the plan is still
   repeating from. Not a colour on its own: ▲ is demand above the seats and ▼ is a
   fare above what was paid, the title says which, and `data-signal` is what a
   spec reads. */
#flights-screen .signal {
  flex:none;
  font-size:0.625rem;
  line-height:1;
}

#flights-screen [data-signal=sold_out] .signal {
  color:var(--color-success);
}

/* The one a player should go and fix, so it is the warning colour and the other
   is not: a cabin that filled left money behind, and a cabin that flew empty
   spent fuel to carry nobody. */
#flights-screen [data-signal=priced_out] .signal {
  color:var(--color-warning);
}

/* --- what a block is ------------------------------------------------------ */

/* Five kinds and a forecast, and colour is never the only mark on any of them:
   a flown block is flat, a cancellation is struck through and dashed, a ferry
   is hatched and says FERRY, and the departure that will not fly is outlined
   and carries ⚠. */
#flights-screen .block_planned {
  background-color:var(--color-action);
  border:1px solid var(--color-action-border);
  color:var(--color-text);
}

#flights-screen .block_locked {
  background-color:#123b44;
  border:1px solid #1d5a63;
  color:var(--color-text);
}

#flights-screen .block_flown {
  background-color:var(--color-disabled);
  border:1px solid var(--color-disabled-border);
  color:var(--color-disabled-text);
}

#flights-screen .block_cancelled {
  background-color:transparent;
  border:1px dashed var(--color-error);
  color:var(--color-error);
}

#flights-screen .block_cancelled .when,
#flights-screen .block_cancelled .pair,
#flights-screen .block_cancelled .leg {
  text-decoration:line-through;
}

#flights-screen .block_ferry {
  background-image:repeating-linear-gradient(135deg,
    rgba(247, 148, 29, 0.45) 0 3px, rgba(247, 148, 29, 0.12) 3px 7px);
  border:1px solid var(--color-warning);
  color:#ffd9a8;
  font-weight:bold;
}

#flights-screen .block_out-of-position {
  background-color:transparent;
  border:2px solid var(--color-error);
  color:var(--color-error);
  font-weight:bold;
}

/* --- under the grid ------------------------------------------------------- */

/* Where in the seven days the three in view are, and the way to move them:
   this stands in for the scrollbar the scroller hides, so it is dragged as well
   as read. Sized and placed by _drawPosition off the scroller itself. */
#flights-screen .position {
  position:relative;
  margin-left:1.75rem;
  height:4px;
  border-radius:2px;
  background-color:var(--color-disabled);

  cursor:grab;
  /* A finger on the bar drags the strip; without this the browser takes the
     same gesture for a scroll of the page and the bar never sees it. */
  touch-action:none;
  user-select:none;
}

#flights-screen .position:active {
  cursor:grabbing;
}

/* Four pixels is a fair rail and a poor target, so the reachable area is grown
   past the box rather than inside it: a taller bar would take its extra height
   out of the grid, which is the one thing on this screen with none to spare.
   Mostly downwards, into the range line, since what is above is the board. */
#flights-screen .position::before {
  content:"";
  position:absolute;
  top:-5px;
  bottom:-9px;
  left:0;
  right:0;
}

#flights-screen .position[hidden] {
  display:none;
}

#flights-screen .position i {
  display:block;
  height:100%;
  border-radius:2px;
  background-color:var(--color-action-border);

  /* The thumb is drawn, never grabbed: the press is handled on the bar, which
     works out where along the strip it landed, so the thumb taking the pointer
     would only hide where it was pressed. */
  pointer-events:none;
}

#flights-screen .foot {
  display:flex;
  justify-content:space-between;
  gap:0.5rem;
  margin:0;
  font-size:0.75rem;
  color:var(--color-disabled-text);
}

#flights-screen .legend {
  flex:1;
  min-width:0;

  display:flex;
  flex-wrap:wrap;
  justify-content:center;
  gap:0.2rem 0.6rem;
  margin:0;
  padding:0;
  list-style:none;
  font-size:0.75rem;
  color:var(--color-disabled-text);
}

#flights-screen .legend li {
  display:inline-flex;
  align-items:center;
  gap:0.3rem;
}

/* The same five treatments at the size of a swatch, so the key and the grid
   cannot drift apart. */
#flights-screen .swatch {
  width:14px;
  height:10px;
  border-radius:2px;
  box-sizing:border-box;
}

/* --- the view switch ------------------------------------------------------ */

/*
 * Drawn into the phone's own screen header rather than into the body, so the
 * grid below loses no height to it. The markup is this screen's, but the
 * element it lands in is the phone's, so the narrowest hand-written id above
 * it is the game layout's and `.view_switch` is what fences it in below that.
 */
#layout_game-layout .view_switch {
  display:inline-flex;
  border:1px solid var(--color-border-deep);
  border-radius:5px;
  overflow:hidden;
}

#layout_game-layout .view_switch button {
  padding:0.15rem 0.85rem;
  border:0;
  border-radius:0;
  background-color:transparent;
  color:var(--color-disabled-text);
  font-size:0.875rem;
}

#layout_game-layout .view_switch button[aria-pressed=true] {
  background-color:var(--color-action);
  box-shadow:inset 0 0 0 1px var(--color-action-border);
  color:var(--color-text);
}

/* Every block opens something -- the editor where the day is still open, what the
   departure did where it is not -- so every one of them says so to the pointer
   and takes focus from the keyboard. */
#flights-screen .block {
  cursor:pointer;
}

#flights-screen .block:focus-visible {
  outline:2px solid var(--color-primary-action);
  outline-offset:-1px;
}

/* Only the one that can be changed answers a hover in the colour of the control
   that changes it: the two are still ruled off from each other by more than the
   block's own fill. */
#flights-screen .block[data-editable=true]:hover {
  border-color:var(--color-primary-action);
}

/* Beside the airframe it adds a flight to, rather than under the grid: the
   board fought for every pixel of that height, and a row of its own would cost
   an hour of the day to hold one button.

   A plus and no wording, squared off against the select's own height: the
   select names the aeroplane the flight is being added to, and a label on the
   button would take that width away from the one place it is read. */
#flights-screen .controls button {
  flex:none;

  /* Its height is the select's, taken off the row rather than written down
     again: the figure lives in the application stylesheet, and a copy of it
     here would sit at the wrong height the day that one moves. The row centres
     everything else, so this overrides it for the one item that has to match. */
  align-self:stretch;

  width:2.25rem;
  padding:0;
  text-align:center;
  font-size:1.125rem;
  line-height:1;
}

/*
 * The frame hub.mjs draws, and the parts every hub's summary opens with.
 *
 * Scoped to the game layout rather than to one screen's root id, which is the
 * exception CLAUDE.md allows: the markup comes out of a module three screens
 * import, and #layout_game-layout is the narrowest hand-written id above all
 * three. `.hub` is what fences it in below that.
 */

/* Fills the screen body rather than sizing to what was read, so the tiles are
   at the foot of the screen and not at the end of the summary. */
#layout_game-layout .hub {
  height:100%;

  display:flex;
  flex-direction:column;
  gap:1rem;
}

/* The summary is what scrolls, which is what leaves the tiles where they were put. */
#layout_game-layout .hub>.body {
  flex:1;
  min-height:0;
  overflow:auto;

  display:flex;
  flex-direction:column;
  gap:1rem;
}

/* Nothing to open -- the summary is still loading, or could not be read. Left in
   flow the empty box would still spend the gap above it. */
#layout_game-layout .hub>.foot:empty {
  display:none;
}

/* The headline: how many, and what they cost a month. */
#layout_game-layout .hub .figure {
  display:flex;
  align-items:baseline;
  justify-content:space-between;
  gap:0.5rem;
}

#layout_game-layout .hub .count {
  font-family:var(--font-airport);
  font-size:1.25rem;
  font-weight:bold;
  line-height:1;
  text-transform:uppercase;
}

#layout_game-layout .hub .rate {
  font-size:1.25rem;
  color:var(--color-primary-action);
  font-variant-numeric:tabular-nums;
}

#layout_game-layout .hub .note {
  display:flex;
  gap:0.65rem;
  margin:0;
  padding:0.75rem 0.85rem;
  border:1px solid var(--color-border-deep);
  border-left-width:3px;
  border-radius:5px;
  background-color:var(--color-background-deep);
  color:var(--color-disabled-text);
}

/* The rule above would otherwise beat the hidden attribute's own display:none */
#layout_game-layout .hub .note[hidden] {
  display:none;
}

#layout_game-layout .hub .note strong {
  color:var(--color-text);
}

/* One column a tile, however many a hub has. */
#layout_game-layout .hub .tiles {
  display:grid;
  grid-auto-flow:column;
  grid-auto-columns:1fr;
  gap:0.6rem;
}

/* No border, no background: the same treatment game-phone.css gives its own menu
   buttons, so what is read is the drawing and its wording. */
#layout_game-layout .hub .tile {
  font:inherit;
  display:flex;
  flex-direction:column;
  align-items:center;
  gap:0.1rem;
  padding:0.65rem 0.35rem 0.7rem;
  text-align:center;
  border:0;
  background-color:transparent;
  color:var(--color-text);
  cursor:pointer;
}

#layout_game-layout .hub .tile:hover {
  color:var(--color-hover);
}

/* The count is muted by default, so it has to be told to follow the hover. */
#layout_game-layout .hub .tile:hover span {
  color:currentColor;
}

/* Drawn with no border of its own, so being disabled has only the wording and
   the cursor left to say it with -- application.css's own disabled rule is all
   border and background. */
#layout_game-layout .hub .tile:disabled,
#layout_game-layout .hub .tile:disabled:hover,
#layout_game-layout .hub .tile:disabled:hover span {
  color:var(--color-disabled-text);
  background-color:transparent;
  cursor:not-allowed;
}

#layout_game-layout .hub .tile .icon {
  font-size:1.4rem;
  margin-bottom:0.2rem;
}

#layout_game-layout .hub .tile b {
  font-family:var(--font-airport);
  font-size:1.0625rem;
  text-transform:uppercase;
  line-height:1.1;
}

#layout_game-layout .hub .tile span {
  font-size:0.6875rem;
  color:var(--color-disabled-text);
  line-height:1.3;
}

/* Every rule scoped under #aircraft_list-screen per CLAUDE.md: the fleet and the
   used market are the same list, so the one stylesheet draws both. The condition
   bar, the rating bars and the band colours are not here -- shared.mjs draws
   those and shared.css is where they live. */

#aircraft_list-screen {
  display:flex;
  flex-direction:column;
  gap:1rem;
}

#aircraft_list-screen .filters {
  display:flex;
  gap:0.5rem;
}

#aircraft_list-screen .filters select {
  flex:1;
  min-width:0;
}

#aircraft_list-screen .list {
  list-style:none;
}

#aircraft_list-screen .note {
  padding:1.5rem 0;
  text-align:center;
  color:var(--color-disabled-text);
}

/* Step 1's hub box, grown: the same border, radius and background, with the
   list's own two facts on a fourth line. */
#aircraft_list-screen .row {
  display:flex;
  flex-direction:column;
  gap:0.4rem;

  margin-bottom:0.6rem;
  padding:0.65rem 0.75rem;
  border:1px solid var(--color-border-deep);
  border-radius:7px;
  background-color:var(--color-background-deep);
  cursor:pointer;
}

#aircraft_list-screen .row:last-child {
  margin-bottom:0;
}

#aircraft_list-screen .row:hover {
  border-color:var(--color-action-border);
}

/* Wrapping so that a long model beside a nine-figure ask drops the ask to its
   own line rather than ellipsing the model, which on a market row is the only
   thing naming what is for sale. Flexbox wraps before it shrinks, so the cell
   that moves is never the identity. */
#aircraft_list-screen .row_top {
  display:flex;
  flex-wrap:wrap;
  align-items:baseline;
  justify-content:space-between;
  gap:0.5rem;
}

#aircraft_list-screen .row_identity {
  font-weight:bold;
  min-width:0;
  white-space:nowrap;
  overflow:hidden;
  text-overflow:ellipsis;
}

/* What qualifies the identity rather than being it: the registration after a
   name the fleet drew, and the size after the model the market drew, since an
   offer has no tail number to head a row with. */
#aircraft_list-screen .row_identity .tail,
#aircraft_list-screen .row_identity .aside {
  font-weight:normal;
  color:var(--color-disabled-text);
}

#aircraft_list-screen .facts {
  display:flex;
  flex-wrap:wrap;
  align-items:center;
  justify-content:space-between;
  gap:0.25rem 0.65rem;

  color:var(--color-disabled-text);
  font-variant-numeric:tabular-nums;
}

/* The size qualifies the model and rides in a bracket after it. The line is
   already muted, so this needs nothing of its own beyond not being bold. */
#aircraft_list-screen .facts .aside {
  font-weight:normal;
}

#aircraft_list-screen .ratings {
  display:flex;
  gap:0.65rem;
}

/* The band word with the airframe's hours after it, one cell against the cabin:
   the two are one fact about what the previous operator left behind. The hours
   are muted so the only colour on the line is the band's. */
#aircraft_list-screen .cond {
  flex:none;
}

#aircraft_list-screen .cond .hours {
  color:var(--color-disabled-text);
  font-variant-numeric:tabular-nums;
}

#aircraft_list-screen .ask {
  flex:none;
  font-variant-numeric:tabular-nums;
}

/* The advisor's verdict, on a line of its own under the three lines of fact: it
   reads the offer as a whole rather than adding a fourth fact to it. The label
   is muted so that the only colour on the line is the verdict's own. The row is
   simply a line shorter where no advisor is employed. */
#aircraft_list-screen .deal {
  display:flex;
  gap:0.35rem;
}

#aircraft_list-screen .deal .label {
  color:var(--color-disabled-text);
}

/* The advisor's verdict, coloured by what it says. Bargain and extortionate are
   the two that change a decision, so those two carry weight as well as colour. */
#aircraft_list-screen .deal_bargain { color:var(--color-success); font-weight:bold; }
#aircraft_list-screen .deal_cheap { color:var(--color-success); }
#aircraft_list-screen .deal_fair { color:var(--color-text); }
#aircraft_list-screen .deal_steep { color:var(--color-warning); }
#aircraft_list-screen .deal_extortionate { color:var(--color-error); font-weight:bold; }

/* A run of words is not a verdict, so it is neutral: colouring "somewhere
   between bargain and fair" as a bargain would dress a worthless advisor's guess
   up as good news. */
#aircraft_list-screen .deal_hedged {
  color:var(--color-disabled-text);
  font-style:italic;
}

/* Every rule scoped under #catalogue_type-screen per CLAUDE.md. The cabin
   controls are not here -- CabinControls draws those and cabin-controls.css is
   where they live, since the refit screen draws the same ones.

   Nothing in this file sets a font-size: every word on the screen is the body
   size it inherits, and what separates a line from the one under it is weight
   and colour. The headings are the exception, and they are the browser's. */

/* Fills the screen body rather than sizing to the controls, so the receipt and
   Order stay at the foot however far down the cabin the reader is. */
#catalogue_type-screen {
  height:100%;

  display:flex;
  flex-direction:column;
  gap:0.8rem;
}

#catalogue_type-screen>.sub {
  flex:none;
  color:var(--color-disabled-text);
}

/* The receipt is what a reader watches while they drag a boundary and tap a
   star, so everything above it is what scrolls. */
#catalogue_type-screen>.body {
  flex:1;
  min-height:0;
  overflow:auto;

  display:flex;
  flex-direction:column;
  gap:0.8rem;
}

#catalogue_type-screen .headline {
  display:flex;
  flex-wrap:wrap;
  align-items:baseline;
  justify-content:space-between;
  gap:0.25rem 0.65rem;
  color:var(--color-disabled-text);
}

#catalogue_type-screen .headline b {
  color:var(--color-text);
  font-variant-numeric:tabular-nums;
}

/* ------------------------------------------------------------- the spec */

/* Boxed off from everything under it: the panel is reference and the rest of
   the screen is the order, drawn in the same box the personnel advisor is. */
#catalogue_type-screen .panel {
  padding:0.7rem 0.8rem;
  border:1px solid var(--color-border-deep);
  border-radius:5px;
  background-color:var(--color-background-deep);
}

#catalogue_type-screen .panel_head {
  font:inherit;
  display:flex;
  align-items:center;
  gap:0.4rem;
  width:100%;
  padding:0;
  border:0;
  background-color:transparent;
  color:var(--color-text);
  cursor:pointer;
}

#catalogue_type-screen .panel_head h3 {
  margin:0;
  font-family:var(--font-airport);
  letter-spacing:0.14em;
  font-weight:bold;
  color:var(--color-primary-action);
}

/* The forward arrow is the icon set's chevron: it points along the way in while
   the panel is shut and down into it once it is open, which is the one thing
   the reader has to be told about a disclosure. */
#catalogue_type-screen .panel_head .icon {
  transition:transform 0.15s;
}

#catalogue_type-screen .panel_head[aria-expanded=true] .icon {
  transform:rotate(90deg);
}

#catalogue_type-screen .spec {
  margin-top:0.5rem;
}

#catalogue_type-screen .facts {
  display:grid;
  grid-template-columns:auto 1fr;
  gap:0.3rem 1rem;
}

#catalogue_type-screen .facts dt {
  color:var(--color-disabled-text);
}

#catalogue_type-screen .facts dd {
  text-align:right;
  font-variant-numeric:tabular-nums;
}

#catalogue_type-screen .hint {
  color:var(--color-disabled-text);
}

/* --------------------------------------------------------- the quantity */

#catalogue_type-screen .qty {
  display:flex;
  align-items:center;
  justify-content:space-between;
  gap:0.5rem;
  color:var(--color-disabled-text);
}

#catalogue_type-screen .qty_controls {
  display:flex;
  align-items:center;
  gap:0.5rem;
}

#catalogue_type-screen .qty_controls b {
  min-width:2.5rem;
  text-align:center;
  color:var(--color-text);
  font-variant-numeric:tabular-nums;
}

#catalogue_type-screen .qty_controls button {
  font:inherit;
  width:2rem;
  padding:0.1rem 0;
  line-height:1;
}

#catalogue_type-screen .totals {
  display:flex;
  flex-wrap:wrap;
  justify-content:space-between;
  gap:0.25rem 0.65rem;
  color:var(--color-disabled-text);
  font-variant-numeric:tabular-nums;
}

#catalogue_type-screen .totals b {
  color:var(--color-text);
}

/* ---------------------------------------------------------- the receipt */

#catalogue_type-screen>.foot {
  flex:none;

  display:flex;
  flex-direction:column;
  gap:0.6rem;
  padding-top:0.6rem;
  border-top:1px solid var(--color-border-deep);
}

#catalogue_type-screen>.foot button {
  align-self:center;
}

/* Two columns of one text size, so what tells a line from the one under it is
   weight and colour: the amounts are in the text colour against muted labels,
   and the total is the only bold thing on it. */
#catalogue_type-screen .receipt .line {
  display:flex;
  align-items:baseline;
  justify-content:space-between;
  gap:0.5rem;
  color:var(--color-disabled-text);
}

#catalogue_type-screen .receipt .label {
  display:flex;
  align-items:center;
  gap:0.4rem;
  min-width:0;
}

#catalogue_type-screen .receipt .amount {
  flex:none;
  color:var(--color-text);
  font-variant-numeric:tabular-nums;
}

/* Ruled off from the lines it sums: a total flush with its own parts reads as
   one more part. */
#catalogue_type-screen .receipt .total {
  margin-top:0.3rem;
  padding-top:0.3rem;
  border-top:1px solid var(--color-border-deep);
  font-weight:600;
}

/* A saving is only a saving against something, so the line it was taken off
   stays and says it is no longer what is paid. */
#catalogue_type-screen .receipt .superseded .amount {
  color:var(--color-disabled-text);
  text-decoration:line-through;
}

#catalogue_type-screen .receipt [data-test-key=negotiated] .amount,
#catalogue_type-screen .receipt .saving {
  color:var(--color-success);
}

/* The whole of the warning the client is entitled to give: every purchase is
   allowed and none is gated on the balance it leaves behind. */
#catalogue_type-screen .receipt .negative {
  color:var(--color-error);
}

#catalogue_type-screen [data-test=aircraft-catalogue-order-note] {
  color:var(--color-error);
  margin-top:0.5rem;
}

/* Every rule scoped under #catalogue-screen per CLAUDE.md. The list, its notes
   and the box a row is drawn in are not here -- the catalogue is an aircraft
   list and aircraft-list.css draws all three. What is left is the one thing
   only this screen has on a row. */

/* The size, in the pill the two lists spend on a band word. It is the one fact
   about an aeroplane that its name never carries, so it is drawn rather than
   read: condensed and tracked to sit as a mark against the figure beside it,
   and no font-size of its own -- every word on this screen is the body size,
   and what separates a line from the one under it is weight and colour. */
#catalogue-screen .chip {
  display:inline-block;
  margin-right:0.4rem;
  padding:0.05rem 0.4rem;
  border-radius:9px;
  background-color:var(--color-disabled);

  font-family:var(--font-airport);
  letter-spacing:0.1em;
  text-transform:uppercase;
  color:var(--color-text);
}

#catalogue-screen .price {
  flex:none;
  font-variant-numeric:tabular-nums;
}

/* Every rule scoped under #aircraft-screen per CLAUDE.md. */

/* The frame, the headline, the note and the tiles are hub.css's. */

#aircraft-screen .average {
  display:flex;
  flex-direction:column;
  gap:0.35rem;
}

#aircraft-screen .average_top {
  display:flex;
  align-items:baseline;
  justify-content:space-between;
  gap:0.5rem;
}

#aircraft-screen .label {
  font-family:var(--font-airport);
  letter-spacing:0.14em;
  text-transform:uppercase;
  color:var(--color-disabled-text);
}

/* The whole fleet's figure rather than one row's, so it is read at the size of
   the headline above it and not the 0.6875rem shared.css draws a row's word at. */
#aircraft-screen .average .cond_word {
  font-size:1rem;
}

/* Nothing resets a paragraph's own margin, and the body already spaces what is
   in it, so both of these lines say so rather than being spaced twice. */
#aircraft-screen .staffing,
#aircraft-screen .grounded {
  display:flex;
  align-items:center;
  gap:0.4rem;
  margin:0;
  color:var(--color-disabled-text);
}

#aircraft-screen .staffing .icon,
#aircraft-screen .grounded .icon {
  flex:none;
}

/* A mechanic short is the only reason the line is on the screen at all, so it is
   the only state that is coloured. */
#aircraft-screen .staffing_short,
#aircraft-screen .staffing_short b {
  color:var(--color-warning);
}

/* The red the "Needs work" band is drawn in rather than the staffing line's
   amber: a grounded airframe is one whose bar the reader has just seen in it. */
#aircraft-screen .grounded_warn,
#aircraft-screen .grounded_warn b {
  color:var(--color-error);
}

/* The market is aircraft coming in rather than going out, and the take-off
   drawing read backwards is the only landing the icon set has. */
#aircraft-screen .tile_market .icon {
  transform:scaleX(-1);
}

/*
 * The identity shared.mjs draws for a pair: the pair, how it is held, and how far.
 *
 * Scoped to the game layout, as aircraft/shared.css is and for the same reason:
 * the identity heads a row on every list of routes and the route screen above its charts,
 * and this is the narrowest hand-written id above both.
 */

#layout_game-layout .route_pair {
  display:flex;
  align-items:baseline;
  gap:0.5rem;
  font-weight:bold;
}

/* The distance is a fact about the pair rather than the right, so it qualifies
   the identity the way the size qualifies a model on the aircraft list. */
#layout_game-layout .route_pair .aside {
  font-weight:normal;
  color:var(--color-disabled-text);
}

/* The word saying how a right is held. */
#layout_game-layout .route_badge {
  font-family:var(--font-airport);
  font-size:0.75rem;
  font-weight:bold;
  letter-spacing:0.14em;
  text-transform:uppercase;
  line-height:1.5;
  padding:0 0.375rem;
  border:1px solid currentColor;
  border-radius:3px;
  white-space:nowrap;
}

#layout_game-layout .route_badge_offer { color:var(--color-primary-action); }
#layout_game-layout .route_badge_rented { color:var(--color-action-border); }
#layout_game-layout .route_badge_locked { color:var(--color-error); }

/*
 * The lists of rights and pairs: Your routes, Offers and Rent draw the same
 * controls, the same notes and the same row, all out of shared.mjs.
 */

#layout_game-layout .route_filters {
  display:flex;
  gap:0.5rem;
}

#layout_game-layout .route_filters select {
  flex:1;
  min-width:0;
}

#layout_game-layout .route_list {
  list-style:none;
}

#layout_game-layout .route_note {
  padding:1.5rem 0;
  text-align:center;
  color:var(--color-disabled-text);
}

#layout_game-layout .route_label {
  font-family:var(--font-airport);
  letter-spacing:0.14em;
  text-transform:uppercase;
  color:var(--color-disabled-text);
}

/* The aircraft list's box: a row opens the route. */
#layout_game-layout .route_row {
  display:flex;
  flex-direction:column;
  gap:0.4rem;

  margin-bottom:0.6rem;
  padding:0.65rem 0.75rem;
  border:1px solid var(--color-border-deep);
  border-radius:7px;
  background-color:var(--color-background-deep);
  cursor:pointer;
}

#layout_game-layout .route_row:last-child {
  margin-bottom:0;
}

#layout_game-layout .route_row:hover {
  border-color:var(--color-action-border);
}

#layout_game-layout .route_row_top {
  display:flex;
  flex-wrap:wrap;
  align-items:baseline;
  justify-content:space-between;
  gap:0.5rem;
}

#layout_game-layout .route_days,
#layout_game-layout .route_figure,
#layout_game-layout .route_fee {
  flex:none;
  font-variant-numeric:tabular-nums;
}

/* What a right costs to take or to hold, in the colour money is asked for in. */
#layout_game-layout .route_fee {
  color:var(--color-primary-action);
}

#layout_game-layout .route_days_ending {
  color:var(--color-warning);
}

#layout_game-layout .route_facts {
  display:flex;
  flex-wrap:wrap;
  align-items:center;
  justify-content:space-between;
  gap:0.25rem 0.65rem;

  color:var(--color-disabled-text);
  font-variant-numeric:tabular-nums;
}

/* The offer's bar is its allowance being spent, and is drawn in the offer's own
   colour. A rental's is its term, coloured by the verdict under it. */
#layout_game-layout .route_bar_offer { color:var(--color-primary-action); }
#layout_game-layout .route_bar_ok { color:var(--color-action-border); }
#layout_game-layout .route_bar_grace { color:var(--color-action-border); }
#layout_game-layout .route_bar_at-risk { color:var(--color-warning); }
#layout_game-layout .route_bar_losing { color:var(--color-error); }

#layout_game-layout .route_term_at-risk { color:var(--color-warning); }
#layout_game-layout .route_term_losing { color:var(--color-error); }

/* Every rule scoped under #route-screen per CLAUDE.md. The pair and its badge
   are shared.css's. */

/* The cabins are ordered, so they are one amber ramp rather than three hues:
   checked as an ordinal ramp against the deep background. */
#route-screen {
  --cabin-f:#f6d08a;
  --cabin-j:var(--color-primary-action);
  --cabin-y:#a86c10;
}

/* Fills the screen body, so the action bar is held at its foot while the route
   scrolls above it: hub.css's arrangement. */
#route-screen {
  height:100%;

  display:flex;
  flex-direction:column;
  gap:1rem;
}

#route-screen>.body {
  flex:1;
  min-height:0;
  overflow:auto;

  display:flex;
  flex-direction:column;
  gap:1rem;
}

#route-screen .note {
  padding:1.5rem 0;
  text-align:center;
  color:var(--color-disabled-text);
}

#route-screen .head {
  display:flex;
  flex-direction:column;
  gap:0.25rem;
}

#route-screen .busyness {
  display:flex;
  flex-wrap:wrap;
  gap:0.25rem 1.5rem;
}

#route-screen .end {
  display:flex;
  align-items:baseline;
  gap:0.5rem;
}

#route-screen .code,
#route-screen .label {
  font-family:var(--font-airport);
  letter-spacing:0.14em;
  text-transform:uppercase;
  color:var(--color-disabled-text);
}

#route-screen .cabins,
#route-screen .chart {
  display:flex;
  flex-direction:column;
  gap:0.5rem;
}

#route-screen .chart_head {
  display:flex;
  flex-wrap:wrap;
  align-items:baseline;
  justify-content:space-between;
  gap:0 0.5rem;
}

#route-screen .unit {
  font-size:0.8125rem;
  color:var(--color-disabled-text);
}

/* The end the hour curve is drawn for, named and then chosen. Wraps rather than
   squeezes: two codes and the words in front of them are wider than a narrow
   handset at the type the rest of the chart is set in. */
#route-screen .departures {
  display:flex;
  flex-wrap:wrap;
  align-items:center;
  gap:0.35rem 0.6rem;
}

#route-screen .mix {
  display:flex;
  gap:2px;
  height:0.75rem;
}

/* Grown by its share alone, so a basis of nothing keeps the widths exact. */
#route-screen .mix span {
  flex-basis:0;
  border-radius:2px;
}

/* The name, then one column a figure: the share, the day's passengers once the
   size is known, and the fare on a pair the game flies. */
#route-screen .cabin_table_1 { grid-template-columns:minmax(0, 1fr) auto; }
#route-screen .cabin_table_2 { grid-template-columns:minmax(0, 1fr) repeat(2, auto); }
#route-screen .cabin_table_3 { grid-template-columns:minmax(0, 1fr) repeat(3, auto); }

#route-screen .cabin_table {
  display:grid;
  font-size:0.9375rem;
  gap:0.1rem 1rem;
  font-variant-numeric:tabular-nums;
}

#route-screen .column {
  text-align:right;
  color:var(--color-disabled-text);
}

#route-screen .figure {
  text-align:right;
}

#route-screen .cabin_name {
  display:flex;
  align-items:center;
  gap:0.4rem;
}

#route-screen .swatch {
  flex:none;
  width:0.625rem;
  height:0.625rem;
  border-radius:2px;
}

#route-screen .cabin_f { background-color:var(--cabin-f); }
#route-screen .cabin_j { background-color:var(--cabin-j); }
#route-screen .cabin_y { background-color:var(--cabin-y); }

#route-screen .hire {
  line-height:1.5;
  color:var(--color-disabled-text);
}

/* What a right would be bought on: the offer's, or the rent's. */
#route-screen .terms {
  display:flex;
  flex-direction:column;
  gap:0.1rem;
  padding:0.65rem 0.75rem;
  border:1px solid var(--color-border-deep);
  border-radius:7px;
  background-color:var(--color-background-deep);
  font-variant-numeric:tabular-nums;
}

#route-screen .terms_head {
  display:flex;
  flex-wrap:wrap;
  align-items:baseline;
  justify-content:space-between;
  gap:0 0.5rem;
}

#route-screen .terms_rule {
  color:var(--color-disabled-text);
}

#route-screen .fee {
  color:var(--color-primary-action);
  font-variant-numeric:tabular-nums;
}

#route-screen .action_bar {
  display:flex;
  align-items:center;
  justify-content:space-between;
  gap:0.75rem;
  padding-top:0.75rem;
  border-top:1px solid var(--color-border-deep);
  color:var(--color-disabled-text);
}

/* The rule above would otherwise beat the hidden attribute's own display:none */
#route-screen .action_bar[hidden] {
  display:none;
}

#route-screen .action_bar button {
  flex:none;
}

#route-screen .sale_cap {
  color:var(--color-warning);
}

/* Every rule scoped under #your_routes-screen per CLAUDE.md. The controls, the
   notes and the row are shared.css's. */

#your_routes-screen {
  display:flex;
  flex-direction:column;
  gap:1rem;
}

#your_routes-screen .lockouts {
  display:flex;
  flex-direction:column;
  gap:0.6rem;
}

/* The rule above would otherwise beat the hidden attribute's own display:none */
#your_routes-screen .lockouts[hidden] {
  display:none;
}

/* Every rule scoped under #rent-screen per CLAUDE.md. The controls, the notes
   and the row are shared.css's. */

#rent-screen {
  display:flex;
  flex-direction:column;
  gap:1rem;
}

/* What the list is, and the unit every figure on it is in. */
#rent-screen .head {
  display:flex;
  flex-wrap:wrap;
  align-items:baseline;
  justify-content:space-between;
  gap:0 0.5rem;
}

/* The rule above would otherwise beat the hidden attribute's own display:none */
#rent-screen .head[hidden] {
  display:none;
}

#rent-screen .unit {
  font-size:0.8125rem;
  color:var(--color-disabled-text);
}

/* Every rule scoped under #offers-screen per CLAUDE.md. The controls, the notes
   and the row are shared.css's. */

#offers-screen {
  display:flex;
  flex-direction:column;
  gap:1rem;
}

/* hub.css's note, which the header is drawn as: the offers held against the cap. */
#offers-screen .held {
  display:flex;
  flex-direction:column;
  gap:0.1rem;
  padding:0.75rem 0.85rem;
  border:1px solid var(--color-border-deep);
  border-left-width:3px;
  border-radius:5px;
  background-color:var(--color-background-deep);
  color:var(--color-disabled-text);
  font-variant-numeric:tabular-nums;
}

/* The rule above would otherwise beat the hidden attribute's own display:none */
#offers-screen .held[hidden] {
  display:none;
}

#offers-screen .held strong,
#offers-screen .held_pair {
  color:var(--color-text);
}

#offers-screen .held_head,
#offers-screen .held_line {
  display:flex;
  flex-wrap:wrap;
  justify-content:space-between;
  gap:0 0.5rem;
}

#offers-screen .held_ending {
  color:var(--color-warning);
}

#offers-screen .held_cap {
  border-left-color:var(--color-warning);
}

#offers-screen .held_cap .held_count {
  color:var(--color-warning);
}

#offers-screen .held_rule {
  margin-top:0.25rem;
}

/* Every rule scoped under #routes-screen per CLAUDE.md. The frame, the headline,
   the note and the tiles are hub.css's. */

#routes-screen .split {
  display:grid;
  grid-template-columns:1fr auto;
  gap:0.1rem 0.85rem;
  align-items:baseline;

  padding:0.75rem 0;
  border-top:1px solid var(--color-border-deep);
  border-bottom:1px solid var(--color-border-deep);
}

#routes-screen .split dt {
  font-weight:normal;
  color:var(--color-disabled-text);
}

#routes-screen .split dd {
  text-align:right;
  font-variant-numeric:tabular-nums;
}

/* A right about to be lost, one line each. Nothing resets a paragraph's own
   margin, and the body already spaces what is in it. */
#routes-screen .alert {
  display:flex;
  align-items:center;
  gap:0.4rem;
  margin:0;
  font-variant-numeric:tabular-nums;
}

#routes-screen .alert .icon {
  flex:none;
}

#routes-screen .alert_offer-ending,
#routes-screen .alert_at-risk {
  color:var(--color-warning);
}

#routes-screen .alert_losing {
  color:var(--color-error);
}

/* Every rule scoped under #person_list-screen: the roster and the pool are the
   same list, so the one stylesheet draws both. */

#person_list-screen {
  display:flex;
  flex-direction:column;
  gap:1rem;
}

#person_list-screen .filters {
  display:flex;
  gap:0.5rem;
}

#person_list-screen .filters select {
  flex:1;
  min-width:0;
}

#person_list-screen .list {
  list-style:none;
}

#person_list-screen .note {
  padding:1.5rem 0;
  text-align:center;
  color:var(--color-disabled-text);
}

#person_list-screen .row {
  display:flex;
  align-items:flex-start;
  gap:0.6rem;
  padding:0.6rem 0.15rem;
  border-bottom:1px solid var(--color-border-deep);
  cursor:pointer;
}

#person_list-screen .row:last-child {
  border-bottom:none;
}

#person_list-screen .row pt-face {
  font-size:2.5rem;
}

#person_list-screen .row:hover {
  background-color:rgba(37, 112, 121, 0.18);
}

#person_list-screen .row_body {
  flex:1;
  min-width:0;
}

#person_list-screen .row_top {
  display:flex;
  align-items:baseline;
  justify-content:space-between;
  gap:0.6rem;
}

#person_list-screen .row_name {
  font-weight:bold;
  white-space:nowrap;
  overflow:hidden;
  text-overflow:ellipsis;
}

#person_list-screen .row_pay {
  flex:none;
  white-space:nowrap;
  font-variant-numeric:tabular-nums;
}

/* The "asks" on a candidate: the cell still carries only the money. */
#person_list-screen .row_pay em {
  font-style:normal;
  font-size:0.6875rem;
  color:var(--color-disabled-text);
  margin-right:0.15rem;
}

#person_list-screen .row_job {
  display:flex;
  align-items:center;
  gap:0.4rem;
  min-width:0;
  color:var(--color-disabled-text);
}

#person_list-screen .row_job span {
  white-space:nowrap;
  overflow:hidden;
  text-overflow:ellipsis;
}

/* The job name above is what the ellipsis is for; the aeroplane is a glyph and
   is neither squeezed nor cut when the name runs long. */
#person_list-screen .row_job .row_assigned {
  display:flex;
  flex:none;
  overflow:visible;
}

/* The mood is a line of a row here, rather than the word in a run of text that
   shared.css draws it as. */
#person_list-screen .mood {
  display:block;
}

/* The advisor's third line: market range, verdict, how long they have left. */
#person_list-screen .row_advisor {
  display:flex;
  flex-wrap:wrap;
  align-items:baseline;
  gap:0.35rem;
  margin-top:0.15rem;
  color:var(--color-disabled-text);
  font-variant-numeric:tabular-nums;
}

/* Every rule scoped under #personnel-screen per CLAUDE.md. */

/* The frame, the headline and the tiles are hub.css's. */

#personnel-screen .screen_personnel_personnel-hub_split {
  display:grid;
  grid-template-columns:auto 1fr auto;
  gap:0.1rem 0.85rem;
  align-items:baseline;

  padding:0.75rem 0;
  border-top:1px solid var(--color-border-deep);
  border-bottom:1px solid var(--color-border-deep);
}

#personnel-screen .screen_personnel_personnel-hub_split dt {
  font-weight:normal;
  color:var(--color-disabled-text);
}

#personnel-screen .screen_personnel_personnel-hub_split dd {
  text-align:right;
  font-variant-numeric:tabular-nums;
}

#personnel-screen .screen_personnel_personnel-money {
  min-width:5.5rem;
}

#personnel-screen .screen_personnel_personnel-nil {
  color:var(--color-disabled-text);
}

/* One block in two states: the advisor, or the space where one is not. */
#personnel-screen .screen_personnel_personnel-advisor_box {
  display:flex;
  gap:0.7rem;
  padding:0.7rem 0.8rem;
  border:1px solid var(--color-border-deep);
  border-radius:7px;
  background-color:var(--color-background-deep);
}

#personnel-screen .screen_personnel_personnel-advisor_box pt-face {
  font-size:2.5rem;
}

#personnel-screen .screen_personnel_personnel-advisor_box_body {
  flex:1;
  min-width:0;
}

#personnel-screen .screen_personnel_personnel-advisor_box h3 {
  font-family:var(--font-airport);
  letter-spacing:0.14em;
  font-weight:bold;
  color:var(--color-primary-action);
  margin:0 0 0.15rem;
  line-height:1.2;
}

#personnel-screen .screen_personnel_personnel-advisor_box p {
  margin:0;
  line-height:1.5;
  color:var(--color-disabled-text);
}

#personnel-screen .screen_personnel_personnel-advisor_box_name {
  display:flex;
  align-items:center;
  gap:0.45rem;
}

#personnel-screen .screen_personnel_personnel-link_button {
  font:inherit;
  display:block;
  padding:0;
  border:0;
  background:none;
  color:var(--color-primary-action);
  text-decoration:underline;
  text-underline-offset:2px;
  cursor:pointer;
}

/* Both are inset from what they cover, so the board is still read around them,
   and hold whatever they carry rather than growing the row they were given.

   The phone is held upright here as it is on a mobile: it takes the board's
   height, and its width is capped to a phone's and centred in what is left, so
   a wider screen gives it more board to sit on rather than a wider phone.
   Sized border-box, since the cap is the phone's whole width while both of
   these carry a padding and a border inside it.

   The case around them is a border and not a ring of shadow, because it is what
   the phone is taken hold of and moved by, and a pointer goes straight through
   a shadow. The shadow the handset casts on the board, and the metal rim that
   keeps the black from reading as a hole cut in it, are what the margin has to
   be wide enough for */
#layout_game-layout>main>nav,
#layout_game-layout>main>section {
  box-sizing:border-box;
  width:min(28rem, 100%);
  margin:2rem auto;
  min-height:0;
  overflow:hidden;

  /* Where the phone has been put down: written on the layout, since the menu
     and the screen covering it are the one handset and are moved together.
     Offset rather than transformed -- a transform makes the element the
     containing block of anything fixed inside it */
  position:relative;
  left:var(--phone-x, 0px);
  top:var(--phone-y, 0px);

  /* Nothing in the palette: black plastic, the rim of metal around it and the
     edge of the screen inside are not colours the game has a use for elsewhere */
  border:0.875rem solid #050505;
  border-radius:2rem;
  box-shadow:
    inset 0 0 0 1px var(--color-border-deep),
    0 0 0 1px #3c4249,
    0 0.75rem 1.5rem rgba(0, 0, 0, 0.5);

  cursor:grab;

  /* Read by the drag, so the breakpoint that decides whether the phone is a
     thing on a board or is the screen itself is not written out a second time
     in the layout it belongs to */
  --phone-draggable:1;
}

/* The case and the surface around what the phone is showing are the handle;
   what is drawn inside the phone is the phone's, and keeps its own cursor */
#layout_game-layout>main>nav>*,
#layout_game-layout>main>section>* {
  cursor:auto;
}

#layout_game-layout[data-phone-dragging]>main>nav,
#layout_game-layout[data-phone-dragging]>main>section {
  cursor:grabbing;
}

/* The menu is a screen of the phone, so it is drawn as the same surface as the
   screen that opens over it, which section in application.css already carries */
#layout_game-layout>main>nav {
  background-color:var(--color-background-main);
  padding:1rem 1.5rem;

  /* The close button is as tall as it needs to be and the menu takes everything
     left over, so the corner the button is in cannot be scrolled away from */
  display:flex;
  flex-direction:column;
  gap:1rem;
}

/* Two columns however many buttons there are, and three rows of them on the
   phone at once: the rows are cut from the height the menu was given rather
   than measured from what is in them, so six buttons fill the phone exactly and
   a seventh is scrolled down to rather than squeezing the six */
#layout_game-layout>main>nav>.apps {
  flex:1;
  min-height:0;
  overflow:auto;

  --menu-gap:1rem;

  display:grid;
  grid-template-columns:repeat(2, auto);
  grid-auto-rows:calc((100% - 2 * var(--menu-gap)) / 3);
  align-items:center;
  gap:var(--menu-gap);

  /* Columns no wider than the buttons in them, with everything left over shared
     out around them, so the space to the left of a row, between its two buttons
     and to its right is the one space. Columns of 1fr take that space
     themselves and leave the outer two half the width of the middle */
  justify-content:space-evenly;
}

/* A button on the menu is an app icon on a home screen: the artwork with its
   wording under it, and none of the chrome a button is otherwise given, so what
   is read is the drawing rather than the box around it */
#layout_game-layout>main>nav>.apps>[data-phone-screen] {
  display:flex;
  flex-direction:column;
  align-items:center;
  gap:0.5rem;

  /* The icon and its wording are what is pressed, so the button is left as wide
     as the wider of the two rather than filled out to the column it is placed
     in, which would leave most of the target on empty board. Nothing is padded
     to the sides for the same reason */
  justify-self:center;
  padding:0.5rem 0;

  border:0;
  background-color:transparent;
  font-size:0.875rem;
  line-height:1.25;
  text-align:center;
  cursor:pointer;
}

#layout_game-layout>main>nav>.apps>[data-phone-screen]:hover {
  color:var(--color-hover);
}

/* The width is written out rather than left to the drawing's own proportions:
   an image given nothing but a height still offers its full drawn width to the
   column it is measured in, and a menu of icons drawn 512 wide would then ask
   for a board several screens across */
#layout_game-layout>main>nav>.apps>[data-phone-screen]>img {
  width:4rem;
  height:4rem;
  max-width:100%;

  /* The corners the app icon is drawn with, so artwork given square edges is
     still cut to the same tile as the rest */
  border-radius:22%;
}

/* The phone is put down from its own corner, the way the screen it opens is
   closed from that one */
#layout_game-layout>main>nav>header {
  display:flex;
  justify-content:flex-end;
}

#layout_game-layout>main>nav>header>.icon_button {
  font-size:1.75rem;
}

/* Nothing on a desktop reaches the menu but the phone, so until it is picked up
   the menu is not on the screen at all */
#layout_game-layout:not([data-phone-open])>main>nav {
  display:none;
}

/* The screen scrolls in its body rather than as a whole, which leaves it a
   bottom edge that stays put: what a screen pins something to, the way the
   person screen keeps its prev/next in reach of a thumb. The header stays put
   with it rather than scrolling away, which is where it was meant to be. */
#layout_game-layout>main>section {
  display:flex;
  flex-direction:column;
}

#layout_game-layout>main>section>.screen_body {
  flex:1;
  min-height:0;
  overflow:auto;
}

/* Header inside the screen, with the title and the close button */
#layout_game-layout>main>section>header {
  display:flex;
  align-items:center;
  justify-content:space-between;
  gap:1rem;

  margin-bottom:1rem;
}

/* Sized against the screen's heading rather than its text, the way the game
   list sets its own header actions */
#layout_game-layout>main>section>header>.icon_button {
  font-size:1.75rem;
}

/* A screen's own control in the header, pushed to the back button's side so
   the heading keeps the left. Empty on every screen but the board, and an
   empty box must not spend the header's gap. */
#layout_game-layout>main>section>header>.screen_header {
  margin-left:auto;
}

#layout_game-layout>main>section>header>.screen_header:empty {
  display:none;
}

/*
 * Mobile -> No board, the menu takes the screen, and a phone screen opens over it.
 *
 * The breakpoint is layout-game.css's: a phone turned sideways is around
 * 430px tall before the browser's own bars, a tablet at least 768px, so the
 * line between them is drawn at 30rem, and the same query has to be repeated
 * here rather than shared since a media block cannot be split across files.
 */
@media (max-width: 40rem), (max-height: 30rem) {
  /* The screen is the phone: there is no board to move it around and no case to
     draw around it, since it is already being held in one. A phone turned
     sideways is also wider than the cap above, which would leave it a column of
     a screen it is meant to fill */
  #layout_game-layout>main>nav,
  #layout_game-layout>main>section {
    width:auto;
    margin:0.5rem;

    left:auto;
    top:auto;

    border:1px solid var(--color-border-deep);
    border-radius:1rem;
    box-shadow:none;

    cursor:auto;
    --phone-draggable:0;
  }

  /* The screen is the phone, so the menu is always up and the state the phone
     button keeps says nothing here. Written against the selector that hides it
     so it wins on order instead of losing on specificity */
  #layout_game-layout:not([data-phone-open])>main>nav {
    display:flex;
  }

  /* There is no board to put a phone in front of, so there is nothing to put
     the phone down onto. The button that would do it is the footer's to place,
     so layout-game.css is what folds it away; the menu's own close button is
     this file's */
  #layout_game-layout>main>nav>header {
    display:none;
  }

  /* The menu covers the page rather than the board, so it is left as the page:
     drawn as a surface it would be the same one as the screen opening over it,
     and the two would not be told apart */
  #layout_game-layout>main>nav {
    background-color:transparent;
    border-color:transparent;

    /* Nothing is held back at the sides, so the whole of the screen is there
       for the menu to share out between its columns */
    padding:1rem 0;
  }
}

/*
 * The briefing morning.mjs draws, under the root id the dialog itself carries.
 */

/* application.css's dialog box without its padding, so the head, the body and
   the foot can each carry their own and the body can scroll between the other
   two. Wider than a confirmation's 26rem because the report is a column of
   short lines, and the narrower box breaks the longer ones */
#morning-layout {
  width:min(32rem, calc(100% - 2rem));
  padding:0;
  overflow:hidden;
}

/* The head and the foot are surfaces rather than ruled regions, the way the
   game's own header and footer are told from the board. That leaves the
   balance's rule as the only line in the briefing, so a line here means one
   thing: the sum of what is above it */
#morning-layout>.head,
#morning-layout>.foot {
  background-color:var(--color-background-main);
}

/* Short at the bottom by the h2's own 0.25rem margin, which would otherwise sit
   the date high in its bar */
#morning-layout>.head {
  padding:1.125rem 1.5rem 0.875rem 1.5rem;
}

#morning-layout>.body {
  max-height:min(60vh, 30rem);
  overflow:auto;
  padding:1rem 1.5rem;
}

#morning-layout>.foot {
  display:flex;
  justify-content:flex-end;
  padding:1rem 1.5rem;
}

#morning-layout .group + .group {
  margin-top:1.25rem;
}

/* Size and weight rather than the uppercase eyebrow .h3 draws: the date above is
   already uppercase and condensed, and a second run of that inside the body
   reads as more chrome instead of as the level below it. Nothing else in the
   briefing is set above the body's own size, which is what leaves size enough */
#morning-layout .group>.group_title {
  margin-bottom:0.4rem;
  font-size:1.1875rem;
  font-weight:bold;
  line-height:1.4;
}

/* Read down the amount column, so the label takes the space and the figure is
   what the eye runs down */
#morning-layout .bill {
  display:flex;
  align-items:baseline;
  gap:0.75rem;
  padding:0.25rem 0;
}

#morning-layout .bill>.label {
  flex:1;
  min-width:0;
}

#morning-layout .bill>.amount {
  font-variant-numeric:tabular-nums;
  color:var(--color-error);
}

/* The one row in the statement that is money in. Coloured rather than only
   signed, because the minus on every other row is one character wide and the
   takings are the row a flying airline reads the statement for */
#morning-layout .bill>.amount.credit {
  color:var(--color-success);
}

/* Ruled off from the bills it follows: a figure flush with them reads as one
   more charge */
#morning-layout .bill.total {
  margin-top:0.5rem;
  padding-top:0.5rem;
  border-top:1px solid var(--color-border-deep);
  font-weight:bold;
}

/* Two rows close the statement -- what the night came to, then where it leaves
   the airline -- and only the first of them is ruled. The rule means "the sum of
   everything above it", which is true of the night's profit and not of the
   balance: ruling the balance as well would claim it was a sum of the profit */
#morning-layout .bill.total + .bill.total {
  margin-top:0;
  padding-top:0.25rem;
  border-top:0;
}

#morning-layout .bill.total>.amount {
  color:var(--color-success);
}

/* Nothing in the game refuses a charge for want of money, so saying so is the
   whole of what the briefing can do about a balance under zero */
#morning-layout .bill.total>.amount.negative {
  color:var(--color-error);
}

#morning-layout .line {
  display:flex;
  align-items:flex-start;
  gap:0.625rem;
  padding:0.3rem 0;
}

#morning-layout .line>.icon {
  flex:0 0 auto;
  margin-top:0.3rem;
  font-size:1.125rem;
  color:var(--color-disabled-text);
}

#morning-layout .line>.text>.detail {
  display:block;
  color:var(--color-disabled-text);
  line-height:1.4;
}

/* What the night leaves to be done rather than read -- an airframe under the
   line, a rental the term took away -- which is why these and nothing else in
   the report are drawn as a box rather than as a line.

   A grid and not a row, because a revoked rental carries the flights that came
   off the board with it: the icon, the text and the button are the first row and
   the list is a second one under the text, clear of the icon's column */
#morning-layout .urgent {
  display:grid;
  grid-template-columns:auto minmax(0, 1fr) auto;
  align-items:center;
  column-gap:0.75rem;
  margin-bottom:0.5rem;
  padding:0.625rem 0.75rem;
  border:1px solid var(--color-destructive-action-border);
  border-radius:0.4375rem;

  /* Mixed from the destructive colour rather than written out as a tint of its
     own, so the box cannot drift away from the border above it */
  background-color:color-mix(in srgb, var(--color-destructive-action) 12%, transparent);
}

#morning-layout .urgent>.icon {
  font-size:1.25rem;
  color:var(--color-error);
}

#morning-layout .urgent>.text {
  min-width:0;
}

#morning-layout .urgent>.text>strong {
  display:block;
  line-height:1.3;
}

#morning-layout .urgent>.text>span {
  color:var(--color-disabled-text);
  line-height:1.4;
}

/* Under the text and out to the button's edge, so a long flight name has the
   whole box to run in rather than the text column it is explained from */
#morning-layout .urgent>.ended {
  grid-column:2 / -1;
  margin-top:0.4rem;
  color:var(--color-disabled-text);
  line-height:1.4;
}

#morning-layout .urgent>.ended>ul {
  margin:0.15rem 0 0 0;
  padding-left:1.1rem;
}

/* Tabular, because the times are what the eye runs down when three of them are
   listed together */
#morning-layout .urgent>.ended>ul>li {
  font-variant-numeric:tabular-nums;
}

#morning-layout .nothing {
  padding:0.25rem 0;
  color:var(--color-disabled-text);
}

pt-balance {
  /* How far the delta's right edge is set in from this element's own. A caller
     that draws a box around a balance gives it that box's padding, so the delta
     ends where the last digit of the figure does */
  --balance-delta-inset:0;

  /* What the delta is hung off. Inline-block so a border drawn around a balance
     is sized by the figure rather than by the line it sits on */
  position:relative;
  display:inline-block;

  color:var(--color-success);
}

pt-balance.negative {
  color:var(--color-error);
}

/* Above the figure rather than beside it, where a phone has no room, and out of
   the flow so the figure counting underneath it never moves */
pt-balance>.delta {
  position:absolute;
  right:var(--balance-delta-inset);
  bottom:calc(100% + 0.125rem + var(--delta-stack, 0) * 1.25em);

  font-size:0.75em;
  font-weight:600;
  white-space:nowrap;

  /* It passes over whatever is above it on the way up, and is nothing to click */
  pointer-events:none;

  /* Stated rather than inherited: money coming in is green over a balance that
     is itself in the red */
  color:var(--color-success);

  /* `forwards` holds the last frame, which is the transparent one -- without it
     the delta flashes back to full opacity for the frame between the animation
     ending and the handler taking it off */
  animation:pt-balance-delta-rise 1100ms ease-out forwards;
}

pt-balance>.delta.negative {
  color:var(--color-error);
}

/* Written by the stylesheet so the element's own text stays the balance and
   nothing reading it picks up a delta that happens to be passing over */
pt-balance>.delta::before {
  content:attr(data-amount);
}

@keyframes pt-balance-delta-rise {
  0% { opacity:0; transform:translateY(0.5rem); }
  18% { opacity:1; transform:translateY(0); }
  70% { opacity:1; transform:translateY(-0.5rem); }
  100% { opacity:0; transform:translateY(-1.25rem); }
}

/* The amount is the news, so the delta stays and only its travel goes -- and it
   still has to end on an animation, since that is what takes it off */
@keyframes pt-balance-delta-hold {
  0% { opacity:0; }
  18% { opacity:1; }
  70% { opacity:1; }
  100% { opacity:0; }
}

@media (prefers-reduced-motion: reduce) {
  pt-balance>.delta {
    animation-name:pt-balance-delta-hold;
  }
}

pt-flapper {
  --flap-gap:0.1875rem;
  --flap-stagger:75ms; /* how long after its neighbour a tile starts turning */
  --flap-duration:900ms; /* the run of three turns, not one of them */

  /* Drawn from the palette in colors.css, lightest to darkest, so the tiles
     sit in the same family as the buttons and inputs */
  --flap-top:var(--color-action-border); /* a tile is lit above the seam... */
  --flap-mid:var(--color-action); /* ...turns over at it... */
  --flap-bottom:var(--color-border-deep); /* ...and is shaded below it */
  --flap-seam:var(--color-background-deep); /* the hinge line across each tile */

  /* How deep the turn looks. Each tile carries its own, so every one of them is
     seen straight on -- a perspective here would put a single vanishing point
     over the word and tip the tiles at the ends into turning diagonally */
  --flap-perspective:400px;

  display: flex;
  flex-wrap: wrap;
  gap:var(--flap-gap);
  font-family:var(--font-airport);
  text-transform: uppercase;
  /* letter-spacing not needed, done by -flap-gap */
  letter-spacing: 0;
}

pt-flapper[still]>.flap {
  animation: none;
}

pt-flapper>.flap {
  display: inline-block;
  min-width:0.8em;
  padding:0.16em 0.08em 0.2em;
  text-align: center;
  border-radius:3px;
  box-shadow:0 1px 2px rgba(0, 0, 0, 0.5);
  /* Lit above the hinge, shaded below it */
  background-image:linear-gradient(
    to bottom,
    var(--flap-top) 0,
    var(--flap-top) calc(50% - 1px),
    var(--flap-seam) calc(50% - 1px),
    var(--flap-seam) 50%,
    var(--flap-mid) 50%,
    var(--flap-bottom) 100%
  );
  /* Each turn eases in: a card is falling, so it picks up speed on the way down.

     `both` rather than `backwards`, for the end of it rather than the start: the
     last frame holds a transform, which puts the tile on its own layer and draws
     it snapped to whole pixels. Dropping that transform at the end would put the
     tile back at its real position, which is half a pixel out -- and the hinge,
     laid at 50% of a tile 38.08px tall, would smear across two rows of pixels
     and read as a gap. Holding the last frame holds the tile where it was. */
  animation:flap-in var(--flap-duration) ease-in both;
  /* Its place in the word is when it starts, so the row sets left to right */
  animation-delay:calc(var(--flap-index, 0) * var(--flap-stagger));
}

/* The word break between two runs of tiles: a gap in the board, not a panel */
pt-flapper>.flap-space {
  min-width:0.35em;
  background-image: none;
  box-shadow: none;
}

@keyframes flap-in {
  0% { transform: perspective(var(--flap-perspective)) rotateX(-90deg); opacity: 0; }
  26% { transform: perspective(var(--flap-perspective)) rotateX(0); opacity: 1; }
  26.01% { transform: perspective(var(--flap-perspective)) rotateX(-90deg); }
  58% { transform: perspective(var(--flap-perspective)) rotateX(0); }
  58.01% { transform: perspective(var(--flap-perspective)) rotateX(-90deg); animation-timing-function: ease-out; }
  92% { transform: perspective(var(--flap-perspective)) rotateX(6deg); animation-timing-function: ease-out; }
  100% { transform: perspective(var(--flap-perspective)) rotateX(0); }
}

@media (prefers-reduced-motion: reduce) {
  pt-flapper>.flap {
    animation: none;
  }
}

/* The board is a screen and not a document: it is given the height of the
   viewport rather than a floor of it, so the bars are always where they were
   left and anything too tall scrolls inside its own box. A floor lets a child
   that cannot be sized push the footer off the bottom of the screen */
#layout_game-layout {
  box-sizing:border-box;
  height:100dvh;

  /* One column, so the rows are filled in the order the bars are written and
     nothing has to be named to be placed.

     The board's row is floored at 0 rather than left at 1fr, which carries a
     minimum of whatever is in it: a canvas brings an intrinsic size and the
     menu brings its buttons, and either would grow the row and take the footer
     off the bottom of the screen with it */
  display:grid;
  grid-template-columns:1fr;
  grid-template-rows:auto minmax(0, 1fr) auto;
}

#layout_game-layout>header,
#layout_game-layout>footer {
  background-color:var(--color-background-main);
  padding:1rem 1.5rem;
}

/* The bars are the one surface wrapped around the board, so the only lines to
   draw are the ones cutting the board out of them. To the left and the right
   the board runs into the edge of the screen, where there is nothing to divide */
#layout_game-layout>header {
  border-bottom:1px solid var(--color-border-deep);

  /* The two outer columns are equal fractions of whatever the middle one does
     not take */
  display:grid;
  grid-template-columns:1fr auto 1fr;
  align-items:center;
  gap:1rem;
}

#layout_game-layout>header>.header_menu {
  position:relative;
  justify-self:end;
}

#layout_game-layout>header>.header_menu>.icon_button {
  font-size:1.5rem;
}

/* Anchored under the button that opens it rather than in the header's own
   grid flow, so it overlays the board instead of pushing it around.
   Positioned relative so the caret below can be placed off its edge. */
#layout_game-layout>header>.header_menu>ul {
  position:absolute;
  top:calc(100% + 0.625rem);
  right:0;
  z-index:1;
  margin:0;
  padding:0.375rem;
  list-style:none;

  min-width:11.5rem;

  background-color:var(--color-background-deep);
  border:1px solid var(--color-action-border);
  border-radius:0.625rem;
  box-shadow:0 0.75rem 1.75rem rgba(0, 0, 0, 0.45);
}

/* Points back at the button that opened the menu, so the two read as one thing */
#layout_game-layout>header>.header_menu>ul::before {
  content:"";
  position:absolute;
  top:-0.375rem;
  right:1.0625rem;
  width:0.75rem;
  height:0.75rem;

  background-color:var(--color-background-deep);
  border-left:1px solid var(--color-action-border);
  border-top:1px solid var(--color-action-border);
  transform:rotate(45deg);
}

#layout_game-layout>header>.header_menu>ul>li>button {
  display:flex;
  align-items:center;
  gap:0.625rem;
  width:100%;
  padding:0.5625rem 0.6875rem;
  border:none;
  border-radius:0.4375rem;
  background:none;

  font:inherit;
  text-align:left;
  cursor:pointer;
}

#layout_game-layout>header>.header_menu>ul>li>button:hover {
  background-color:var(--color-action);
}

#layout_game-layout>header>.header_menu>ul>li>button .icon {
  flex:0 0 auto;
  color:var(--color-primary-action);
}

#layout_game-layout>header>.header_airline {
  display:flex;
  flex-direction:column;
  align-items:center;

  min-width:0;
  overflow:hidden;
}

#layout_game-layout>header>.header_airline>h2 {
  max-width:100%;
  margin:0;

  overflow:hidden;
  text-overflow:ellipsis;
  white-space:nowrap;
}

#layout_game-layout>header>.header_airline>span {
  font-size:0.875rem;
  color:var(--color-disabled-text);
}

/* Sized off the text around it rather than left at the icon's own 1em, and
   raised onto its baseline since an inline SVG otherwise hangs off the
   bottom of the line */
#layout_game-layout>header>.header_airline>span .icon {
  vertical-align:-0.125em;
}

/* Each area's name is repeated under itself in both rows, which is what makes
   every column span both rows and stay vertically centered next to a date
   two lines tall, rather than every column having to be that tall itself. */
#layout_game-layout>footer {
  border-top:1px solid var(--color-border-deep);

  /* The anchor for the note above it, which is drawn out of this grid */
  position:relative;

  display:grid;
  grid-template-columns:auto auto auto 1fr auto auto;
  grid-template-rows:auto auto;
  grid-template-areas:
    "date close-day balance thoughts advisor phone"
    "date close-day balance thoughts advisor phone";
  align-items:center;
  gap:0.25rem 1rem;
}

/* No right border: the close-day button picks up the seam as its own left
   border, so the two read as one box split by a single hairline rather than
   two boxes glued together. */
#layout_game-layout>footer>.footer_date {
  grid-area:date;

  background-color:var(--color-background-deep);
  border:1px solid var(--color-border-deep);
  border-right:none;
  border-radius:0.4375rem 0 0 0.4375rem;
  padding:0.375rem 0.625rem;
}

#layout_game-layout>footer>.footer_date>.desktop {
  display:flex;
  flex-direction:column;
}

#layout_game-layout>footer>.footer_date>.mobile {
  display:none;
}

/* Pulled left by the grid's own column-gap to sit flush against the date box
   -- the two are still separate grid items, so the gap is closed rather than
   removed, leaving it in place for every other pair of columns in this row.
   Stretched to the date box's height so a border/background matched to it
   reads as the other half of the same control, split by its own left border. */
#layout_game-layout>footer>.footer_close_day {
  grid-area:close-day;
  align-self:stretch;
  display:flex;
  align-items:center;
  justify-content:center;

  /* icon_button's own fixed 1.6em square is what align-self:stretch would
     otherwise be overridden by */
  width:auto;
  height:auto;
  margin-left:-1rem;
  padding:0 1.25rem;
  background-color:var(--color-background-deep);
  border:1px solid var(--color-border-deep);
  border-radius:0 0.4375rem 0.4375rem 0;

  /* Set apart from the plain teal boxes it's attached to: this is the one
     action that advances the game, so it takes the same accent colour as a
     primary button rather than the footer's neutral text colour */
  color:var(--color-primary-action);

  /* Sized down from the phone button's 1.75rem: next-day's rays reach nearly
     the full width of the icon's viewBox where phone's handset sits in the
     middle of its own, so the same font-size reads visibly bigger here. */
  font-size:1.5rem;
}

/* icon_button's own :hover only changes color, but its specificity loses to
   the rule above -- restated here at matching specificity, same treatment as
   .icon_button[data-game-action="open"]:hover in the game list */
#layout_game-layout>footer>.footer_close_day:hover {
  color:var(--color-hover);
}

/* Same story as the hover: the accent colour above would otherwise leave a
   button that is waiting on a night looking as pressable as one that is not */
#layout_game-layout>footer>.footer_close_day:disabled {
  color:var(--color-disabled-text);
  cursor:default;
}

/* The night the button is waiting on: a night is one transaction over six
   steps, so it is not instant and the sun turns for as long as it takes */
#layout_game-layout>footer>.footer_close_day.working .icon {
  animation:layout-game-close-day-turn 900ms linear infinite;
}

@keyframes layout-game-close-day-turn {
  from { transform:rotate(0deg); }
  to { transform:rotate(360deg); }
}

/* The turn is the only thing saying the request is still out, so it is swapped
   for a fade rather than dropped -- same trade as pt-balance's held delta */
@keyframes layout-game-close-day-wait {
  0%, 100% { opacity:1; }
  50% { opacity:0.35; }
}

@media (prefers-reduced-motion: reduce) {
  #layout_game-layout>footer>.footer_close_day.working .icon {
    animation-name:layout-game-close-day-wait;
  }
}

/* Drawn over the board rather than in the footer's own grid: a note that took
   a row would push the board up every time a night failed, and give the row
   back the moment the next one worked. Flush against the bar below it and
   painted the same colour, so it reads as the top of it. */
#layout_game-layout>footer>.footer_note {
  position:absolute;
  bottom:100%;
  left:0;
  right:0;

  margin:0;

  /* The inline half matches the footer's own padding, so the note's first
     word starts over the date below it */
  padding:0.5rem 1.5rem;
  background-color:var(--color-background-main);
  border-top:1px solid var(--color-border-deep);
}

/* pt-balance draws its own colour and hangs its own delta; what belongs here is
   where in the footer it sits and the box drawn around it */
#layout_game-layout>footer>.footer_balance {
  grid-area:balance;

  /* The delta is set in by the padding, so it ends over the last digit of the
     figure rather than over the box's corner */
  --balance-delta-inset:0.625rem;

  background-color:var(--color-background-deep);
  border:1px solid var(--color-border-deep);
  border-radius:0.4375rem;
  padding:0.375rem 0.625rem;

  /* Matches the browser's default h2 size, the same one .header_airline>h2
     relies on rather than setting its own */
  font-size:1.5em;
}

/* Flex space for future use */
#layout_game-layout>footer>.footer_passenger_thoughts {
  grid-area:thoughts;
}

/* Non-flex space for future use */
#layout_game-layout>footer>.footer_advisor {
  grid-area:advisor;
}

#layout_game-layout>footer>#layout_game-phone_button {
  grid-area:phone;
  font-size:1.75rem;
}

#layout_game-layout>header>h1 {
  margin:0;
}

/* The tiles are the word, so they are never broken over two rows */
#layout_game-layout>header pt-flapper {
  flex-wrap:nowrap;
}

#layout_game-layout>header .mobile {
  display:none;
}

/* A grid item will not shrink under its content, and a canvas carries an
   intrinsic size, so without the minimum the board pushes the column past the
   viewport */
#layout_game-layout>main {
  min-width:0;

  /* The board is a window onto the game, so nothing inside it reaches the bars
     above and below, whatever it takes its own size to be */
  overflow:hidden;

  /* The one cell everything is stacked in is the whole of main and no more,
     floored the same way and for the same reason as the row main sits in */
  display:grid;
  grid-template-columns:minmax(0, 1fr);
  grid-template-rows:minmax(0, 1fr);
}

/* The menu and the screen it opens sit on the board rather than beside it:
   every child is put in the one cell, and a later one paints over an earlier
   one. That is what orders the markup -- board, then menu, then the screen */
#layout_game-layout>main>* {
  grid-area:1 / 1;
}

/* The rule above would otherwise beat the hidden attribute's own display:none */
#layout_game-layout>main>[hidden] {
  display:none;
}

/* A canvas is inline by default, so it sits on a text baseline and leaves a gap */
#layout_game-layout canvas {
  display:block;
  width:100%;
  height:100%;
}

/*
 * Mobile -> No board, the menu takes the screen, and a phone screen opens over it.
 *
 * The height clause is a phone turned sideways: wide enough to read as a
 * desktop, but far too short to give the board a row worth drawing. Height is
 * what tells it from a tablet, which is owed the desktop -- a phone in
 * landscape is around 430px tall before the browser's own bars, a tablet at
 * least 768px, so the line between them is drawn at 30rem.
 */
@media (max-width: 40rem), (max-height: 30rem) {
  #layout_game-layout>header .desktop {
    display:none;
  }

  #layout_game-layout>header .mobile {
    display:flex;
  }

  #layout_game-layout>header,
  #layout_game-layout>footer {
    padding:0.5rem 1rem;
  }

  /* No room to spare for a second row here, so the name, the star and the
     score run as one line and read as a single h2. */
  #layout_game-layout>header>.header_airline {
    flex-direction:row;
    gap:0.375em;
  }

  #layout_game-layout>header>.header_airline>h2 {
    flex:0 1 auto;
    min-width:0;
  }

  #layout_game-layout>header>.header_airline>span {
    flex:0 0 auto;

    font-size:1.375rem;
    font-weight:bold;
    text-transform:uppercase;
    color:var(--color-text);
  }

  /* The word is what a screen this narrow has no room for -- the star next to
     the score already says what the number means. */
  #layout_game-layout>header>.header_airline .reputation_label {
    display:none;
  }

  #layout_game-layout canvas {
    display:none;
  }

  /* One row: the date, the close-day button and the balance, with everything
     reserved for later folded away rather than squeezed in beside them.

     The outer columns are an equal share of what the button leaves rather than
     each their own content's width, which is what keeps the button on the
     footer's centre line however wide the date and the balance happen to be.
     The zero minimum is what holds that share equal: a bare 1fr floors at its
     content and the wider side would push the button off centre. */
  #layout_game-layout>footer {
    grid-template-columns:minmax(0, 1fr) auto minmax(0, 1fr);
    grid-template-rows:auto;
    grid-template-areas:"date close-day balance";
  }

  #layout_game-layout>footer>.footer_note {
    padding-inline:1rem;
  }

  #layout_game-layout>footer>.footer_date>.desktop {
    display:none;
  }

  #layout_game-layout>footer>.footer_date>.mobile {
    display:inline;
  }

  /* Desktop-only background, undone here so mobile keeps its plain footer */
  #layout_game-layout>footer>.footer_date,
  #layout_game-layout>footer>.footer_balance,
  #layout_game-layout>footer>.footer_close_day {
    background-color:transparent;
    border:none;
    border-radius:0;
    padding:0;
  }

  /* Its column now reaches past it to the centre, so it is put back on the
     footer's right edge */
  #layout_game-layout>footer>.footer_balance {
    justify-self:end;
    font-size:inherit;

    /* The padding the inset was matched to went with the box */
    --balance-delta-inset:0;
  }

  /* Desktop merges this into the date box's right edge; mobile keeps it a
     plain, separately centered icon button, so every property that merge
     added is undone here rather than just the ones that would clash. */
  #layout_game-layout>footer>.footer_close_day {
    align-self:auto;
    justify-self:center;
    width:1.6em;
    height:1.6em;
    margin-left:0;
    font-size:1.25rem;
  }

  /* With no board to put the phone in front of, the menu is the screen already
     and there is nothing to pick up */
  #layout_game-layout>footer>.footer_passenger_thoughts,
  #layout_game-layout>footer>.footer_advisor,
  #layout_game-layout>footer>#layout_game-phone_button {
    display:none;
  }
}

#layout_game_config-layout {
  box-sizing:border-box;
  min-height:100dvh;

  display:flex;
  align-items:center;
  justify-content:center;

  padding:1.5rem 1rem;
}

/* Mobile -> Align at the top, for the same reason as the game list: a phone
   turned sideways is short enough that a centred panel is pushed off screen */
@media (max-width: 40rem), (max-height: 30rem) {
  #layout_game_config-layout {
    align-items:flex-start;
  }
}

#layout_game_config-layout>section {
  width:100%;
  min-width:0;
  max-width:40rem;
}

#layout_game_config-layout header {
  display:flex;
  align-items:center;
  justify-content:space-between;
  gap:1rem;

  margin-bottom:1.5rem;
}

#layout_game_config-layout .header_actions {
  display:flex;
  align-items:center;
  gap:0.5rem;
}

/* pt-flapper for PT is within h1 */
#layout_game_config-layout h1 {
  margin:0;
}

#layout_game_config-layout h1>pt-flapper {
  flex-wrap:nowrap;
}

#layout_game_config-layout .header_actions>.icon_button {
  font-size:1.75rem;
}

/* Sits between the logo and the back button, and takes whatever space they
   do not want so it never pushes the back button off the edge of the panel */
#layout_game_config-layout header>span {
  flex:1;
  min-width:0;

  font-size:1.375rem;
  overflow:hidden;
  text-overflow:ellipsis;
  white-space:nowrap;
}

#layout_game_config-layout h3 {
  margin-bottom:0.75rem;
}

#layout_game_config-layout .error,
#layout_game_config-layout .hint {
  margin:0 0 1rem 0;
}

#layout_game_config-layout .hint {
  color:var(--color-disabled-text);
}

/* One label/control pair, the label sitting on its own line above the control */
#layout_game_config-layout .field_container {
  margin:0 0 1rem 0;
}

#layout_game_config-layout .field_container>label {
  display:block;
}

/* Width is this form's own; application.css gives every select its box,
   border and colors. */
#layout_game_config-layout select {
  width:100%;
}

/* The checkbox and its wording are one line, so the label wraps the control
   instead of sitting above it */
#layout_game_config-layout .checkbox_label {
  display:flex;
  align-items:center;
  gap:0.5rem;
}

#layout_game_config-layout hr {
  border:1px solid var(--color-border-deep);
  border-bottom-width:0;
  margin:1.5rem 0;
}

#layout_game_config-layout table {
  width:100%;
  border-collapse:collapse;
}

#layout_game_config-layout th {
  text-align:left;
  text-transform:uppercase;
  padding:0 0.75rem;
}

/* Horizontal space between the columns so text in column 2 never touches text
   in column 1 or 3 */
#layout_game_config-layout td {
  padding:0 0.75rem;
}

/* No horizontal space on the left of the first column otherwise the content
   is not aligned with the content in the thead anymore */
#layout_game_config-layout th:first-child,
#layout_game_config-layout td:first-child {
  /* The name column takes whatever the other two do not want, which leaves them
     sized to their content without either being given a fixed width */
  width:100%;
  padding-left:0;
}

/* No horizontal space on the right of the last column otherwise the content
   is not aligned with the content in the thead anymore */
#layout_game_config-layout th:last-child,
#layout_game_config-layout td:last-child {
  padding-right:0;
}

/* i.e. error messages, loading element... */
#layout_game_config-layout .note_cell {
  padding:1.5rem 0;
  text-align:center;
}

/* Making sure the button in the action columns do not wrap */
#layout_game_config-layout th:last-child,
#layout_game_config-layout .actions_cell {
  text-align:center;
  white-space:nowrap;
}

/* In a row the icons repeat down the page, so they sit closer to the size of
   the text they are lined up with */
#layout_game_config-layout .actions_cell>.icon_button {
  font-size:1.25rem;
}

#layout_game_config-layout .actions_cell>.icon_button:hover {
  color:var(--color-error);
}

#layout_game_config-layout footer {
  margin-top:0.5rem;
}

#layout_game_config-layout footer>form {
  display:flex;
  align-items:center;
  gap:0.5rem;
}

#layout_game_config-layout footer>form>input {
  flex:1;
  min-width:0;
}

/* The form is two fields wide, which is more than a narrow screen can line up
   next to a button */
@media (max-width: 30rem) {
  #layout_game_config-layout footer>form {
    flex-wrap:wrap;
    justify-content:center;
  }

  #layout_game_config-layout footer>form>input {
    flex:1 1 100%;
  }
}

#layout_game_config-layout .take_off_container {
  margin-top:1.5rem;
  text-align:center;
}

#layout_game_list-layout {
  box-sizing:border-box;
  min-height:100dvh;

  display:flex;
  align-items:center;
  justify-content:center;

  padding:1.5rem 1rem;
}

/* Mobile -> Align at the top. The height clause is a phone turned sideways,
   which is short enough that a centred list is pushed off the screen; it is
   drawn where layout-game.css draws it, and for the same reason */
@media (max-width: 40rem), (max-height: 30rem) {
  #layout_game_list-layout {
    align-items:flex-start;
  }
}

/* A flex item with no width is sized to its content,
   and one with no min-width will not shrink under the widest line inside it */
#layout_game_list-layout>section {
  width:100%;
  min-width:0;
  max-width:40rem;
}

#layout_game_list-layout header {
  display:flex;
  align-items:center;
  justify-content:space-between;
  gap:1rem;

  margin-bottom:1.5rem;
}

#layout_game_list-layout .header_actions {
  display:flex;
  align-items:center;
  gap:0.5rem;
}

/* pt-flapper for PT is within h1 */
#layout_game_list-layout h1 {
  margin:0;
}

#layout_game_list-layout h1>pt-flapper {
  flex-wrap:nowrap;
}

#layout_game_list-layout .icon_button[data-game-action="open"] {
  color:var(--color-primary-action);
}

#layout_game_list-layout .icon_button[data-game-action="open"]:hover {
  color:var(--color-hover);
}

#layout_game_list-layout .icon_button[data-game-action="delete"]:hover {
  color:var(--color-error);
}

/* Bigger icon size for the ones in the header actions */
#layout_game_list-layout .header_actions>.icon_button {
  font-size:1.75rem;
}

/* In a row the icons are one of three columns and repeat down the page, so they
   sit closer to the size of the text they are lined up with */
#layout_game_list-layout .actions_cell>.icon_button {
  font-size:1.25rem;
}

#layout_game_list-layout table {
  width:100%;
  border-collapse: collapse;
}

#layout_game_list-layout footer>form {
  display:flex;
  align-items:center;
  gap:0.5rem;

  margin-top:1.5rem;
}

#layout_game_list-layout footer>form>input {
  flex:1;
  min-width:0;
}

#layout_game_list-layout th {
  text-align: left;
  text-transform: uppercase;
  padding:0 0.75rem;
}

/* Horizontal space between the columns so text in column 2 never touches text
   in column 1 or 3 */
#layout_game_list-layout td {
  padding:0 0.75rem;
}

/* No horizontal space on the left of the first column otherwise the content
   is not aligned with the content in the thead anymore */
#layout_game_list-layout th:first-child,
#layout_game_list-layout td:first-child {
  /* The name column takes whatever the other two do not want, which leaves them
     sized to their content without either being given a fixed width */
  width:100%;
  padding-left:0;
}

/* No horizontal space on the right of the last column otherwise the content
   is not aligned with the content in the thead anymore */
#layout_game_list-layout th:last-child,
#layout_game_list-layout td:last-child {
  padding-right:0;
}

/* i.e. error messages, loading element... */
#layout_game_list-layout .note_cell {
  padding:1.5rem 0;
  text-align: center;
}

/* Making sure the text in the date column do not wrap */
#layout_game_list-layout th:nth-child(2),
#layout_game_list-layout .date_cell {
  text-align: center;
  white-space: nowrap;
}

/* Making sure the button in the action columns do not wrap */
#layout_game_list-layout th:last-child,
#layout_game_list-layout .actions_cell {
  text-align: center;
  white-space: nowrap;
}

/* Horizontal spacing between actions buttons */
#layout_game_list-layout .actions_cell>button+button {
  margin-left:0.5rem;
}

pt-loading {
  position:fixed;
  inset:0;

  display:flex;
  align-items:center;
  justify-content:center;

  padding:0 1rem;

  background-color:var(--color-background-overlay);
}

#layout_login-layout {
  box-sizing:border-box;
  min-height:100dvh;

  display:flex;
  align-items:center;
  justify-content:center;

  padding:1.5rem 1rem;
}

/* The panel: as wide as it is given, never wider than a form wants to be */
#layout_login-layout>section {
  width:100%;
  max-width:20rem;
}

/* The tiles fill the panel's width, so the word is centred inside them */
#layout_login-layout pt-flapper {
  justify-content:center;
}

/* The board stands clear of the form, rather than titling the first field */
#layout_login-layout h1 {
  margin-bottom:1.5rem;
}

#layout_login-layout .error {
  text-align:center;
  font-weight:bold;
}

/* One label/input pair, the label sitting on its own line above the input */
#layout_login-layout .field_container {
  margin:0 0 1rem 0;
}

#layout_login-layout .field_container>label {
  display:block;
}

/* A full width fits the panel because application.css counts an input's own
   padding and border inside its width */
#layout_login-layout .field_container>input {
  display:block;
  width:100%;
}

/* The row of buttons that closes a form. The padding is on top of the last
   field's own 1rem margin, so the gap here is the wider of the two. */
#layout_login-layout .actions_container {
  margin:0;
  padding-top:1rem;
  text-align: center;
}

/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */

/* Document
   ========================================================================== */

/**
 * 1. Correct the line height in all browsers.
 * 2. Prevent adjustments of font size after orientation changes in iOS.
 */

html {
  line-height: 1.15; /* 1 */
  -webkit-text-size-adjust: 100%; /* 2 */
}

/* Sections
   ========================================================================== */

/**
 * Remove the margin in all browsers.
 */

body {
  margin: 0;
}

/**
 * Render the `main` element consistently in IE.
 */

main {
  display: block;
}

/**
 * Correct the font size and margin on `h1` elements within `section` and
 * `article` contexts in Chrome, Firefox, and Safari.
 */

h1 {
  font-size: 2em;
  margin: 0.67em 0;
}

/* Grouping content
   ========================================================================== */

/**
 * 1. Add the correct box sizing in Firefox.
 * 2. Show the overflow in Edge and IE.
 */

hr {
  box-sizing: content-box; /* 1 */
  height: 0; /* 1 */
  overflow: visible; /* 2 */
}

/**
 * 1. Correct the inheritance and scaling of font size in all browsers.
 * 2. Correct the odd `em` font sizing in all browsers.
 */

pre {
  font-family: monospace, monospace; /* 1 */
  font-size: 1em; /* 2 */
}

/* Text-level semantics
   ========================================================================== */

/**
 * Remove the gray background on active links in IE 10.
 */

a {
  background-color: transparent;
}

/**
 * 1. Remove the bottom border in Chrome 57-
 * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
 */

abbr[title] {
  border-bottom: none; /* 1 */
  text-decoration: underline; /* 2 */
  text-decoration: underline dotted; /* 2 */
}

/**
 * Add the correct font weight in Chrome, Edge, and Safari.
 */

b,
strong {
  font-weight: bolder;
}

/**
 * 1. Correct the inheritance and scaling of font size in all browsers.
 * 2. Correct the odd `em` font sizing in all browsers.
 */

code,
kbd,
samp {
  font-family: monospace, monospace; /* 1 */
  font-size: 1em; /* 2 */
}

/**
 * Add the correct font size in all browsers.
 */

small {
  font-size: 80%;
}

/**
 * Prevent `sub` and `sup` elements from affecting the line height in
 * all browsers.
 */

sub,
sup {
  font-size: 75%;
  line-height: 0;
  position: relative;
  vertical-align: baseline;
}

sub {
  bottom: -0.25em;
}

sup {
  top: -0.5em;
}

/* Embedded content
   ========================================================================== */

/**
 * Remove the border on images inside links in IE 10.
 */

img {
  border-style: none;
}

/* Forms
   ========================================================================== */

/**
 * 1. Change the font styles in all browsers.
 * 2. Remove the margin in Firefox and Safari.
 */

button,
input,
optgroup,
select,
textarea {
  font-family: inherit; /* 1 */
  font-size: 100%; /* 1 */
  line-height: 1.15; /* 1 */
  margin: 0; /* 2 */
}

/**
 * Show the overflow in IE.
 * 1. Show the overflow in Edge.
 */

button,
input { /* 1 */
  overflow: visible;
}

/**
 * Remove the inheritance of text transform in Edge, Firefox, and IE.
 * 1. Remove the inheritance of text transform in Firefox.
 */

button,
select { /* 1 */
  text-transform: none;
}

/**
 * Correct the inability to style clickable types in iOS and Safari.
 */

button,
[type="button"],
[type="reset"],
[type="submit"] {
  -webkit-appearance: button;
}

/**
 * Remove the inner border and padding in Firefox.
 */

button::-moz-focus-inner,
[type="button"]::-moz-focus-inner,
[type="reset"]::-moz-focus-inner,
[type="submit"]::-moz-focus-inner {
  border-style: none;
  padding: 0;
}

/**
 * Restore the focus styles unset by the previous rule.
 */

button:-moz-focusring,
[type="button"]:-moz-focusring,
[type="reset"]:-moz-focusring,
[type="submit"]:-moz-focusring {
  outline: 1px dotted ButtonText;
}

/**
 * Correct the padding in Firefox.
 */

fieldset {
  padding: 0.35em 0.75em 0.625em;
}

/**
 * 1. Correct the text wrapping in Edge and IE.
 * 2. Correct the color inheritance from `fieldset` elements in IE.
 * 3. Remove the padding so developers are not caught out when they zero out
 *    `fieldset` elements in all browsers.
 */

legend {
  box-sizing: border-box; /* 1 */
  color: inherit; /* 2 */
  display: table; /* 1 */
  max-width: 100%; /* 1 */
  padding: 0; /* 3 */
  white-space: normal; /* 1 */
}

/**
 * Add the correct vertical alignment in Chrome, Firefox, and Opera.
 */

progress {
  vertical-align: baseline;
}

/**
 * Remove the default vertical scrollbar in IE 10+.
 */

textarea {
  overflow: auto;
}

/**
 * 1. Add the correct box sizing in IE 10.
 * 2. Remove the padding in IE 10.
 */

[type="checkbox"],
[type="radio"] {
  box-sizing: border-box; /* 1 */
  padding: 0; /* 2 */
}

/**
 * Correct the cursor style of increment and decrement buttons in Chrome.
 */

[type="number"]::-webkit-inner-spin-button,
[type="number"]::-webkit-outer-spin-button {
  height: auto;
}

/**
 * 1. Correct the odd appearance in Chrome and Safari.
 * 2. Correct the outline style in Safari.
 */

[type="search"] {
  -webkit-appearance: textfield; /* 1 */
  outline-offset: -2px; /* 2 */
}

/**
 * Remove the inner padding in Chrome and Safari on macOS.
 */

[type="search"]::-webkit-search-decoration {
  -webkit-appearance: none;
}

/**
 * 1. Correct the inability to style clickable types in iOS and Safari.
 * 2. Change font properties to `inherit` in Safari.
 */

::-webkit-file-upload-button {
  -webkit-appearance: button; /* 1 */
  font: inherit; /* 2 */
}

/* Interactive
   ========================================================================== */

/*
 * Add the correct display in Edge, IE 10+, and Firefox.
 */

details {
  display: block;
}

/*
 * Add the correct display in all browsers.
 */

summary {
  display: list-item;
}

/* Misc
   ========================================================================== */

/**
 * Add the correct display in IE 10+.
 */

template {
  display: none;
}

/**
 * Add the correct display in IE 10.
 */

[hidden] {
  display: none;
}

html {
  --color-background-main:#0e2c3b;
  --color-background-deep:#081f2b;
  --color-background-overlay:rgba(0, 0, 0, 0.75);
  --color-border-deep:#144153;
  --color-action:#144d52;
  --color-action-border:#257079;
  --color-primary-action:#eca42e; /*#e7a840;*/
  --color-primary-action-border:#BA7E1C;
  --color-primary-action-text:#333;

  /* Deeper than --color-error */
  --color-destructive-action:#b32424;
  --color-destructive-action-border:#8f1d1d;
  --color-destructive-action-text:#fff;

  --color-hover:#257079; /* what a button or a link answers a pointer in */

  --color-disabled:#26333b;
  --color-disabled-border:#38464f;
  --color-disabled-text:#8b98a2;

  --color-text:#ddd;

  --color-warning:#f7941d;
  --color-error:#f24646;
  --color-success:#049b4a;
}

html, body {
  --font-regular: "Inter", "Open Sans", Verdana, Arial, Helvetica, sans-serif;
  --font-airport: "Barlow Condensed", "Inter", Arial, Helvetica, sans-serif;

  color-scheme: dark;

  width:100%;
  height:100%;
  margin:0 0 0 0;
  padding:0 0 0 0;
  line-height: 1.75;
}

body {
  background-color:var(--color-background-main);
  background-image:radial-gradient(ellipse 75% 55% at 50% 0%, var(--color-action) 0%, var(--color-background-deep) 70%);
  background-attachment: fixed;
  background-repeat: no-repeat;
}

body, input, button, select, textarea, [contenteditable=true] {
  color:var(--color-text);
  font-family:var(--font-regular);
  /* Tabular figures keep money/times/flight numbers from shifting as they update */
  font-feature-settings:"tnum" 1;
}

/* Containers for the main content of the page (not all content) --> signin form, gamelist + create game,
   in-game details (the middle part above the canvas, i.e. airplanes list, personnel, ...) */
section {
  background-color: var(--color-background-main);
  padding: 1rem 1.5rem;
  border-radius: 1rem;
  border: 1px solid var(--color-border-deep);
}

dialog {
  box-sizing:border-box;
  width:min(26rem, calc(100% - 2rem));

  background-color: var(--color-background-deep);
  color: var(--color-text);
  padding: 1rem 1.5rem;
  border-radius: 1rem;
  border: 1px solid var(--color-border-deep);
}

dialog::backdrop {
  background-color:var(--color-background-overlay);
}

.dialog_actions {
  display:flex;
  align-items:center;
  justify-content:flex-end;
  gap:0.5rem;

  margin-top:1.5rem;
}

ul {
  padding:0 0 0 0;
  margin:0 0 0 0;
}

dl, dd {
  margin:0;
}

dt {
  font-weight: bold;
}

h1, .h1,
h2, .h2,
h3, .h3 {
  font-weight: bold;
  text-transform: uppercase;
}

h1, .h1 {
  font-size:1.75rem;
  margin:0 0 0.5rem 0;
  line-height: 1;
}

h2, .h2 {
  font-size:1.375rem;
  margin:0 0 0.25rem 0;
  line-height: 1;
}

h3, .h3 {
  font-size:1.0625rem;
  margin:0;
  line-height: 1;
}

fieldset {
  margin:0;
  padding:0;
  border:0 transparent;
}

input[type=submit],
button {
  border:1px solid var(--color-action-border);
  background-color:var(--color-action);
  color:var(--color-text);

  padding:0.25rem 1rem 0.25rem 1rem;
  border-radius: 5px;
  user-select: none;
}

input[type=submit].primary,
button.primary {
  border:1px solid var(--color-primary-action-border);
  background-color:var(--color-primary-action);
  color:var(--color-primary-action-text);
}

input[type=submit].destructive,
button.destructive {
  border:1px solid var(--color-destructive-action-border);
  background-color:var(--color-destructive-action);
  color:var(--color-destructive-action-text);
}

input[type=submit]:disabled,
button:disabled {
  border-color:var(--color-disabled-border);
  background-color:var(--color-disabled);
  color:var(--color-disabled-text);
  cursor:not-allowed;
}

/* I.e. Cancel button next to a Delete button on a confirmation screen */
input[type=submit].quiet,
button.quiet {
  border-color:transparent;
  background-color:transparent;
}

/* A button whose whole content is an icon, so it drops the chrome the rule
   above gives one with wording. Its box is in em, which leaves a layout setting
   nothing but font-size to pick how big the icon comes out */
.icon_button {
  display:inline-flex;
  align-items:center;
  justify-content:center;

  width:1.6em;
  height:1.6em;
  padding:0;

  border:0;
  background-color:transparent;
  color:var(--color-text);
  border-radius:5px;
  cursor:pointer;
}

.icon_button:hover {
  color:var(--color-hover);
}

input[type=text],
input[type=password],
input[type=email],
input[type=number],
input[type=date],
input[type=search] {
  /* Padding and border counted inside the height and the width, so a field
     given a width still fits the space it was given */
  box-sizing:border-box;

  background-color:var(--color-background-deep);
  border: 1px solid var(--color-border-deep);
  border-radius: 5px;
  color:var(--color-text);
  padding:0.25rem 0.5rem;
  height:2.25rem;
}
input[type=text]:focus,
input[type=password]:focus,
input[type=email]:focus,
input[type=number]:focus,
input[type=date]:focus,
input[type=search]:focus {
  outline:none;
  box-shadow:none;
}
/*
:user-invalid only matches once the field has been interacted with or submitted,
so empty required fields and autofill previews are not flagged red on load.
*/
input[type=text]:user-invalid,
input[type=password]:user-invalid,
input[type=email]:user-invalid,
input[type=number]:user-invalid,
input[type=date]:user-invalid,
input[type=search]:user-invalid {
  border-color:var(--color-error);
}

select {
  box-sizing:border-box;

  background-color:var(--color-background-deep);
  border: 1px solid var(--color-border-deep);
  border-radius: 5px;
  color:var(--color-text);
  padding:0.25rem 0.5rem;
  height:2.25rem;
}
select:focus {
  outline:none;
  box-shadow:none;
}

label {
  font-weight: bold;
}

/*
 * CSS cannot follow a label's for= to the control it names, so the star goes by
 * position instead: a required control immediately after the label, or wrapped
 * inside it. A label sitting anywhere else still says so with class="required".
 */
label:has(+ :required)::after,
label:has(:required)::after,
label.required::after {
  content: " *";
  color: var(--color-error);
}

.error,
.critical {
  color:var(--color-error);
}

/*

a, .link,
a:link,
a:visited,
a:active {
  cursor: pointer;
  color:var(--color-link);
  text-decoration:none;
}
a:hover, .link:hover, .link:hover>a {
  color:var(--color-link-hover);
  text-decoration:underline;
}

a.disabled, .link.disabled,
a.disabled:link,
a.disabled:visited,
a.disabled:active,
li.disabled {
  cursor:default;
  pointer-events: none;
  color:var(--color-link-disabled);
  text-decoration:none;
  cursor: not-allowed;
}
a.disabled:hover, .link.disabled:hover {
  color:var(--color-link-disabled);
  text-decoration:none;
}

select {
  background-color:var(--color-element-input-background);
  color:var(--color-element-input-text);
}

textarea {
  border: 1px solid var(--color-element-input-border);
  background-color:var(--color-element-input-background);
  color:var(--color-element-input-text);
  padding:0.25rem 0.25rem 0.25rem 0.25rem;
  border-radius:2px;
}

textarea:focus {
  border-color:var(--color-element-input-border-focus);
  outline:none;
  box-shadow:none;
}
textarea:user-invalid {
  border-color:var(--color-error);
}

textarea:disabled {
  border:1px solid var(--color-element-input-border-disabled);
  color: var(--color-element-input-text-disabled);
  cursor: not-allowed;
}



input[type=text]:disabled,
input[type=password]:disabled,
input[type=email]:disabled,
input[type=date]:disabled,
input[type=search]:disabled {
  border-bottom-color:var(--color-element-input-border-disabled);
  color: var(--color-element-input-text-disabled);
  cursor: not-allowed;
}





.success {
  color:var(--color-success);
}

hr {
  border: 1px solid var(--color-separator);
  border-bottom-width: 0;
}

.important {
  font-weight: bold;
}

.secondary,
.secondary h1, .secondary .h1,
.secondary h2, .secondary .h2,
.secondary h3, .secondary .h3 {
  color:var(--color-text-secondary);
}

h1 + p,
h2 + p,
h3 + p {
  margin-top:0;
}

p {
  margin:0.5rem 0;
}

p + p {
  margin-top:1rem;
}
*/

