/* ═══════════════════════════════════════════════════════════════════════════
   Trip Schedule — component styles · MAC-FLAVOURED BUILD
   Requires tokens.css. No raw hex values below this line: if a colour is
   missing, add a semantic token to tokens.css rather than inlining one here.

   This file is the default build's styles.css plus the "MAC:" blocks below —
   the handful of shapes that tokens alone cannot express: vibrancy on the
   chrome, the macOS focus ring, the System-Settings segmented control, and
   overlay scrollbars. Everything else is theme, and lives in tokens.css.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ─── Reset / base ─────────────────────────────────────────────────────── */

*,
*::before,
*::after { box-sizing: border-box; }

html {
  /* 100%, not a hard 14px — the browser's own font-size setting is a user
     accessibility preference and the old build silently overrode it.
     Board density now lives in the type scale instead. */
  font-size: 100%;
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
}

body {
  margin: 0;
  padding:
    calc(var(--space-4) + env(safe-area-inset-top))
    calc(var(--space-5) + env(safe-area-inset-right))
    calc(var(--space-8) + env(safe-area-inset-bottom))
    calc(var(--space-5) + env(safe-area-inset-left));
  font-family: var(--font-sans);
  font-size: var(--text-sm);
  line-height: var(--leading-normal);
  color: var(--color-text);
  background: var(--color-bg);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  font-synthesis-weight: none;
  /* WORKLIST #256: `auto` let San Francisco (a variable font) pick its own
     optical size, and is suspected of a WebKit mis-measure — a whole line
     stacked onto a tenth of its width after the Mac sleeps or Safari purges
     memory. `none` locks it off; small text a touch tighter and lighter,
     large headings a touch looser, columns may move a pixel or two, nothing
     else. `--tracking-normal` (tokens.css, -0.005em) stays: at cell size
     it's a tenth of a pixel and not the suspect. Revert this rule alone if
     it bothers the owner on Apple hardware. */
  font-optical-sizing: none;
  letter-spacing: var(--tracking-normal);
}

:where(a) { color: var(--color-text-accent); text-underline-offset: 0.2em; }
:where(a:hover) { color: var(--color-accent-hover); }

/* One focus treatment for the whole app.
   MAC: macOS draws a soft, wide ring just outside the control rather than a
   hard 2px outline. This uses true systemBlue (#0A84FF) — legitimate here
   because a focus ring is a non-text indicator needing only 3:1, which it
   clears at 3.65:1 on white. */
:where(a,
button,
input,
select,
textarea,
summary,
[tabindex]):focus-visible {
  outline: var(--focus-ring-width) solid var(--focus-glow);
  outline-offset: var(--focus-ring-offset);
  border-radius: var(--radius-sm);
}
:where(button,
input,
select,
textarea,
summary):focus-visible {
  border-color: var(--color-focus);
}

/* ─── Typography hierarchy ─────────────────────────────────────────────── */

h1 {
  margin: 0 0 var(--space-1);
  /* Was --text-2xl (22px), barely a step above a section heading at 19px. */
  font-size: var(--text-3xl);
  font-weight: var(--weight-semibold);
  line-height: var(--leading-tight);
  letter-spacing: var(--tracking-tight);
  color: var(--color-text);
}

h2 {
  margin: var(--space-8) 0 var(--space-3);
  font-size: var(--text-xl);
  font-weight: var(--weight-semibold);
  line-height: var(--leading-tight);
  letter-spacing: var(--tracking-tight);
  color: var(--color-text);
}

/* A block title is a label for the table under it, not a third heading level
   competing with the section above. It used to be 16px semibold — near enough
   to the 19px section heading to read as a peer. */
h3,
.block-title {
  margin: var(--space-6) 0 var(--space-2);
  font-size: var(--text-md);
  font-weight: var(--weight-medium);
  line-height: var(--leading-snug);
  letter-spacing: var(--tracking-wide);
  color: var(--color-text-muted);
}

/* Section headings carry a quiet rule so the eye finds phase boundaries
   without another box. */
main > section > h2 {
  margin-top: var(--space-12);
  padding-bottom: var(--space-2);
  border-bottom: var(--hairline) solid var(--color-border);
}

header { margin-bottom: var(--space-5); }

/* All-caps eyebrow — the one place tracking opens up. */
.duty-label {
  font-size: var(--text-2xs);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
  color: var(--color-text-muted);
}

code,
kbd {
  font-family: var(--font-mono);
  font-size: 0.92em;
  font-variant-numeric: tabular-nums;
}

kbd {
  padding: 0.1em 0.4em;
  border: var(--hairline) solid var(--color-border-strong);
  border-bottom-width: 2px;
  border-radius: var(--radius-xs);
  background: var(--color-surface);
  font-size: var(--text-2xs);
  white-space: nowrap;
}

/* ─── Utilities ────────────────────────────────────────────────────────── */

/* One of the four places !important is still right: a utility whose whole job
   is to remove something from view has to win wherever it is dropped, and it
   is applied to elements whose own rules it cannot know. */
.visually-hidden {
  position: absolute !important;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* Keyboard escape hatch into the board. */
.skip-link {
  position: absolute;
  left: var(--space-2);
  top: var(--space-2);
  z-index: var(--layer-popover);
  padding: var(--space-2) var(--space-4);
  min-height: var(--tap-target);
  display: inline-flex;
  align-items: center;
  background: var(--color-accent);
  color: var(--color-on-accent);
  font-weight: var(--weight-semibold);
  border-radius: var(--radius-sm);
  /* No shadow while parked: the link is translated off the top, but its
     shadow still painted into the corner of every page. */
  box-shadow: none;
  transform: translateY(calc(-100% - var(--space-4)));
  transition: transform var(--motion-fast) var(--ease-out);
}
.skip-link:focus {
  transform: translateY(0);
  box-shadow: var(--shadow-lg);
}

.legend,
.note { color: var(--color-text-muted); margin: var(--space-1) 0; }

.legend {
  font-size: var(--text-xs);
  line-height: var(--leading-relaxed);
  max-width: var(--measure-prose);
}
.legend strong { color: var(--color-text); }

.legend .swatch {
  display: inline-block;
  flex: 0 0 auto;
  width: 1.15em;
  height: 1.15em;
  vertical-align: -0.2em;
  background: var(--cell-changed-bg);
  border: var(--hairline) solid var(--cell-changed-edge);
  border-left-width: 3px;
  border-radius: var(--radius-xs);
  margin-right: var(--space-1);
}

.legend-body {
  padding: var(--space-2) var(--space-2) var(--space-1) var(--space-6);
  line-height: var(--leading-relaxed);
}
.legend-body p { margin: 0 0 var(--space-2); }
.legend-body p:last-child { margin-bottom: 0; }
.legend-body strong { color: var(--color-text); }

.meta {
  font-size: var(--text-xs);
  color: var(--color-text-muted);
  margin: var(--space-3) 0 var(--space-4);
  padding: var(--space-2) var(--space-3);
  background: var(--color-surface-sunken);
  border: var(--hairline) solid var(--color-border);
  border-radius: var(--radius-sm);
  font-variant-numeric: tabular-nums;
}
/* WORKLIST #130: the marker paints from `dataset.source`, never from the
   stamp text itself — #meta-strip is contenteditable and its blur handler
   parses that text back into the model (admin.js), so anything appended to
   it would be read back in and persisted. */
.meta[data-source="server"]::after { content: ' (server)'; font-style: italic; }

/* ─── Controls ─────────────────────────────────────────────────────────── */

/* Every control clears the 44px tap target via a transparent ::after that
   extends the hit area without changing the visual box.
   `.att-add` (ts-notes.js: `'btn btn-mini att-add'`) already carries `.btn`,
   so `.btn.btn-mini`'s own comment (below, "Small only in ink") already
   holds it to 44px; nothing to add for it here.
   #428 (a) fix round: `.btn-plus`/`.pax-doc-flag` do NOT join this
   unconditional group — `.btn-plus` once also drew the date "+" (#415
   (c), gone with #512 (b)), stacked directly under a date cell's own text.
   An always-on 44px invisible zone there reaches up into that td's own
   clickable area and steals the click a mouse user meant for the date cell
   (#347 broke this way, caught by test-a11y-admin/test-a11y). Their touch
   area is scoped to `(hover: none), (pointer: coarse)` below instead, same
   as (b)'s Pax controls — a mouse's click point never lands inside it. */
.btn,
.btn-primary,
button.btn,
.toolbar button,
.tab-bar button,
.leg-notes-marker {
  position: relative;
}
.btn::after,
.btn-primary::after,
button.btn::after,
.toolbar button::after,
.tab-bar button::after,
.leg-notes-marker::after {
  content: "";
  position: absolute;
  inset: 50% 0 0 50%;
  width: max(100%, var(--tap-target));
  height: max(100%, var(--tap-target));
  transform: translate(-50%, -50%);
  pointer-events: auto;
}

/* `hidden` has to win over the display below, or an anchor marked hidden
   stays on screen. The browser's own [hidden] rule loses on specificity,
   which is why these used to carry inline `display` — and an inline display
   also beat `inline-flex`, so the label stopped centring and sat at the top
   of the box. Both fixed here rather than per element. */
.btn[hidden],
button.btn[hidden] { display: none; }

.btn,
button.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* These are anchors as often as buttons. */
  text-decoration: none;
  gap: var(--space-2);
  min-height: var(--control-h-md);
  padding: var(--space-2) var(--space-3);
  font: inherit;
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  line-height: var(--leading-snug);
  color: var(--color-text);
  background: var(--color-surface);
  border: var(--hairline) solid var(--color-border-strong);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-xs), var(--shadow-inset-top);
  cursor: pointer;
  text-decoration: none;
  transition:
    background-color var(--motion-fast) var(--ease-out),
    border-color var(--motion-fast) var(--ease-out),
    box-shadow var(--motion-fast) var(--ease-out),
    transform var(--motion-instant) var(--ease-out);
}
.btn:hover,
button.btn:hover { background: var(--color-surface-hover); }
/* Tactile: the control gives, then returns. */
.btn:active,
button.btn:active {
  transform: translateY(1px);
  box-shadow: none;
}

.btn-primary {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  min-height: var(--control-h-lg);
  padding: var(--space-3) var(--space-5);
  margin-top: var(--space-2);
  font: inherit;
  font-size: var(--text-md);
  font-weight: var(--weight-semibold);
  color: var(--color-on-accent);
  background: var(--color-accent);
  border: var(--hairline) solid var(--color-border-accent);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-sm);
  cursor: pointer;
  transition:
    background-color var(--motion-fast) var(--ease-out),
    box-shadow var(--motion-fast) var(--ease-out),
    transform var(--motion-instant) var(--ease-out);
}
.btn-primary:hover { background: var(--color-accent-hover); }
.btn-primary:active { transform: translateY(1px); box-shadow: none; }

/* `.btn-primary` alone is the full-width form submit (login, add user). Paired
   with `.btn` it is an INLINE primary: the same box as its neighbours in a
   toolbar, wearing the accent so the commit action is not one of five
   identically weighted buttons.

   Both variants are written at `.btn.<variant>` and `button.btn.<variant>`
   because the base rule is `.btn, button.btn` — on a <button>, that (0,1,1)
   beat a bare `.btn-danger` (0,1,0), so the destructive colour had quietly
   never applied to a single button in the app. */
.btn.btn-primary,
button.btn.btn-primary {
  width: auto;
  margin-top: 0;
  min-height: var(--control-h-md);
  padding: var(--space-2) var(--space-4);
  font-size: var(--text-sm);
  font-weight: var(--weight-semibold);
  color: var(--color-on-accent);
  background: var(--color-accent);
  border-color: var(--color-border-accent);
}
.btn.btn-primary:hover,
button.btn.btn-primary:hover { background: var(--color-accent-hover); }

.btn.btn-danger,
button.btn.btn-danger {
  color: var(--color-danger);
  /* #427 (#437's finding): a control's edge is >= 3:1 (WCAG 1.4.11);
     `--color-danger-border` is the softer tint the notices keep. */
  border-color: var(--color-danger-border-strong);
}
.btn.btn-danger:hover,
button.btn.btn-danger:hover { background: var(--color-danger-surface); }

/* WORKLIST #364: the one "+" that adds an optional field, drawn not typed
   since #377 — a 20×20 square (#377 (b): shrunk from the 24×24 the owner's
   first pick, variant C, landed at) holding an inline SVG (#377 (c):
   `TS.plusGlyph`, ts-widgets.js — two strokes crossing at the box's centre,
   never a font glyph, whose own baseline sat below the box's true middle).
   `TS.plusButton` (ts-widgets.js) is the one place that builds it; a "(+)"
   drawn straight into an existing control (ts-notes.js) wears this same
   class on a `<span>` instead, and calls the same `TS.plusGlyph()`.
   #377 (d): `vertical-align` centres the square on the CAPITALS of the text
   beside it — `calc((1cap - 20px) / 2)` measured equal above the capitals
   and below the baseline on the Mac at 2× and 3×; flex centring and
   `vertical-align: middle` both measured low (the owner: "the + itself
   seems to be offset a bit down from center").
   #441 (b) (the owner, 2026-09-28: "make all '+' windows white rather
   than blue"): the plain button's own look (`.btn` above) — surface
   fill, ordinary border, text colour — not the accent tokens. Size and
   touch area (20×20, #377, #428) unchanged. */
.btn-plus {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  padding: 0;
  margin: 0;
  font: inherit;
  vertical-align: calc((1cap - 20px) / 2);
  color: var(--color-text);
  background: var(--color-surface);
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-control);
  cursor: pointer;
}
.btn-plus svg { display: block; }
button.btn-plus:hover { background: var(--color-surface-hover); }
.btn-plus:focus-visible {
  outline: var(--focus-ring-width) solid var(--color-focus);
  outline-offset: var(--focus-ring-offset);
}

.toolbar button,
.tab-bar button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: var(--control-h-sm);
  padding: var(--space-2) var(--space-3);
  font: inherit;
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  color: var(--color-text);
  background: var(--color-surface);
  border: var(--hairline) solid var(--color-border-strong);
  border-radius: var(--radius-control);
  box-shadow: var(--shadow-xs);
  cursor: pointer;
  transition:
    background-color var(--motion-fast) var(--ease-out),
    transform var(--motion-instant) var(--ease-out);
}
.toolbar button:hover,
.tab-bar button:hover { background: var(--color-surface-hover); }
.toolbar button:active,
.tab-bar button:active { transform: translateY(1px); }

/* ─── Form fields ──────────────────────────────────────────────────────── */

.field-input {
  width: 100%;
  min-height: var(--control-h-lg);
  font: inherit;
  font-size: var(--text-sm);
  padding: var(--space-2) var(--space-3);
  color: var(--color-text);
  background: var(--color-surface);
  border: var(--hairline) solid var(--color-border-strong);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-xs);
  transition:
    border-color var(--motion-fast) var(--ease-out),
    box-shadow var(--motion-fast) var(--ease-out);
}
/* #24's anti-zoom floor is in RESPONSIVE below (#366): here, above
   `.people-input` and `.leg-cell-input`, those two beat it. */

.field-input:focus-visible {
  outline: var(--focus-ring-width) solid var(--color-focus);
  outline-offset: 1px;
  border-color: var(--color-focus);
}

.panel-note {
  margin: calc(-1 * var(--space-2)) 0 var(--space-3);
  max-width: 46rem;
  color: var(--color-text-muted);
  font-size: var(--text-sm);
}

/* ─── App chrome / toolbars ────────────────────────────────────────────── */

/* #415 (b): the top bar stays at the top of the window on every page while
   the page scrolls — sticky, so it keeps its own place in the flow and the
   first row is never under it. app.js measures it into `--chrome-h` for
   what pins under it and for anchor jumps (`scroll-padding-top` below).
   No vibrancy: a backdrop filter on a sticky element over content that
   repaints beneath it makes Safari draw the text twice (#138), so the
   material is the toolbar's near-opaque fill with the hairline at its base. */
html {
  scroll-padding-top: calc(var(--chrome-h, 0px) + var(--pin-h, 0px) + var(--space-2));
}
.app-chrome {
  position: sticky;
  top: 0;
  z-index: var(--layer-sticky);
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-4);
  align-items: center;
  justify-content: space-between;
  padding: var(--space-2) var(--space-3);
  /* Paint under the notch / status bar on iOS rather than clipping into it. */
  padding-top: max(var(--space-2), env(safe-area-inset-top));
  /* #439 (N13) fix round 2: opaque on every page and view (the .96 of
     --toolbar-bg let board lines show through); the same two colours, alpha 1. */
  background: var(--app-chrome-bg);
  border: 0;
  border-bottom: var(--hairline) solid var(--chrome-edge);
  /* #511 (a): a sticky bar spans the window's top edge; no top radius. */
  border-radius: 0;
  margin-bottom: var(--space-4);
}
.app-chrome .user-chip {
  font-size: var(--text-sm);
  font-weight: var(--weight-semibold);
}
.app-chrome .user-chip .role {
  font-weight: var(--weight-normal);
  color: var(--color-text-muted);
  margin-left: var(--space-1);
}
/* #497 fix 4 (the owner, ticket Flight_20261002_162620): the name, and
   just below it the one Tankering button (app.js `drawChromeTanker`), on
   every page and device; a full 44px target of its own. */
.chrome-who {
  /* #511 (g): a grid so the UTC chip sits right of the name on its line
     and everything else spans the line below it, each as wide as itself. */
  display: grid;
  grid-template-columns: minmax(0, auto) auto;
  justify-content: start;
  justify-items: start;
  align-items: baseline;
  gap: var(--space-1) var(--space-2);
  min-width: 0;
}
.chrome-who > :not(.user-chip):not(.clock-utc) { grid-column: 1 / -1; }
.chrome-who .chrome-tanker { min-height: var(--tap-target); }
.chrome-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  align-items: center;
}
/* #511 (f), the owner's 1B (ticket Flight_20261002_234458): every top-bar
   item — Tankering under the name and each page item on the right (Crew,
   Person ▾, Actuals, Notes, Config, Passkeys, Log out) — is one text link
   of one size on every device: one font size, one height (a full 44px
   target), one padding, each as wide as its word. Buttons among them
   (Person, Log out) wear it too; they stay buttons. The ☰ Menu button
   (outside `.chrome-actions`) keeps the `.btn` skin, the one button on a
   phone. Add flight block, beside Tankering under the name, wears
   Tankering's style (#497 fix 6) and stays a <button> (#415 (a)). */
.app-chrome .chrome-actions > .btn,
.app-chrome .chrome-who .btn {
  min-height: var(--tap-target);
  padding: 0 var(--space-2);
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  color: var(--color-text-accent);
  background: transparent;
  border: 0;
  border-radius: var(--radius-sm);
  box-shadow: none;
}
.app-chrome .chrome-actions > .btn:hover,
.app-chrome .chrome-who .btn:hover {
  background: transparent;
  text-decoration: underline;
  text-underline-offset: 0.2em;
}
.app-chrome .chrome-actions > .btn:active,
.app-chrome .chrome-who .btn:active { transform: none; }
/* #425 (b): on a phone the page buttons fold into one "☰ Menu" (app.js
   adds the button before `.chrome-actions` on every page); off a phone it
   is not drawn and the buttons stay in the bar. The phone rules are in
   the 40rem block below. */
.app-chrome button.chrome-menu-btn { display: none; }


/* ─── The segmented control (#427) ────────────────────────────────────
   `TS.segmented` (ts-widgets.js) draws plain `.btn`s on any page that
   calls it (Users, Airline home), so this is the base's; this is the group
   wrapper only — tight enough that the tap targets read as one control
   rather than a loose row of buttons. The pressed option is `.btn-primary`,
   already styled above; nothing here repeats that skin. */
.ts-segmented {
  display: inline-flex;
  gap: var(--space-1);
}
.ts-segmented-option { min-height: var(--control-h-sm); }
/* #508 (a): the pressed option is the pill with the 2 px selected-edge ring,
   never `.btn-primary` (the accent fill is the page's one commit action). */
.ts-segmented .ts-segmented-option[aria-pressed="true"],
.ts-segmented .ts-segmented-option[aria-pressed="true"]:hover {
  color: var(--color-text);
  background: var(--segment-pill);
  box-shadow: inset 0 0 0 2px var(--color-selected-edge);
}

/* ─── Passkey controls (#136) ──────────────────────────────────────────────
   The menu the "Passkeys" button opens is the chrome's own second row, not a
   floating card: `.app-chrome` already wraps, so a child that takes the full
   basis lands under the buttons and pushes nothing over the board. Existing
   tokens only — it is the same material as the row above it. */
.passkey-panel {
  flex: 1 1 100%;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2) var(--space-3);
  padding-top: var(--space-2);
  border-top: var(--hairline) solid var(--chrome-edge);
}
.passkey-panel[hidden] { display: none; }

.passkey-list {
  flex: 1 1 100%;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  margin: 0;
  padding: 0;
  list-style: none;
}
.passkey-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-1) var(--space-2);
}
.passkey-label { font-weight: var(--weight-semibold); }
/* #506 (h): Remove stands at the row's far right. */
.passkey-row .passkey-revoke { margin-left: auto; }
.passkey-meta,
.passkey-empty,
.passkey-said {
  margin: 0;
  color: var(--color-text-muted);
}
.passkey-said { flex: 1 1 100%; }
/* The device in the person's hand, marked so the row that ends this session
   is the one they can see is theirs. */
.passkey-here {
  font-size: var(--text-xs);
  font-weight: var(--weight-semibold);
  padding: 0 var(--space-2);
  background: var(--color-surface-sunken);
  border: var(--hairline) solid var(--color-border);
  border-radius: var(--radius-sm);
}

/* ─── The passkey banner (#271; #137's gate until then) ────────────────────
   The same material as every other banner — existing tokens only, so the
   colours are the ones already proved AAA in both themes — laid out as one
   row of controls under two lines: the count, then why. Nothing hides the
   board any more: the server ends the password's session at the window's
   end, so the page has nothing to lock. */
.passkey-banner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2) var(--space-3);
}
.passkey-banner-head,
.passkey-banner-why,
.passkey-banner-said {
  flex: 1 1 100%;
  margin: 0;
}
.passkey-banner-head { font-weight: var(--weight-semibold); }
.passkey-banner-said:empty { display: none; }

.toolbar {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  align-items: center;
  padding: var(--space-2) var(--space-3);
  background: var(--toolbar-bg);
  border: var(--hairline) solid var(--color-border);
  border-radius: var(--radius-md);
  margin-bottom: var(--space-4);
}

.toolbar .status {
  margin-left: auto;
  font-size: var(--text-xs);
  color: var(--color-text-muted);
  font-variant-numeric: tabular-nums;
}


/* ─── Next duty ─────────────────────────────────────────────────────────
   The answer to the question the crew board is opened with. It is the one
   element on the page that floats: --color-surface-raised and a shadow are
   reserved for it, so "raised" reads as "this is the answer" rather than as
   decoration. Everything else on the page sits flat on the ground.
   #332: the strip's own rules are the crew board's, in user.css. */
.crew-header { margin-bottom: var(--space-4); }

/* ── The admin board's handoff marker (#180): its own row, read-only, tap
   to expand — never inside the Notes cell (see handoffMarkerRow, ts-board.js). ── */
.handoff-marker-row > .handoff-marker-cell {
  padding: var(--space-1) var(--space-4);
  border-left: 3px solid var(--color-border);
  border-bottom: var(--hairline) solid var(--color-border);
  white-space: normal;
  vertical-align: middle;
}
/* Data about a leg, not a leg: no stripe, no hover, like the duty/pax rows. */
.flight-table tbody tr.handoff-marker-row:hover > td,
.flight-table tbody tr.handoff-marker-row:nth-child(even) > td {
  background: var(--table-bg);
}
.handoff-admin-wrap { display: inline-block; }
.handoff-admin-marker {
  font: inherit;
  font-size: var(--text-2xs);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-wide);
  padding: var(--space-1) var(--space-2);
  border-radius: var(--radius-full);
  border: var(--hairline) solid var(--color-border-strong);
  background: var(--color-surface);
  color: var(--color-text-muted);
  cursor: pointer;
  white-space: nowrap;
}
.handoff-admin-marker.has-unread {
  background: var(--cell-changed-bg);
  border-color: var(--cell-changed-edge);
  color: var(--cell-changed-text);
}
.handoff-admin-detail {
  margin-top: var(--space-1);
  max-width: var(--measure-cell);
  white-space: normal;
  font-size: var(--text-xs);
  color: var(--color-text);
}
.handoff-admin-note { margin: 0 0 var(--space-1); }
.handoff-admin-note:last-child { margin-bottom: 0; }

/* ─── The crew board's compact view (#205, a striped gridded table #278) ──
   A second renderer, not a mode of the tables above: one line per leg, a
   thin trip heading, a quiet row for each gap. Everything a full leg row
   carries but where/when/who stays out of the DOM entirely — this is not
   the full card with CSS hiding the rest. Each trip is its own
   `table.flight-table.compact-table` (#278): four real cells a leg row —
   Date, Route, Times, Crew (#308: the Brief column is gone; a briefing
   button belongs only on a sector Grokbot has actually briefed, a later
   entry's to add) — so the gridlines the shared `.flight-table` rules draw
   (the unlayered `table`/`th,td` rules) fall BETWEEN them too, not just
   around the row; the outer border and the `row-alt` stripe come from those
   same rules and `@layer stripe`. The list styling that used to live here
   (the old compact-legs list class, `.compact-leg`'s own border) is gone.

   #337: the shared `.flight-table` rule below (:1608) sizes each table to
   its OWN longest cell (`table-layout: auto; width: max-content`), which
   is why a sparse trip's table used to end well short of a busy trip's —
   every row's right edge, and every column's left edge, differed from
   table to table. `.compact-table`'s own override (below the shared rule,
   so it wins the cascade tie) fixes every trip's table to 100% of its
   container instead; `col` percentages come from `crew-board.js`'s
   `syncCompactColumnWidths`, which measures every compact table's content
   ONCE (all of them share one grid) and writes the shares there, at every
   viewport — not only below the 40rem breakpoint #298 first fixed this
   for. A `colspan="4"` row (the gap/home line) then always spans the
   table's own 100%, so it lines up too, with no separate rule needed for
   it.

   #332: the view's own rules (`.compact-table`, `.compact-trip-title`,
   …) are in user.css, which every page that draws them
   links after this sheet — so the override still follows the shared rule. */
.compact-root { margin: var(--space-4) 0 var(--space-6); }

/* The wall clock beside a status: for looking at, never announced (#27). */
.status-detail {
  font-variant-numeric: tabular-nums;
  color: var(--color-text-muted);
}

/* ── Derived Open flags ──────────────────────────────────────────────────
   Not bullets: no id, no delete button, nothing to edit. The way to clear one
   is to correct the home span it describes, which is why it reads as a
   separate, quieter group under the flags somebody actually wrote. */
.derived-flags { margin-top: var(--space-3); }
.derived-flags-label {
  margin: 0 0 var(--space-1);
  font-size: var(--text-2xs);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
  color: var(--color-text-muted);
}
.derived-flag-list { margin-top: 0; }
.derived-flag-list li {
  padding-right: var(--space-5);
  color: var(--color-text-muted);
}
.derived-flag-list li strong { color: var(--color-text); }

/* #244(b): 40rem/64rem stay literal at every @media below — custom
   properties do not resolve inside a media query. */

/* ─── Undo toast ─────────────────────────────────────────────────────────
   #21. It sits above the bottom safe-area inset so a phone's home indicator
   does not cover the Undo button, and it never spans more than a readable
   line. It is the one thing allowed to float over the board besides a
   dropdown, so it takes the raised surface and the large shadow. */
.undo-toast {
  position: fixed;
  left: 50%;
  transform: translateX(-50%);
  bottom: calc(var(--space-5) + env(safe-area-inset-bottom, 0px));
  z-index: var(--layer-popover);
  display: flex;
  align-items: center;
  gap: var(--space-3);
  max-width: min(32rem, calc(100vw - 2 * var(--space-4)));
  padding: var(--space-2) var(--space-3);
  font-size: var(--text-sm);
  color: var(--color-text);
  background: var(--color-surface-raised);
  border: var(--hairline) solid var(--color-border-strong);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-lg);
}
/* #508 (i): the stale-version line. Bottom-left, clear of the undo toast
   (app.js lifts it above a toast that is showing). */
.ts-version-line {
  position: fixed;
  left: calc(var(--space-4) + env(safe-area-inset-left, 0px));
  bottom: calc(var(--space-4) + env(safe-area-inset-bottom, 0px));
  max-width: calc(100vw - 2 * var(--space-4));
  z-index: var(--layer-popover);
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-2) var(--space-2) var(--space-2) var(--space-4);
  background: var(--color-surface-raised);
  color: var(--color-text);
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-sm);
  font-family: var(--font-sans);
  font-size: var(--text-sm);
  line-height: var(--leading-snug);
}
.undo-toast-text { flex: 1 1 auto; }
.undo-toast .undo-toast-undo { flex: 0 0 auto; }
.undo-toast .undo-toast-close {
  /* #508 (d): an unbordered icon, not a second button; 44 px hit on touch. */
  flex: 0 0 auto;
  position: relative;
  min-width: var(--control-h-sm);
  min-height: var(--control-h-sm);
  padding: 0 var(--space-2);
  font: inherit;
  font-size: var(--text-md);
  line-height: var(--leading-none);
  color: var(--color-text-muted);
  background: none;
  border: 0;
  cursor: pointer;
}
.undo-toast .undo-toast-close:hover { color: var(--color-text); }
.undo-toast .undo-toast-close:focus-visible {
  outline: var(--focus-ring-width) solid var(--color-focus);
  outline-offset: var(--focus-ring-offset);
}
@media (hover: none), (pointer: coarse) {
  .undo-toast .undo-toast-close::after {
    content: "";
    position: absolute;
    inset: 50% 0 0 50%;
    width: max(100%, var(--tap-target));
    height: max(100%, var(--tap-target));
    transform: translate(-50%, -50%);
  }
}

/* ─── Are you sure? (#432 S5, `TS.confirmDialog` in ts-chrome.js) ─────────
   The one in-page question, on every page that removes, resets or sends
   something: a modal <dialog>, the question as its heading, what else goes
   under it, the action then Cancel. Here, not in a page's sheet, because
   five pages draw it. The material is the crew board's home dialog's
   (user.css `.home-dialog`). */
dialog.ts-confirm {
  width: min(28rem, calc(100vw - 2 * var(--space-4)));
  padding: var(--space-5);
  color: var(--color-text);
  background: var(--color-surface);
  border: var(--hairline) solid var(--color-border-strong);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-lg);
}
dialog.ts-confirm::backdrop { background: var(--dialog-backdrop); }
.ts-confirm-title {
  margin: 0 0 var(--space-2);
  font-size: var(--text-lg);
  font-weight: var(--weight-semibold);
}
.ts-confirm-detail { margin: 0 0 var(--space-3); }
.ts-confirm-actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: var(--space-2);
  margin-top: var(--space-4);
}
/* #506 (a): the dialog's destructive action is the one solid red button
   (#505 (d)); `.btn-danger` elsewhere stays the outline. */
.btn.btn-danger.ts-confirm-solid,
button.btn.btn-danger.ts-confirm-solid {
  color: var(--btn-danger-solid-fg);
  background: var(--btn-danger-solid-bg);
  border-color: var(--btn-danger-solid-bg);
}
.btn.btn-danger.ts-confirm-solid:hover,
button.btn.btn-danger.ts-confirm-solid:hover { background: var(--color-danger-hover); }
/* On the phone the two stack full-width, the action on top (decision 11A). */
@media (max-width: 40rem) {
  .ts-confirm-actions { flex-direction: column-reverse; }
  .ts-confirm-actions .btn { width: 100%; }
}

/* ─── The hover box (#452 fix round 1, `TS.hoverBox` in ts-chrome.js) ─────
   The whole of something cut short: a floating box, no Close, placed by
   the script (centred on a resting pointer, or laid over its trigger on a
   tap or a key). A manual popover, so the top layer; the UA's popover
   inset and margin are undone so `left`/`top` place it. Never wider than
   the window (the script caps max-width to it). */
.ts-hoverbox {
  position: fixed;
  inset: auto;
  margin: 0;
  z-index: 2147483000;
  box-sizing: border-box;
  width: max-content;
  max-width: min(28rem, calc(100vw - 16px));
  overflow: auto;
  padding: var(--space-2) var(--space-3);
  color: var(--color-text);
  background: var(--color-surface);
  border: var(--hairline) solid var(--color-border-strong);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-lg);
  font-family: var(--font-sans);
  font-size: var(--text-sm);
  font-weight: normal;
  line-height: 1.45;
  text-align: left;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
.ts-hoverbox-title { margin: 0 0 var(--space-1); font-weight: var(--weight-semibold); }

/* ─── Banners / empty states ───────────────────────────────────────────── */

.banner {
  padding: var(--space-3);
  background: var(--banner-bg);
  border: var(--hairline) solid var(--banner-edge);
  border-left-width: 3px;
  border-radius: var(--radius-sm);
  margin-bottom: var(--space-4);
  font-size: var(--text-sm);
  color: var(--banner-text);
}
.banner a { color: var(--banner-link); }
.banner .hint { color: var(--banner-muted); }

/* ─── The UTC clock and its notice (#282) ───────────────────────────────
   The chip sits in the top bar right of the name (#511 (g): 12px, no
   border; it was under the page title); tabular figures so the
   minutes do not jiggle as they tick. The notice is the plain banner: an
   unverified clock is a caution, not a lost edit. The action bar's local
   times read as a quiet label beside its fields. Existing tokens only. */
.clock-utc {
  display: inline-block;
  padding: 0;
  border: 0;
  font-size: var(--text-xs);
  font-weight: var(--weight-semibold);
  font-variant-numeric: tabular-nums;
  color: var(--color-text);
}

/* ─── Sync notice: an edit of yours that is not what the board says ─────
   Danger red, not the banner's amber, and deliberately so: amber already
   means "changed since your last view" on the cells and on every other
   banner, and this is a different statement — *your* value is gone. Two
   meanings sharing one colour is how a signal stops being read. Everything
   it says is also in the text, so the colour carries nothing on its own
   (1.4.1).
   ────────────────────────────────────────────────────────────────────── */

.banner-sync {
  background: var(--color-danger-surface);
  border-color: var(--color-danger-border);
  border-left-color: var(--color-danger);
}
.banner-sync-head {
  margin: 0 0 var(--space-2);
  font-weight: var(--weight-semibold);
}
.banner-sync-list {
  margin: 0 0 var(--space-3);
  padding-left: var(--space-4);
}
.banner-sync-list li + li { margin-top: var(--space-1); }

/* The cell itself. A dotted underline rather than a fill: the changed-since-
   last-view amber already owns the cell background, and a cell can be both. */
td.cell-superseded {
  text-decoration: underline var(--color-danger) dotted;
  text-underline-offset: 0.2em;
  text-decoration-thickness: 2px;
}

/* ─── Tables ───────────────────────────────────────────────────────────── */

section { margin-bottom: var(--space-6); }

table {
  width: 100%;
  border-collapse: separate;
  border-spacing: 0;
  table-layout: fixed;
  font-size: var(--text-sm);
  background: var(--table-bg);
  /* MAC: a grouped inset list — hairline edge and macOS card radius.
     NOT overflow:hidden — that would make the table its own scrollport and
     stop the sticky Date column from pinning at <= 64rem. The corner cells
     carry the radius instead. */
  border: var(--hairline) solid var(--color-border);
  border-radius: var(--radius-md);
}
/* MAC: follow the container radius on the corner cells. */
table > thead > tr:first-child > th:first-child { border-top-left-radius: calc(var(--radius-md) - 1px); }
table > thead > tr:first-child > th:last-child { border-top-right-radius: calc(var(--radius-md) - 1px); }
table > tbody > tr:last-child > td:first-child { border-bottom-left-radius: calc(var(--radius-md) - 1px); }
table > tbody > tr:last-child > td:last-child { border-bottom-right-radius: calc(var(--radius-md) - 1px); }

caption {
  text-align: left;
  font-weight: var(--weight-semibold);
  padding: var(--space-1) 0;
  color: var(--color-text);
}

th,
td {
  /* MAC: sub-pixel hairlines, the way AppKit draws separators on Retina.
     Controlled by --hairline in tokens.css. */
  border-right: var(--hairline) solid var(--table-gridline);
  border-bottom: var(--hairline) solid var(--table-gridline);
  padding: var(--table-cell-pad-y) var(--table-cell-pad-x);
  vertical-align: top;
  color: var(--color-text);
  overflow-wrap: break-word;
}
th:last-child,
td:last-child { border-right: 0; }
tbody tr:last-child > td { border-bottom: 0; }

th {
  background: var(--table-header-bg);
  color: var(--table-header-text);
  /* MAC: AppKit column headers are small, semibold and sentence case —
     not letter-spaced small caps. */
  font-size: var(--text-xs);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-normal);
  text-transform: none;
  text-align: left;
  white-space: nowrap;
  border-bottom: var(--hairline) solid var(--color-border);
}

/* ── #31 The zebra stripe was winning fights it should not be in ─────────
   `tbody tr:nth-child(even) > td` is (0,1,3): more specific than the editing
   chrome (`tr.leg-editing > td`, (0,1,2)), the duty row, the changed cell and
   the phone card's header strip. Every one of those had to carry !important
   to beat a background nobody was arguing with — nine of them, and each one
   then had to be beaten by the next thing, which is how a stylesheet ends up
   with 35 of these.

   A cascade layer settles it by precedence instead of by weight: unlayered
   rules always beat layered ones, whatever their specificity. The stripe is
   the base coat, so it goes in a layer and stops competing. Nothing else in
   the file is layered, so nothing else changes. */
@layer stripe {
  tbody tr:nth-child(even) > td { background: var(--table-stripe); }

  /* The flight tables likewise (#276): a leg's marker, passenger, briefing
     and duty rows sit between legs, so on the admin board every leg row
     landed on the same parity and none alternated. `createFlightTable`
     (ts-board.js) sets `row-alt` on every second LEG row instead. */
  .flight-table tbody tr:nth-child(even) > td { background: transparent; }
  .flight-table tbody tr.row-alt > td { background: var(--table-stripe); }
}

tbody tr.selected,
tbody tr.selected > td {
  background: var(--color-accent-subtle);
}

/* ─── Flight tables ────────────────────────────────────────────────────── */

/* Horizontal overflow is contained by the phase root, not the page body.
   These are the elements the edit action bar anchors inside, so vertical
   space is reserved (never clipped) by the JS pad. */
#flight-blocks,
#finished-root,
#in-progress-root,
#other-metal-root,
#location-root {
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scrollbar-width: thin;
  -webkit-overflow-scrolling: touch;
}

.flight-block {
  margin-bottom: var(--space-6);
  padding-bottom: var(--space-1);
}

.flight-table,
.other-table {
  margin-bottom: var(--space-2);
  box-shadow: var(--shadow-xs);
}
/* The board's tables share one column grid sized to the longest content in
   each column, so they shrink-wrap and are measured together. */
.flight-table {
  table-layout: auto;
  width: max-content;
  max-width: none;
}
/* ── #15 Other metal is six short columns, not a shared grid ─────────────
   It shrink-wrapped to ~490px on a 1400px canvas, and because its column
   widths are percentages, 12% of 490px was not enough for "30 Sep" — the
   Date column wrapped in a table with a thousand pixels to spare. It sizes
   to its container now, capped at a readable measure rather than stretched
   across the whole window. */
.other-table {
  table-layout: fixed;
  width: 100%;
  max-width: 64rem;
}

/* Column widths are assigned to <col> by TripSchedule.syncFlightColumnWidths
   so that every table on the board shares one grid. Leave them auto here. */
.flight-table col { width: auto; }

.flight-table th,
.flight-table td {
  white-space: nowrap;
  text-align: center;
  vertical-align: middle;
  overflow-wrap: normal;
}
/* ── #13 / #352 Alignment by what the column holds ────────────────────────
   Everything was centred, so Dep/Arr strings of different length scattered
   and there was no left edge to run the eye down. Text reads from the left,
   so text columns are left throughout, heading and cells. A short numeric
   column (Block, Pax) is centred instead, heading and cells — a heading
   takes its column's alignment, everywhere (#352; a right edge with nothing
   else on the row to line up against just looked adrift).

   One rule for the value, by field, not by table: `[data-field]` is the
   marker every td already carries wherever that field appears, so the bare
   `td[data-field="pax"]` below reaches Other metal's Pax column as well as
   the flight tables' without repeating the selector per table. A `<th>`
   carries no `data-field` (only `scope="col"`), so the two short columns'
   headings are picked out by position, once per table, immediately below —
   there is no class to hang the rule on there instead. */
.flight-table th { text-align: center; }
.flight-table th[scope="col"] { text-align: left; }

.flight-table td[data-field="date"],
.flight-table td[data-field="etd"],
.flight-table td[data-field="eta"],
.flight-table td[data-field="dep"],
.flight-table td[data-field="arr"],
/* WORKLIST #414 (k): the crew's read-only Full board merges Dep/Arr into
   one Route cell, `data-field="route"` — left, same as the two it replaces. */
.flight-table td[data-field="route"],
/* WORKLIST #419 (a): and ETD/ETA into one Times cell, `data-field="times"`
   — left, same as the two it replaces. */
.flight-table td[data-field="times"],
.flight-table td[data-field="crew"],
.flight-table td[data-field="crewNotes"] { text-align: left; }
/* WORKLIST #414 (k): the Route cell's own arrow ("KDAL→KADS") — muted, the
   same style the Next duty card's own route arrow carries (user.css). In
   the base sheet, not a page's own: `createFlightTable` (`ts-board.js`) is
   shared shell, loaded on every board page. */
.leg-route-arrow {
  color: var(--color-text-muted);
  margin: 0 var(--space-1);
}

td[data-field="block"],
td[data-field="pax"] { text-align: center; }
/* Block, FPL (#290) and Pax: the sixth, eighth and ninth headings on the
   admin's eleven-column table (`.is-editable`; the seventh is #473's
   Actual − plan, left like its lines). WORKLIST #414 (k) and #419
   (a): the crew's read-only table (`.is-readonly`) merges Dep/Arr into one
   Route column and ETD/ETA into one Times column, so the same three
   headings are its fourth, fifth and sixth — position alone cannot pick
   them out on both shapes at once (rule 13's own note above: a `<th>`
   carries no `data-field`), so the two table shapes get their own selector
   rather than one shared by accident. */
/* #512 (c): Dep · ETD and Arr · ETA are one heading each (colspan 2), so
   Block, FPL and Pax are the admin table's fourth, sixth and seventh. */
.flight-table.is-editable th:nth-child(4),
.flight-table.is-editable th:nth-child(6),
.flight-table.is-editable th:nth-child(7) { text-align: center; }
.flight-table.is-readonly th:nth-child(4),
.flight-table.is-readonly th:nth-child(5),
.flight-table.is-readonly th:nth-child(6) { text-align: center; }
.other-table th:nth-child(4) { text-align: center; }

/* #512 (c): Dep+ETD and Arr+ETA, two cells on the admin table. Each pair
   is two field cells under one heading (ts-board.js) — the edit machine
   opens each half on its own — joined here: no gridline between the
   airport and its time, a muted "·" before the time, both clocks kept
   ("KEWR Newark (UTC -4) · 2100lcl/0100z"). Above the phone width only;
   on the phone card each field keeps its own label (the ::before there). */
@media (min-width: 40.0625rem) {
  .flight-table.is-editable > tbody > tr > td[data-field="dep"],
  .flight-table.is-editable > tbody > tr > td[data-field="arr"] {
    border-right: 0;
    padding-right: 0;
  }
  .flight-table.is-editable > tbody > tr > td[data-field="etd"],
  .flight-table.is-editable > tbody > tr > td[data-field="eta"] {
    padding-left: var(--space-1);
  }
  .flight-table.is-editable > tbody > tr > td[data-field="etd"]::before,
  .flight-table.is-editable > tbody > tr > td[data-field="eta"]::before {
    content: "\00b7" / "";
    color: var(--color-text-muted);
    margin-right: var(--space-1);
  }
  /* An open editor is the cell's whole content: no "·" beside the input. */
  .flight-table.is-editable > tbody > tr > td.leg-cell-editing::before { content: none; }
}

/* Times, dates, codes and counts are read by scanning a column, so they get
   the mono face and tabular figures. */
/* Times, dates, codes and counts are read by scanning a column, so they get
   the mono face and tabular figures.

   They used to be --text-xs (12px) — a step BELOW Dep/Arr and level with the
   muted Notes column, which made the numbers the whole board exists for its
   smallest text. They are the primary content and now read like it. */
.flight-table td[data-field="date"],
.flight-table td[data-field="etd"],
.flight-table td[data-field="eta"],
/* WORKLIST #419 (a): the crew's merged Times cell reads times too. */
.flight-table td[data-field="times"],
.flight-table td[data-field="block"],
.flight-table td[data-field="pax"],
.flight-table td[data-field="crew"],
.other-table td[data-field="date"],
.other-table td[data-field="pax"] {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  color: var(--color-text);
  letter-spacing: var(--tracking-normal);
}

/* Dep / Arr lead with the ICAO code; the "(UTC ±N)" tail is supporting text. */
.flight-table td[data-field="dep"],
.flight-table td[data-field="arr"] {
  white-space: nowrap;
  font-weight: var(--weight-medium);
}

/* #458 (a): the cell grows with its note (wrapping), never an ellipsis. */
.flight-table td[data-field="crewNotes"] {
  white-space: normal;
  max-width: 28rem;
  overflow: hidden;
  text-overflow: clip;
  text-align: left;
  color: var(--color-text-muted);
  font-size: var(--text-xs);
}
/* WORKLIST #312: the Crew Notes cell is one line a leg — the marker
   (`ts-notes.js#drawNoteMarkers`) cuts the latest note with an ellipsis
   inside the cell's width, and the whole note is one tap away in the panel
   under the leg, so the cell no longer expands on hover or focus. A
   focused cell still sits above its neighbours. */
.flight-table td[data-field="crewNotes"]:focus-visible {
  position: relative;
  z-index: 3;
  background: var(--color-surface);
  box-shadow: var(--shadow-md);
}

/* Repeated headers on the 2nd+ Finished mini-tables collapse visually but
   stay in the accessibility tree, so cells keep their header association. */
#finished-root > table.flight-table:nth-of-type(n + 2) > thead > tr > th {
  padding: 0;
  border-width: 0;
  height: 0;
  line-height: 0;
  font-size: 0;
  color: transparent;
  overflow: hidden;
}

/* ─── Roving-tabindex cell grid ────────────────────────────────────────── */

/* Exactly one cell per board is tabbable; arrows move between them. */
.flight-table td[tabindex],
.other-table td[tabindex] {
  scroll-margin: var(--space-8);
}
/* Only editable rows may advertise themselves as clickable. */
tr.leg-row-clickable > td[tabindex] { cursor: pointer; }
.flight-table td[tabindex]:focus-visible,
.other-table td[tabindex]:focus-visible {
  outline: var(--focus-ring-width) solid var(--color-focus);
  outline-offset: calc(-1 * var(--focus-ring-width));
  border-radius: var(--radius-xs);
  position: relative;
  z-index: 2;
}

/* ─── "Changed since last view" ────────────────────────────────────────── */

/* The amber fill alone is a colour-only signal at ~1.1:1 against the
   surrounding cells — invisible to a colour-blind or low-vision user and
   silent to a screen reader. It now carries three cues:
     fill  (the familiar amber)
   + edge  (3px bar, 4.5:1 against the cell — satisfies 1.4.11)
   + text  (aria-label added in app.js — satisfies 1.4.1) */
.changed,
td.changed,
span.changed {
  background-color: var(--cell-changed-bg);
  color: var(--cell-changed-text);
  box-shadow: inset 3px 0 0 0 var(--cell-changed-edge);
  font-weight: var(--weight-semibold);
}
td.changed { position: relative; }

.block-title { background: transparent; }

/* ─── Bullet lists ─────────────────────────────────────────────────────── */

/* #688 (b) b2b: the admin board's Now / Later / Open flags lines are K17's
   (blocks/controls.css); the editable list, its × button and the
   "+ Add bullet" row went with them. ul.bullet-list stays
   for the derived flags under Open flags (ts-crew.js renderDerivedFlags). */

ul.bullet-list {
  margin: var(--space-2) 0;
  padding-left: 0;
  list-style: none;
}
ul.bullet-list li {
  position: relative;
  padding: var(--space-2) var(--space-10) var(--space-2) var(--space-5);
  border-radius: var(--radius-sm);
  border: var(--hairline) solid transparent;
  transition: background-color var(--motion-fast) var(--ease-out);
}
/* ── #18 Rhythm follows importance ──────────────────────────────────────
   A read-only bullet is prose, not a control. Giving it a 36px control
   height plus 4px of margin put dispatcher commentary on a 40px pitch while
   the flight rows it comments on sat on 36 — the page breathed hardest
   around its least important content. Read-only bullets are now set tighter
   than a leg row, so the board keeps the visual weight. */
ul.bullet-list li {
  min-height: 0;
  margin: 0;
  padding-block: var(--space-1);
  line-height: var(--leading-normal);
}
ul.bullet-list li::before { top: var(--space-1); }
ul.bullet-list li::before {
  content: "•";
  position: absolute;
  left: var(--space-2);
  top: var(--space-2);
  color: var(--color-text-subtle);
}

/* ─── Other metal ──────────────────────────────────────────────────────── */

/* A board date is scanned down a column; it never wraps. */
.other-table td[data-field="date"],
.other-table th:first-child { white-space: nowrap; }

/* ─── Users / crew admin ───────────────────────────────────────────────── */

.users-table { table-layout: auto; font-size: var(--text-sm); width: 100%; }
.users-table th,
.users-table td { white-space: nowrap; }
.users-table td.wrap { white-space: normal; }

/* ─── People spreadsheet (merged Crew + Users) ─────────────────────────── */

.users-table-wrap {
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scrollbar-width: thin;
  margin-bottom: var(--space-4);
}

/* ─── Phase sections ───────────────────────────────────────────────────── */

/* ── #16 One treatment per kind of section ──────────────────────────────
   Three phases had three different shapes: Finished was a tinted, ruled band,
   In progress a 2px coloured heading rule, Next flying a 1px one — so the eye
   had to learn three signals for one idea. Now there are two, and each means
   something: a PHASE heading carries a 2px rule in its own phase colour; a
   prose section (Now, Later, Open flags) keeps the neutral hairline. No boxes.
   Finished still recedes, but through muted text and a muted heading rather
   than a wash that offset its tables from the ones above. */
.finished-section {
  margin-top: var(--space-2);
  margin-bottom: var(--space-6);
}
.finished-heading {
  color: var(--color-text-muted);
  font-size: var(--text-xl);
  margin-top: 0;
  border-bottom: 2px solid var(--phase-finished-edge);
  padding-bottom: var(--space-2);
}
/* Finished is history: recede it without dropping below AA. */
.finished-section .flight-table td { color: var(--color-text-muted); }
.finished-section .flight-table td.changed { color: var(--cell-changed-text); }

.finished-hint,
.finished-empty {
  font-size: var(--text-xs);
  color: var(--color-text-muted);
  margin: var(--space-2) 0;
}
.finished-empty { font-style: italic; }
/* #425 (d): Finished's Last 24h | All toggle and the count beside it. Not
   the retired block-controls class, whose button rules restyled the toggle. */
.finished-controls {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2) var(--space-3);
  margin: var(--space-2) 0 var(--space-3);
}


/* Airborne right now — one live marker, not a colour wash. The accent lives
   on the section, pulled back by its own width, so the table underneath
   keeps the plain hairline border (and the same column grid) every other
   flight table has — #168: a border on the table itself made this table
   2px wider than the rest and stepped every column boundary. */
.in-progress-section {
  margin-top: var(--space-6);
  border-left: 3px solid var(--phase-progress-accent);
  margin-left: -3px;
}
.in-progress-heading {
  color: var(--phase-progress-accent);
  border-bottom: 2px solid var(--phase-progress-accent);
  padding-bottom: var(--space-2);
}
#next-flying > h2 {
  border-bottom: 2px solid var(--phase-next-accent);
  padding-bottom: var(--space-2);
}

/* ─── Duty end, as a row inside its block's table ──────────────────────── */

.duty-row > .duty-cell {
  padding: var(--space-2) var(--space-4);
  background: var(--phase-finished-bg);
  border-left: 3px solid var(--color-border-strong);
  border-top: var(--hairline) solid var(--color-border);
  border-bottom: var(--hairline) solid var(--color-border);
}
/* #715: where a table is a block (the lines form, below the full table's width) its row group is an anonymous table
   as wide as its content; the Duty end's group, and its row, span the card instead. */
.or-table.or-lines > tbody:has(> tr.duty-row), .k-phone .or-table > tbody:has(> tr.duty-row) { display: block; }
.or-table.or-lines > tbody > tr.duty-row, .k-phone .or-table > tbody > tr.duty-row { display: block; }
/* The cell too: in a block row a table-cell sits in an anonymous table as wide as its content, so its background stopped half-way. */
.or-table.or-lines > tbody > tr.duty-row > td.duty-cell, .k-phone .or-table > tbody > tr.duty-row > td.duty-cell { display: block; }
/* It is a rule across the table, not a row of data: no stripe, no hover. */
.flight-table tbody tr.duty-row:hover > td,
.flight-table tbody tr.duty-row:nth-child(even) > td {
  background: var(--phase-finished-bg);
}

.duty-card-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-2);
  font-size: var(--text-sm);
  color: var(--color-text);
}
.duty-arr {
  color: var(--color-text);
  font-weight: var(--weight-semibold);
  font-size: var(--text-sm);
}
.duty-arr { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }
/* #593 round 15c (S2 @duty): on the admin board the duty end's time and each
   "Duty 8.9 h · rest 24 h+" are pieces that never break inside. */
.flight-table.is-editable .duty-arr,
.flight-table.is-editable .duty-piece { white-space: nowrap; }
/* The duty card's old label/field editor is gone — hotel and airline are leg
   fields edited from the selected row's action bar. `.duty-row` is now the
   table row rendered by renderDutyRow, styled above; it used to be a flex
   container in here, which broke the row's colspan the moment they collided. */

/* ─── Passengers, as a row under the leg they are on (#143) ─────────────
   The Pax COUNT stays in its own cell; this is who those people are. Quiet
   by design: one line, the same indent as a duty end, and nothing at all on
   a leg with no names — which is every leg the sheet has ever produced. On
   the admin board the row carries the editor, and the Pax cell carries the
   names toggle that opens it (#276). */

.pax-row > .pax-cell {
  padding: var(--space-1) var(--space-4);
  border-left: 3px solid var(--color-border);
  border-bottom: var(--hairline) solid var(--color-border);
  white-space: normal;
  vertical-align: middle;
}
/* WORKLIST #414 (n): the "Pax" tag (`.duty-tag`, no `vertical-align` of its
   own — the default `baseline`) and the list beside it (`.pax-list`,
   `vertical-align: middle` below) sat on two different lines. One
   baseline: the tag matches the list, scoped to this cell so the Hotel/
   Airline duty-end tags (`.duty-stay .duty-tag`) keep their own. */
.pax-cell > .duty-tag {
  vertical-align: middle;
}
/* Data about a leg, not a leg: no hover, like the duty row. It is never
   striped either — the stripe follows `tr.row-alt`, which only leg rows
   carry (#276). */
.flight-table tbody tr.pax-row:hover > td {
  background: var(--table-bg);
}

.pax-list {
  display: inline-flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-1) var(--space-3);
  margin: 0 var(--space-2);
  padding: 0;
  list-style: none;
  vertical-align: middle;
}
/* A listed passenger (#317): the directory's name, the passport used on a
   leg between two countries, and one Remove — never edited in place; the
   Pax data tab is where a passenger's details are written. */
.pax-item {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--text-sm);
}
.pax-nat {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-wide);
}
.pax-del {
  font-size: var(--text-xs);
  min-height: 24px;
  display: inline-flex;
  align-items: center;
}
/* A link to a profile this device does not hold (#315's note) — said, not
   dropped. */
.pax-missing {
  font-size: var(--text-xs);
  font-style: italic;
  color: var(--color-text-muted);
}
.pax-passport {
  font-size: var(--text-xs);
  color: var(--color-text-muted);
}
/* "Passport used" — a real select, like the count's; none chosen, or none
   on file, is the owner's amber (#317). */
.pax-passport-select {
  font: inherit;
  font-size: var(--text-xs);
  min-height: 24px;
  padding: 0 var(--space-1);
  border: var(--hairline) solid var(--color-border-strong);
  border-radius: var(--radius-sm);
  background: var(--color-surface);
  color: var(--color-text);
  cursor: pointer;
}
.pax-passport-select.is-unchosen,
.pax-passport.pax-passport-missing {
  background: var(--cell-changed-bg);
  border: var(--hairline) solid var(--cell-changed-edge);
  border-radius: var(--radius-sm);
  color: var(--cell-changed-text);
}
.pax-passport.pax-passport-missing { padding: 0 var(--space-1); }

/* #318: a passport or visa within six months of expiry — the same amber as
   the passport menu's, on the leg's passenger row and the Pax data line.
   The reason lives in `title` and the accessible name, not here. */
.pax-doc-flag {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 20px;
  min-height: 20px;
  padding: 0 var(--space-1);
  font-size: var(--text-xs);
  font-weight: var(--weight-semibold);
  background: var(--cell-changed-bg);
  border: var(--hairline) solid var(--cell-changed-edge);
  border-radius: var(--radius-sm);
  color: var(--cell-changed-text);
  cursor: default;
}

/* The admin board's tally, "6 pax · 4 named" (#317, option 3): quiet when
   the two agree, amber when they differ — derived on every draw. */
.pax-tally {
  font-size: var(--text-xs);
  color: var(--color-text-muted);
  margin-right: var(--space-2);
  padding: 0 var(--space-1);
  border: var(--hairline) solid transparent;
  border-radius: var(--radius-sm);
}
.pax-tally.is-mismatch {
  background: var(--cell-changed-bg);
  border-color: var(--cell-changed-edge);
  color: var(--cell-changed-text);
  font-weight: var(--weight-semibold);
}
.flight-table td.pax-mismatch {
  box-shadow: inset 3px 0 0 var(--cell-changed-edge);
}

/* #365 (the owner): the picker on the left showing the count and a down
   arrow on its right, the standard + right of it. `.has-pax-add` no
   longer reserves space for an absolutely-positioned button — the picker
   and the + are two ordinary inline-block siblings, so the cell's own
   `text-align: center` (above) centres the pair as a unit, picker first.
   The bare text node the count used to be is gone (ts-board.js clears it):
   the select shows its own selected option, which the browser already
   draws with its own affordance — so the "down arrow" is native, not
   drawn here. */
select.pax-rest-select {
  display: inline-block;
  width: auto;
  min-width: 2.25rem;
  min-height: 24px;
  vertical-align: middle;
  margin-right: var(--space-1);
  padding: 0 var(--space-1);
  font: inherit;
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: var(--text-xs);
  text-align: center;
  border: var(--hairline) solid var(--color-border-strong);
  border-radius: var(--radius-sm);
  background: var(--color-surface);
  color: var(--color-text);
  cursor: pointer;
}
/* #512 (b): the per-row "+" under each date (#415 (c), #441) is gone, and
   with it the date cell's room for it. One "+ Add leg" row ends each
   block instead (ts-board.js `blockAddLegRow`), phone and computer: the
   standard "+" (`.btn-plus`, its 20×20 drawing the owner's, #377/#441)
   with the words beside it, the pair one button. "Insert here" is in a
   selected leg's edit bar (admin-schedule.js). */
.block-add-leg {
  margin: var(--space-2) 0 var(--space-4);
}
.block-add-leg-btn {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  min-height: var(--tap-target);
  padding: 0 var(--space-3) 0 var(--space-1);
  font: inherit;
  font-size: var(--text-sm);
  color: var(--color-text);
  background: transparent;
  border: 0;
  border-radius: var(--radius-sm);
  cursor: pointer;
}
.block-add-leg-btn .btn-plus { pointer-events: none; }
.block-add-leg-btn:hover .btn-plus { background: var(--color-surface-hover); }
.block-add-leg-btn:focus-visible {
  outline: var(--focus-ring-width) solid var(--color-focus);
  outline-offset: var(--focus-ring-offset);
}

/* Only positioning here — `.btn-plus` (ts-widgets.js, #364) owns the
   button's own look; this cell never restyles it. */
.pax-add-btn {
  vertical-align: middle;
}

/* The Add passenger menu itself: fixed, in viewport coordinates, for the
   reason `.nat-dropdown` is (the cell clips). A filter box, the directory,
   and "New passenger…" last. */
.pax-menu {
  position: fixed;
  z-index: var(--layer-popover);
  top: 0;
  left: 0;
  min-width: 14rem;
  max-width: 22rem;
  padding: var(--space-2);
  background: var(--color-surface-raised);
  border: var(--hairline) solid var(--color-border-strong);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-lg);
}
.pax-menu-filter {
  width: 100%;
  font-size: var(--text-sm);
  margin-bottom: var(--space-1);
}
.pax-menu-list {
  list-style: none;
  margin: 0;
  padding: 0;
  max-height: 15rem;
  overflow: auto;
}
/* #365 (the owner: "the cell height in the menu should be smaller"): the
   compact control height, not the 44px tap target — still >= 24px for a
   touch target, and the row itself stays the click target. */
.pax-menu-opt {
  display: block;
  width: 100%;
  min-height: var(--control-h-sm);
  text-align: left;
  font: inherit;
  font-size: var(--text-sm);
  padding: var(--space-2) var(--space-3);
  border: 0;
  /* #427: each option is a control, so its edge is >= 3:1 (WCAG 1.4.11). */
  border-bottom: var(--hairline) solid var(--color-border-strong);
  background: transparent;
  color: var(--color-text);
  cursor: pointer;
  white-space: nowrap;
}
.pax-menu-list > li:last-child > .pax-menu-opt { border-bottom: 0; }
.pax-menu-opt:hover,
.pax-menu-opt:focus {
  background: var(--color-accent-subtle);
  outline: none;
}
.pax-menu-opt:focus-visible { outline: var(--focus-ring-width) solid var(--color-focus); outline-offset: -2px; }
.pax-menu-new { color: var(--color-text-muted); font-style: italic; }

/* ── Standing reminders (#542): an accent box per reminder, under the next
   leg on both boards (Full, Compact, the admin board) and on the crew
   board's Next duty card — "Reminder · KTEB" then the text; several stack.
   Text at the body colour (AAA on the accent wash, both themes); the edge
   and the thick left rule at the accent border (>= 3:1 on the surface). ── */
.reminder-row > .reminder-cell {
  padding: var(--space-2) var(--space-3);
  border-bottom: var(--hairline) solid var(--color-border);
  white-space: normal;
}
.flight-table tbody tr.reminder-row:hover > td,
.flight-table tbody tr.reminder-row:nth-child(even) > td,
.compact-table tbody tr.reminder-row > td {
  background: var(--table-bg);
}
.reminder-stack {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}
.reminder-box {
  background: var(--color-accent-subtle);
  color: var(--color-text);
  border: 1px solid var(--color-border-accent);
  border-left-width: 4px;
  border-radius: var(--radius-sm);
  padding: var(--space-2) var(--space-3);
  text-align: left;
}
.reminder-box p { margin: 0; }
.reminder-head {
  font-weight: 700;
  font-size: var(--text-sm);
  color: var(--color-text);
}
.reminder-text {
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

/* ── The briefing panel (#186): a full-width row under the leg, on both
   boards, read-only. Never inside the Notes cell (see briefingRow,
   ts-board.js) and never striped or hover-highlighted, like the marker and
   pax rows above — this is data about the leg, not the leg itself. ── */
.briefing-row > .briefing-cell {
  padding: var(--space-2) var(--space-4);
  border-left: 3px solid var(--color-border);
  border-bottom: var(--hairline) solid var(--color-border);
  white-space: normal;
  vertical-align: middle;
}
.flight-table tbody tr.briefing-row:hover > td,
.flight-table tbody tr.briefing-row:nth-child(even) > td {
  background: var(--table-bg);
}
.briefing-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}
.briefing-item {
  padding-left: var(--space-2);
  border-left: 3px solid var(--color-border);
}
.briefing-item.severity-red { border-left-color: var(--color-danger-border); }
.briefing-item.severity-yellow { border-left-color: var(--cell-changed-edge); }
/* #488 Part 2: a green TripWatch result (a PreDeparture crew card, or what
   the office sees) — a border-and-text tag, as red's is. */
.briefing-item.severity-green { border-left-color: var(--color-success); }
.briefing-severity-tag {
  font: inherit;
  font-size: var(--text-2xs);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  padding: var(--space-1) var(--space-2);
  border-radius: var(--radius-full);
  border: var(--hairline) solid var(--color-border-strong);
}
/* Red keeps the panel's background plain (`--table-bg`, above): a filled
   `--color-danger-surface` wash falls just under AAA at this text size in
   the light theme, so red is a border-and-text tag rather than yellow's
   filled one. */
.briefing-severity-tag.severity-red {
  border-color: var(--color-danger-border);
  color: var(--color-danger);
}
.briefing-severity-tag.severity-green {
  border-color: var(--color-success);
  color: var(--color-success);
}
.briefing-severity-tag.severity-yellow {
  background: var(--cell-changed-bg);
  border-color: var(--cell-changed-edge);
  color: var(--cell-changed-text);
}
.briefing-text {
  margin: var(--space-1) 0 0;
  font-size: var(--text-sm);
}
.briefing-sources {
  margin: var(--space-1) 0 0;
  font-size: var(--text-xs);
}
.briefing-checked {
  margin: var(--space-1) 0 0;
  font-size: var(--text-xs);   /* #508 (l): not 2xs */
  color: var(--color-text-muted);
}

/* ── The board row is ONE line (#199) ───────────────────────────────────
   The severity word, how many items, when it was checked, and the link to
   briefing.html. Laid out as a wrapping flex line so the phone card keeps
   all four parts rather than truncating the last of them. ── */
.briefing-line {
  margin: 0;
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-3);
  font-size: var(--text-sm);
}
.briefing-count { font-weight: var(--weight-semibold); }
/* The item version of this class is a block `<p>` with a top margin; on the
   one-line row it is one span among four and the margin would push it off
   the baseline. */
.briefing-line > .briefing-checked { margin: 0; }
.briefing-link { font-size: var(--text-sm); }
/* The tag on this page sits on the WINDOW background, not the table's, and
   `--color-danger` reads 6.65:1 there at 11px — AA but under the AAA this
   repo holds itself to (scripts/test-contrast.js). One step darker down the
   same ramp clears it at 8.9:1, and the board row, on the lighter table
   background, keeps the colour it already passed with. */
#briefing-root .briefing-severity-tag.severity-red { color: var(--color-danger-hover); }
/* Same reason, same fix (#205): the tag inside a Compact card sits on the
   window background too — `#compact-root` is not a `.flight-table` — so it
   needs the same one-step-darker red the briefing page uses. */
#compact-root .briefing-severity-tag.severity-red { color: var(--color-danger-hover); }

#finished-root .flight-table { margin-bottom: var(--space-1); }

/* ─── DST notes ────────────────────────────────────────────────────────── */

.dst-notes {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-3);
  margin: var(--space-1) 0 var(--space-3);
}
.dst-note {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  font-size: var(--text-xs);
  font-weight: var(--weight-semibold);
  color: var(--cell-changed-text);
  background: var(--amber-50);
  border: var(--hairline) solid var(--cell-changed-edge);
  border-radius: var(--radius-sm);
  padding: var(--space-1) var(--space-3);
}
.finished-dst-notes { margin-bottom: var(--space-4); }

/* ═══════════════════════════════════════════════════════════════════════════
   INLINE LEG EDIT — cell-only, green selection chrome.
   Greens are the handover values, routed through tokens so dark mode can
   re-point them without changing the role of any rule.
   ═══════════════════════════════════════════════════════════════════════════ */

tr.leg-row-clickable { cursor: pointer; }

/* No blue row hover wash while editing (explicitly rejected UX). */
tr.leg-row-clickable:not(.leg-editing):hover > td { background: var(--table-row-hover); }
tr.leg-editing:hover > td,
tr.leg-row-clickable.selected:hover > td { background: var(--row-editing-bg); }

/* Selected / editing row */
tr.leg-editing > td,
tr.leg-editing:nth-child(even) > td {
  background: var(--row-editing-bg);
}

/* ─── Where each aeroplane is ──────────────────────────────────────────────
   Derived, so it is styled as a statement rather than as an editable bullet:
   no delete affordance, no contenteditable, and a left rule that marks it as
   coming from the board rather than from somebody's typing. */
.position-list {
  margin: 0 0 var(--space-3);
  padding: 0;
  list-style: none;
}
.position-line {
  padding: var(--space-2) var(--space-3);
  border-left: 3px solid var(--color-border-strong);
  background: var(--color-surface-sunken);
  border-radius: 0 var(--radius-xs) var(--radius-xs) 0;
  margin-bottom: var(--space-1);
  font-size: var(--text-sm);
  color: var(--color-text);
}
.position-when { color: var(--color-text-muted); }

/* ─── Block meta: which aircraft, and who was PIC ──────────────────────────
   A sibling of the block title, never inside it — the title is contenteditable
   on the admin board, so anything placed in it becomes part of what the
   dispatcher types. Renders nothing at all when a single-aircraft operation
   has no tail and no PIC to show. */
.block-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3);
  margin: calc(-1 * var(--space-1)) 0 var(--space-2);
  font-size: var(--text-sm);
  color: var(--color-text-muted);
}
.block-fleet {
  font-weight: var(--weight-semibold);
  color: var(--color-text);
  font-variant-numeric: tabular-nums;
}
.block-pic-static {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
}
/* A PIC nobody chose reads as provisional, because it is: it is the captain,
   inferred, until somebody says otherwise. */
.block-pic-static.is-derived { font-style: italic; }
/* #471: the tail's maintenance note, and its editor for who may set it. */
.block-mx-note {
  display: inline-flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  color: var(--color-text);
}
/* #762 (759-b): on touch the note's button is a 44 × 44 target, its box and so its hit box (the ::after of .btn is
   no wider than the box's 44 there); the icon drawn as before, centred in it. */
@media (pointer: coarse) {
  .btn.btn-mini.block-mx-edit { min-width: var(--tap-target); min-height: var(--tap-target); }
}

/* ─── PIC on a sector ──────────────────────────────────────────────────
   The Crew cell already lists who is aboard, so the one who commands is
   marked there rather than given a column of its own. Weight plus an
   underline, never colour: a state a sighted user reads off a colour alone
   fails WCAG 1.4.1, and the cell has no room for a badge. */
.crew-pic {
  font-weight: var(--weight-semibold);
  text-decoration: underline;
  text-decoration-thickness: from-font;
  text-underline-offset: 2px;
}
/* #277: on the admin board a captain's code is the control that makes them
   PIC of the sector. The PIC keeps the solid underline above; the other
   captains get a dotted one on hover and a focus ring, so the one mark that
   means "commands" is never borrowed. A first officer's code is plain text. */
.crew-code.is-captain {
  cursor: pointer;
  border-radius: var(--radius-xs);
}
.crew-code.is-captain:not(.crew-pic):hover {
  text-decoration: underline dotted;
  text-underline-offset: 2px;
}

/* ─── #513 (e): the captain's code is a pill button ─────────────────────
   Was a bare 15x19 px span[role=button]. A hairline edge, 32 px tall, 44 on
   a touch screen (`--tap-target`); the PIC keeps its bold and underline. The
   shape is the control's (`--radius-control`, docs/contracts/styles.md —
   a pill shape is for tags, never a button). */
.crew-code.is-captain {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: var(--control-h-sm);
  min-width: var(--control-h-sm);
  padding-inline: var(--space-2);
  border: var(--hairline) solid var(--color-border-strong);
  border-radius: var(--radius-control);
  background: var(--color-surface);
  vertical-align: middle;
}
/* The first pill's text stays under its column heading (#360). */
td > .crew-code.is-captain:first-child {
  margin-inline-start: calc(-1 * (var(--space-2) + var(--hairline)));
}
@media (pointer: coarse) {
  .crew-code.is-captain {
    min-height: var(--tap-target);
    min-width: var(--tap-target);
  }
}

/* ─── #464: Contract Pilot not sourced ────────────────────────────────
   Admins only (the page decides). The stand-in's code in a Crew cell, and
   the words on the trip heading, on a yellow fill within 10 days of the
   leg and a red one within 5. Never colour alone: the code carries the
   words beside it (visually hidden) and as its title, the heading says
   them outright. */
.cp-alert {
  border-radius: var(--radius-xs);
  padding: 0 var(--space-1);
  font-weight: var(--weight-semibold);
}
.cp-alert-soon {
  background: var(--cp-soon-bg);
  color: var(--cp-soon-text);
}
.cp-alert-urgent {
  background: var(--cp-urgent-bg);
  color: var(--cp-urgent-text);
}
.cp-alert-heading {
  font-size: var(--text-sm);
  white-space: normal;
}

/* The row is DEFINED by its fill and the action bar below it.
   The bar carries a hairline edge in the same green. */
tbody tr.leg-editing,
tbody tr.leg-editing.selected {
  outline: none;
  outline-width: 0;
}
.flight-table tr.leg-editing > td[data-field],
.other-table tr.leg-editing > td[data-field] {
  background: var(--row-editing-bg);
  padding: var(--table-cell-pad-y) var(--table-cell-pad-x);
  vertical-align: middle;
  overflow: hidden;
}

/* On a phone the row is a card and each cell's ::before label sits in the
   gutter on a negative margin — which overflow:hidden above clipped clean off.
   Editing showed eight green rows with no way to tell ETD from ETA. */
@media (max-width: 40rem) {
  .flight-table tr.leg-editing > td[data-field],
.other-table tr.leg-editing > td[data-field] { overflow: visible; }
}

/* Hover on a non-active cell while editing: two shades lighter. */
.flight-table tr.leg-editing > td[data-field]:not(.leg-cell-editing):hover,
.other-table tr.leg-editing > td[data-field]:not(.leg-cell-editing):hover {
  background: var(--cell-editing-hover);
}

/* Active edit cell: white bg. */
.flight-table tr.leg-editing > td.leg-cell-editing,
.other-table tr.leg-editing > td.leg-cell-editing,
.flight-table tr.leg-editing > td.leg-cell-editing:focus-within,
.other-table tr.leg-editing > td.leg-cell-editing:focus-within {
  background: var(--cell-editing-bg);
  position: relative;
  z-index: 1;
}

/* Never striped, never hovered, never part of the keyboard grid. */
tbody tr.leg-toolbar-row:nth-child(even) > td { background: var(--editbar-bg); }
tr.leg-toolbar-row:hover > td { background: var(--editbar-bg); }
/* #513 (d): a leg is now opened to reach its Pax picker, so a control inside
   the open row sits on the green editing tint, where the control edge
   (--color-border-strong) reads 2.9:1; the muted text colour clears 3:1. */
tr.leg-editing .btn-plus,
tr.leg-toolbar-row .btn,
.leg-inline-bar .btn { border-color: var(--color-text-muted); }

/* Frozen columns while a row is in edit — widths come from JS. */
table.leg-cols-frozen,
/* The second place: syncFlightColumnWidths writes these widths as INLINE
   styles with setProperty(..., 'important') while a row is being edited, so
   the column grid cannot reflow under the cursor. An inline !important can
   only be answered by another !important — that is the rule, not a smell. */
table.flight-table.leg-cols-frozen {
  table-layout: fixed !important;
  width: auto;
  max-width: none !important;
}
table.leg-cols-frozen td,
table.leg-cols-frozen th {
  box-sizing: border-box;
  overflow: hidden;
}

.leg-cell-input {
  display: block;
  width: 100%;
  max-width: 100%;
  min-width: 0;
  box-sizing: border-box;
  font: inherit;
  font-size: inherit;
  line-height: var(--leading-snug);
  color: var(--color-text);
  padding: 0;
  margin: 0;
  border: none;
  border-radius: 0;
  background: transparent;
  box-shadow: none;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* The white cell + 1px frame IS the focus indicator here, so the ring is
   suppressed deliberately rather than by accident. */
.leg-cell-input:focus,
.leg-cell-input:focus-visible {
  outline: none;
  background: transparent;
  box-shadow: none;
}
textarea.leg-cell-input {
  min-height: var(--measure-line);
  max-height: var(--measure-line);
  resize: none;
}

/* ─── The date field (#347) ───────────────────────────────────────────────
   One control on every date field (ts-widgets.js `TS.dateField`). #307's
   calendar button and the `calc(24px + 2ch)` it padded the date cell with
   are gone: a click in the cell opens the calendar, so the column is as
   wide as its text. The cell stays the positioning box #314's held-year
   prompt hangs from. */
td[data-field="date"] {
  position: relative;
}
/* What typed text was read as, beside the field before it is saved. */
.date-field-reading {
  display: inline-block;
  margin-inline-start: var(--space-2);
  font-size: var(--text-xs);
  color: var(--color-text-muted);
  white-space: nowrap;
}
.date-field-reading.is-unread { color: var(--color-danger); }
/* The calendar: top layer (`popover="manual"`), placed under its field by
   the widget. No `display` here — the UA's own `[popover]:not(:popover-open)`
   rule is what hides it, and an author `display` would win over it. */
.date-cal {
  position: fixed;
  inset: auto;
  margin: 0;
  padding: var(--space-2);
  width: max-content;
  border: var(--hairline) solid var(--color-border-strong);
  border-radius: var(--radius-sm);
  background: var(--color-surface-raised);
  color: var(--color-text);
  box-shadow: var(--shadow-lg);
  font-family: var(--font-sans);
  font-size: var(--text-sm);
  font-weight: var(--weight-normal);
  white-space: normal;
  text-align: center;
  z-index: 50;
}
.date-cal-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-1);
  margin-bottom: var(--space-1);
}
.date-cal button {
  min-width: 32px;
  min-height: 32px;
  padding: 0 var(--space-1);
  border: var(--hairline) solid transparent;
  border-radius: var(--radius-xs);
  background: transparent;
  color: var(--color-text);
  font: inherit;
  cursor: pointer;
}
.date-cal button:hover:not([disabled]) { background: var(--color-surface-hover); }
.date-cal button:focus-visible {
  outline: var(--focus-ring-width) solid var(--color-focus);
  outline-offset: var(--focus-ring-offset);
}
.date-cal .date-cal-title { flex: 1; font-weight: var(--weight-semibold); }
.date-cal .date-cal-title[aria-disabled="true"] { cursor: default; }
.date-cal-grid { display: grid; gap: 2px; }
.date-cal-days { grid-template-columns: repeat(7, 32px); }
.date-cal-years,
.date-cal-decades { grid-template-columns: repeat(3, 72px); }
.date-cal-wd {
  font-size: var(--text-2xs);
  color: var(--color-text-muted);
  line-height: 24px;
}
.date-cal .is-today { border-color: var(--color-border-strong); }
.date-cal .is-selected,
.date-cal [aria-pressed="true"] {
  background: var(--color-accent);
  color: var(--color-on-accent);
}
.date-cal .is-selected:hover:not([disabled]),
.date-cal [aria-pressed="true"]:hover { background: var(--color-accent-hover); }
.date-cal button[disabled] { color: var(--color-text-subtle); cursor: default; opacity: 0.6; }
/* The device's picker on a board cell (coarse pointer): rendered only for
   the moment it is open, pinned under the cell so its sheet never covers
   the text. #366: transparent, never clipped — iOS presents no picker for
   a clipped input — and 16px, so its focus never zooms the page. */
td[data-field="date"] .date-field-native {
  position: absolute !important;
  left: 0;
  top: 100%;
  width: 100%;
  height: 1px;
  margin: 0;
  padding: 0;
  border: 0;
  font-size: 16px;
  opacity: 0;
  pointer-events: none;
}

/* ─── Airport / crew autocomplete ──────────────────────────────────────── */

/* A cell being edited is overflow:hidden so its input cannot exceed the frozen
   column width. The Dep/Arr suggestion list has to break out of exactly that
   clip, and sit above the rows beneath it.

   `has-suggest` marks every cell that holds a popover control, and since
   #201 that is three: the two Dep/Arr cells, the People tab's Nationality
   cell and the passenger row. The last two carry CHIPS as well as a list,
   and the passenger row sits inside `table.leg-cols-frozen td { overflow:
   hidden }`, which would cut the fourth chip off. */
.flight-table tr.leg-editing > td.leg-cell-editing.has-suggest,
.other-table tr.leg-editing > td.leg-cell-editing.has-suggest,
table.leg-cols-frozen tr.leg-editing > td.leg-cell-editing.has-suggest,
#users-table td.has-suggest,
table.leg-cols-frozen tr.pax-row > td.pax-cell.has-suggest,
.flight-table tr.pax-row > td.pax-cell.has-suggest {
  overflow: visible;
  z-index: var(--layer-popover);
}
/* Dep/Arr columns are narrow; the list is sized to its content, not the cell. */
td.has-suggest > .airport-dropdown {
  right: auto;
  min-width: max(100%, 17rem);
  width: max-content;
  max-width: 26rem;
}
.airport-option {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  white-space: nowrap;
}

.airport-dropdown {
  position: absolute;
  z-index: var(--layer-popover);
  left: 0;
  right: 0;
  top: calc(100% + var(--space-1));
  max-height: 15rem;
  overflow: auto;
  background: var(--color-surface-raised);
  border: var(--hairline) solid var(--color-border-strong);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-lg);
}

.airport-option {
  display: block;
  width: 100%;
  min-height: var(--tap-target);
  text-align: left;
  font: inherit;
  font-size: var(--text-sm);
  padding: var(--space-2) var(--space-3);
  border: 0;
  border-bottom: var(--hairline) solid var(--color-border);
  background: transparent;
  color: var(--color-text);
  cursor: pointer;
}
.airport-option:last-child { border-bottom: 0; }
.airport-option:hover,
.airport-option:focus,
.airport-option[aria-selected="true"] {
  background: var(--color-accent-subtle);
  outline: none;
}
.ao-code { font-weight: var(--weight-semibold); font-family: var(--font-mono); }
.ao-city { color: var(--color-text-muted); }
.ao-cc { color: var(--color-text-muted); font-size: var(--text-2xs); }

/* ─── The nationality control (#201) ───────────────────────────────────────
   Chips for what has been chosen, a text box for choosing another, and the
   airport suggest's own dropdown shape reused rather than re-invented: the
   two controls are the same gesture over two tables, and a second popover
   that looked different would say they were not.

   The chips and the box sit in the flow of their cell; `has-suggest` on
   that cell (above) is what stops it clipping the fourth chip.

   The LIST does not sit in the cell at all. `.nat-dropdown` is
   `position: fixed` and `attachNationalitySuggest`'s `place()` puts it in
   viewport coordinates, because `overflow: visible` on the cell cannot
   answer a clip that belongs to an ancestor — `.users-table-wrap` is
   `overflow-x: auto` (so a scroll container in both axes) and the passenger
   row's table clips its cells — nor a list that would run off the bottom of
   the window on the last row of either table. `place()` opens it upwards
   when there is no room below. */
.nat-suggest {
  position: relative;
  display: inline-flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-1);
  min-width: 9rem;
}
.nat-chips {
  display: contents;
  margin: 0;
  padding: 0;
  list-style: none;
}
.nat-chip {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  min-height: var(--control-h-sm);
  padding: 0 0 0 var(--space-2);
  border: var(--hairline) solid var(--color-border-strong);
  border-radius: var(--radius-md);
  background: var(--color-surface-raised);
  color: var(--color-text);
  font-size: var(--text-xs);
  white-space: nowrap;
}
.nat-chip-name { color: var(--color-text); }
/* A real button, not a glyph in a span: removing a passport is an action, and
   it has to be reachable by keyboard like every other one on this row. */
.nat-chip-x {
  border: 0;
  background: none;
  position: relative;
  align-self: stretch;
  min-width: var(--control-h-sm);
  padding: 0 var(--space-1);
  font: inherit;
  font-size: var(--text-sm);
  line-height: var(--leading-none);
  color: var(--color-text);
  cursor: pointer;
}
.nat-chip-x:hover,
.nat-chip-x:focus { color: var(--color-danger); outline: none; }
.nat-input {
  font: inherit;
  font-size: var(--text-sm);
  width: 8rem;
  min-width: 6rem;
  padding: var(--space-1);
  border: var(--hairline) solid var(--color-border-strong);
  border-radius: var(--radius-sm);
  background: var(--color-surface);
  color: var(--color-text);
}
.nat-input:disabled {
  background: var(--color-surface-sunken);
  color: var(--color-text-muted);
}
.nat-dropdown {
  /* fixed, not absolute — see the note above. `place()` sets top and left;
     the width is content-sized, so it is the same list wherever it opens. */
  position: fixed;
  z-index: var(--layer-popover);
  top: 0;
  left: 0;
  min-width: 14rem;
  width: max-content;
  max-width: 22rem;
  max-height: 15rem;
  overflow: auto;
  background: var(--color-surface-raised);
  border: var(--hairline) solid var(--color-border-strong);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-lg);
}
.nat-option {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-2);
  width: 100%;
  min-height: var(--tap-target);
  text-align: left;
  font: inherit;
  font-size: var(--text-sm);
  padding: var(--space-2) var(--space-3);
  border: 0;
  border-bottom: var(--hairline) solid var(--color-border);
  background: transparent;
  color: var(--color-text);
  cursor: pointer;
  white-space: nowrap;
}
.nat-option:last-child { border-bottom: 0; }
.nat-option:hover,
.nat-option:focus,
.nat-option[aria-selected="true"] {
  background: var(--color-accent-subtle);
  outline: none;
}
.nat-option-name { color: var(--color-text); }
.nat-option-code {
  font-family: var(--font-mono);
  font-weight: var(--weight-semibold);
  color: var(--color-text-muted);
  font-size: var(--text-xs);
}

/* ─── Location timeline ────────────────────────────────────────────────── */

.badge-home {
  background: var(--color-success-surface);
  border-color: var(--color-success);
  color: var(--color-success);
}
/* Small only in ink: the ::after tap target on .btn keeps every one of these
   at the 44px touch minimum. */
.btn.btn-mini {
  min-height: var(--control-h-sm);
  padding: var(--space-1) var(--space-2);
  font-size: var(--text-xs);
  border-radius: var(--radius-control);
}
@media (hover: hover) {
}

/* ─── Footer ───────────────────────────────────────────────────────────── */

footer {
  margin-top: var(--space-10);
  padding-top: var(--space-4);
  border-top: var(--hairline) solid var(--color-border);
  font-size: var(--text-xs);
  color: var(--color-text-muted);
}

/* ═══════════════════════════════════════════════════════════════════════════
   RESPONSIVE — desktop is the base; these layers carry it to iPad and iPhone.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── #24 The anti-zoom floor is for touch, not for everyone ──────────────
   iOS Safari zooms the viewport when you focus an input smaller than 16px, so
   every field carried `max(--text-base, 16px)`. On a Mac there is no zoom to
   prevent: the rule just made an editing row grow 20px and read a size larger
   than the row above it. It applies where the problem exists.
   #366: it sat above `.people-input` and `.leg-cell-input`, whose own
   font-size (equal weight, later) won — every Pax data and Schedule field
   was 13px on the iPhone, focus zoomed the page, and the date picker was
   drawn over a zoomed, scrolled page with its Reset and ✓ cut off. It is
   here, after them, and a date field of any class is under it.
   `.people-input` and `.leg-inline-field .field-input` are admin.html's
   alone, so their rules live in admin.css, after every rule here; their
   floor went with them, into that sheet after those rules (#332). */
@media (pointer: coarse) {
  .field-input,
.leg-cell-input,
.users-table input,
.users-table select,
input[type="date"] {
    font-size: max(var(--text-sm), 16px);
  }
}

/* ── #371: the Users tab widened the whole phone screen ──────────────────
   `#users-table` is `table-layout: auto` with `white-space: nowrap` cells,
   scrolling inside `.users-table-wrap` (`overflow-x: auto`) — the CSS box
   model already contains it (the wrap's own `clientWidth` matches the
   viewport, `scrollWidth` is the table's full width). But a mobile
   browser's `width=device-width` viewport is sized from the page's
   UNCONTAINED intrinsic layout, which walks past an `overflow: auto`
   ancestor: with an auto-layout table this wide anywhere in the document,
   the visual viewport widens to fit it and the whole page reads zoomed
   out — worse once a row opens for edit and gains still more width.
   `contain: layout` on a box stops that walk (it says nothing outside is
   sized by what is inside), which is exactly what a table CELL is for —
   except the nationality cell, whose `.nat-dropdown` (`ts-widgets.js`,
   `attachNationalitySuggest`) is `position: fixed` specifically because
   NO ancestor may become its containing block (that is how it escapes the
   wrap's clip and the window's edge); `contain: layout` makes an element
   the containing block for a fixed descendant same as a transform would,
   so that column is left out here on purpose. Table layout stays `auto`,
   nowrap stays, the table still scrolls inside its wrapper — only the
   page stops matching its width. */
@media (pointer: coarse) {
  #users-table td:not([data-field="nationality"]),
  #users-table th,
  .actuals-table td,
  .actuals-table th {
    contain: layout;
  }
}

/* ── Pointer-quality, not screen size. A touch device gets the full 44px
      target on every control, and hover-only affordances are dropped. ── */
@media (hover: none), (pointer: coarse) {
  /* `.tab-bar button`'s target is in admin.css, after its own rule (#332). */
  .toolbar button,
.btn,
button.btn {
    min-height: var(--tap-target);
    padding-inline: var(--space-4);
  }
  /* #508 (a)(b)(f): the segmented options, the disclosure summaries and the
     nationality chip's remove sit at the full touch target. */
  .ts-segmented-option { min-height: var(--tap-target); }
  /* #508 (f): the chip stays 32 px tall; its remove's hit area is 44 px. */
  .nat-chip-x::after {
    content: "";
    position: absolute;
    inset: 50% 0 0 50%;
    width: max(100%, var(--tap-target));
    height: max(100%, var(--tap-target));
    transform: translate(-50%, -50%);
  }

  /* On touch the Crew Notes cell keeps its one line (#312) and narrows;
     the whole note is the panel's, one tap away. */
  .flight-table td[data-field="crewNotes"] {
    max-width: 22rem;
  }
  /* A tap is a select, not a hover — drop the row hover wash entirely. */
  tr.leg-row-clickable:not(.leg-editing):hover > td { background: revert-layer; }

  /* #428 (b): the Pax row's 24px controls — the desktop look stays; only
     the coarse-pointer floor changes. */
  .pax-del,
  .pax-passport-select,
  select.pax-rest-select {
    min-height: var(--tap-target);
  }

  /* #428 (a) fix round: `.btn-plus`/`.pax-doc-flag`'s 44px hit area, moved
     here from the unconditional `.btn`-family group above — a mouse's
     click point never lands inside it, so the old date "+"'s copy
     (nested under a date cell's text, #415 (c), gone with #512 (b)) never stole a click
     meant for the cell (#347). The drawing stays #377's 20×20. */
  .btn-plus,
  .pax-doc-flag {
    position: relative;
  }
  .btn-plus::after,
  .pax-doc-flag::after {
    content: "";
    position: absolute;
    inset: 50% 0 0 50%;
    width: max(100%, var(--tap-target));
    height: max(100%, var(--tap-target));
    transform: translate(-50%, -50%);
    pointer-events: auto;
  }

  /* #428 (c) fix round: no `.date-cal button`/`.date-cal-days` floor here —
     `finePointer()` (ts-widgets.js #347) only ever draws the custom
     calendar under a fine primary pointer; `(hover: none), (pointer:
     coarse)` can never be true at the same time, so a rule inside this
     query could never reach it (the device's own picker stands in
     instead, #366) — a rule that can never match is not a fix. */
}

/* ── iPad portrait and below: pin the Date column while the board scrolls ── */
@media (max-width: 64rem) {
  body { padding-inline: var(--space-3); }

  .flight-table th:first-child,
.flight-table td[data-field="date"] {
    position: sticky;
    left: 0;
    z-index: 1;
    background: var(--table-bg);
    box-shadow: 1px 0 0 var(--table-gridline);
  }
  .flight-table tbody tr.row-alt > td[data-field="date"] {
    background: var(--table-stripe);
  }
  .flight-table th:first-child { background: var(--table-header-bg); z-index: 2; }
}

/* ── Tablet only: the board fits the viewport instead of scrolling off it.
   Below 40rem the table has already become cards, so these rules stop there —
   the card layout owns its own padding and the negative-margin field labels
   depend on it. ── */
@media (min-width: 40.0625rem) and (max-width: 64rem) {
  /* ── Nothing off the edge ─────────────────────────────────────────────
     At 820px the frozen column widths made the board 1270px wide inside a
     796px viewport: Block, Pax, Crew and Notes were simply not on screen,
     and nothing on the page said so. A tablet is wide enough for all nine
     columns if the long text is allowed to wrap, so below this breakpoint
     the table sizes itself to the viewport instead of to its content and
     the horizontal scroll disappears rather than being signposted.
     WORKLIST #419 fix round 3: `:not(.compact-table)` — the same exclusion
     #299 already had to add to this rule's sibling below (the phone/card
     breakpoint) after this `!important` silently beat Compact's own
     `table-layout: fixed` grid there too. This band (40.0625rem–64rem,
     the iPad width the owner's render showed a bloated Date column at)
     had never had it: Compact fell into `auto` layout with its `<colgroup>`
     hidden, so `user.css`'s `col.c-*` shares never applied at 1024px at
     all — the real cause of "about a fifth of the table", not merely a
     wrong percentage.
     WORKLIST #419 fix round 3: `:not(.is-readonly)` too — the crew Full
     board (`ts-board.js`'s `syncFlightColumnWidths`, the "one column grid
     sized to the longest content" `.flight-table`'s own default comment
     above describes) already measures and pins a correct, tight
     `table-layout: fixed` width on every board, crew Full included — this
     band's `!important` was discarding that real measurement in favour of
     a stretch-to-100% meant for the admin board's nine columns, inflating
     crew Full's Date column at 1024px the same way it inflated Compact's.
     Crew Full's own merged Route/Times (#414 (k), #419 (a)) is short
     enough now that it never needed the tablet wrap-instead-of-scroll fix
     this band exists for — only the admin board (`.is-editable`, unchanged
     here) still does.
     #593 round 15c (S2 G13): `:not(.bt-cols)` — the admin board's leg tables
     take C4's one set of column shares (blocks/built.css) in this band too. ── */
  .flight-table:not(.compact-table):not(.is-readonly):not(.bt-cols),
.other-table,
table.leg-cols-frozen {
    table-layout: auto !important;
    width: 100% !important;
    min-width: 0 !important;
  }
  .flight-table:not(.compact-table):not(.is-readonly):not(.bt-cols) > colgroup { display: none; }
  .flight-table > thead > tr > th,
.flight-table > tbody > tr > td[data-field],
.other-table > thead > tr > th,
.other-table > tbody > tr > td[data-field] {
    width: auto !important;
    min-width: 0 !important;
    max-width: none !important;
    white-space: normal;
    overflow: visible;
    text-overflow: clip;
    overflow-wrap: break-word;
  }
  /* Only the long prose columns wrap. Times, dates, counts and crew codes are
     read by scanning a column, so they keep their line whatever the width. */
  .flight-table > thead > tr > th,
.flight-table > tbody > tr > td[data-field="date"],
.flight-table > tbody > tr > td[data-field="etd"],
.flight-table > tbody > tr > td[data-field="eta"],
.flight-table > tbody > tr > td[data-field="block"],
.flight-table > tbody > tr > td[data-field="pax"],
.flight-table > tbody > tr > td[data-field="crew"] {
    white-space: nowrap;
  }
  /* A wrapped cell is taller, so text sits at the top of the row rather than
     floating in the middle of a two-line neighbour. */
  .flight-table > tbody > tr > td[data-field] { vertical-align: top; }
  .flight-table > thead > tr > th,
.flight-table > tbody > tr > td[data-field] {
    padding-inline: var(--space-2);
  }

}

/* #365: the Add button's old reservation here is gone with the absolute
   overlay it protected — the picker and the + are in normal flow now, so
   the generic `padding-inline` above is all `td.has-pax-add` needs at
   this breakpoint too. */

/* ── Phone: the 9-column table stops being a table. Each leg becomes a
      labelled card, driven entirely by data-field + CSS — no second render
      path in JS, so the edit / autosave / collect logic is untouched. ── */
@media (max-width: 40rem) {
  body { padding-inline: var(--space-2); }

  h1 { font-size: var(--text-xl); }
  h2 { font-size: var(--text-lg); margin-top: var(--space-6); }

  .app-chrome { gap: var(--space-2); }
  /* #425 (b): first line the name and ☰ Menu; second line the board's own
     controls (the crew board's toggles, user.css); the page buttons open
     from Menu as a list under the bar, in the order they had. #511 (d):
     Add flight block is the list's first item here (app.js moves it). */
  /* #511 (g): basis 0, so the name row (name and UTC chip) gives way
     rather than pushing the ☰ Menu to a line of its own. */
  .app-chrome .chrome-who { flex: 1 1 0; order: 1; min-width: 0; }
  .app-chrome button.chrome-menu-btn { display: inline-flex; order: 2; }
  .passkey-panel { order: 4; }
  .chrome-actions { display: none; }
  .app-chrome.is-menu-open .chrome-actions {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    position: absolute;
    top: 100%;
    right: var(--space-3);
    z-index: var(--layer-popover);
    min-width: 12rem;
    gap: 0;
    padding: var(--space-1) 0;
    background: var(--color-surface-raised);
    border: var(--hairline) solid var(--color-border-strong);
    border-radius: var(--radius-card);
    box-shadow: var(--shadow-lg);
  }
  .app-chrome.is-menu-open .chrome-actions > [hidden] { display: none; }
  .chrome-actions .btn { flex: 0 1 auto; }
  /* #511 (c): the open Menu is a plain list — 48px rows, a hairline
     between them, no button borders — and Log out is last, under a rule
     (last in the DOM too: ts-chrome.js puts Passkeys before it). */
  .app-chrome.is-menu-open .chrome-actions > .btn {
    justify-content: flex-start;
    min-height: 48px;
    padding: 0 var(--space-4);
    color: var(--color-text);
    border: 0;
    border-bottom: var(--hairline) solid var(--color-border);
    border-radius: 0;
  }
  .app-chrome.is-menu-open .chrome-actions > #btn-logout {
    border-top: var(--hairline) solid var(--color-border-strong);
    border-bottom: 0;
  }
  .legend-body { padding-left: var(--space-3); }


  #flight-blocks,
#finished-root,
#in-progress-root { overflow-x: visible; }

  /* #278: `.compact-table` is excluded from every rule below that turns a
     leg row into a card — it stays a real table (gridlines, the outer
     border, the `row-alt` stripe) at every width, phone included, which is
     where Compact is actually used by default. */
  .flight-table:not(.compact-table) {
    width: 100%;
    display: block;
    border: 0;
    background: transparent;
    box-shadow: none;
  }
  .flight-table:not(.compact-table) > caption { display: block; }
  .flight-table:not(.compact-table) > colgroup,
.flight-table:not(.compact-table) > thead { display: none; }
  .flight-table:not(.compact-table) > tbody { display: block; }

  .flight-table:not(.compact-table) > tbody > tr {
    display: grid;
    grid-template-columns: minmax(var(--row-label-width), auto) 1fr;
    gap: 0;
    margin-bottom: var(--space-3);
    background: var(--table-bg);
    border: var(--hairline) solid var(--color-border);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-sm);
    overflow: hidden;
  }
  .flight-table:not(.compact-table) > tbody > tr.row-alt > td { background: var(--table-bg); }

  /* The duty row is a divider between cards, not a card: one cell, full width,
     no label, and it keeps the left rule that marks the end of a duty. */
  .flight-table > tbody > tr.duty-row {
    display: block;
    border-radius: var(--radius-sm);
    box-shadow: none;
    margin-bottom: var(--space-3);
  }
  .flight-table > tbody > tr.duty-row > td.duty-cell {
    display: block;
    min-height: 0;
    padding: var(--space-2) var(--space-3);
    border-bottom: 0;
  }
  .flight-table > tbody > tr.duty-row > td::before { content: none; }

  /* Who is aboard rides under its card rather than becoming one: one block,
     one cell, and no field label — there is no field. */
  .flight-table > tbody > tr.pax-row {
    display: block;
    box-shadow: none;
  }
  .flight-table > tbody > tr.pax-row > td.pax-cell {
    display: block;
    min-height: 0;
    padding: var(--space-1) var(--space-3);
  }
  .flight-table > tbody > tr.pax-row > td::before { content: none; }

  /* The handoff marker rides under its card the same way: one block, one
     cell, no field label (there is no field — admin-only, never on the
     read-only card, see `.flight-table.is-readonly` below). */
  .flight-table > tbody > tr.handoff-marker-row {
    display: block;
    box-shadow: none;
  }
  .flight-table > tbody > tr.handoff-marker-row > td.handoff-marker-cell {
    display: block;
    min-height: 0;
    padding: var(--space-1) var(--space-3);
  }
  .flight-table > tbody > tr.handoff-marker-row > td::before { content: none; }

  /* The briefing panel rides under its card the same way — one block, one
     cell, no field label, on both boards (see `.flight-table.is-readonly`
     below, which excludes it from the read-only card grid just as it
     excludes `.pax-row`). */
  .flight-table > tbody > tr.briefing-row {
    display: block;
    box-shadow: none;
  }
  .flight-table > tbody > tr.briefing-row > td.briefing-cell {
    display: block;
    min-height: 0;
    padding: var(--space-2) var(--space-3);
  }
  .flight-table > tbody > tr.briefing-row > td::before { content: none; }

  /* #260 (e): the Crew notes panel row (WORKLIST #305: the marker itself
     moved into the leg's own Notes cell; this row now only exists to hold
     the panel, and only when there is a note or the viewer may write one)
     is the bottom of its leg's card, not a strip under it — the leg's card
     gives up its bottom edge to it.
     #298: `:not(.compact-table)` here too — Compact's marker row is a real
     `<td colspan=5>` (`ts-notes.js`) and wants to STAY a table cell so it
     spans every column; `display: block` on a compact `<tr>`/`<td>` forced
     the browser into anonymous-table-fixup layout, which collapsed the row
     to the width of the Date column alone (#278's own leg row is a card at
     this width, but `.compact-table` was never one — the same exclusion its
     other card rules already carry). */
  .flight-table:not(.compact-table) > tbody > tr:has(+ tr.note-marker-row) {
    margin-bottom: 0;
    border-bottom: 0;
    border-bottom-left-radius: 0;
    border-bottom-right-radius: 0;
  }
  .flight-table:not(.compact-table) > tbody > tr.note-marker-row {
    display: block;
    margin-top: 0;
    border-top: 0;
    border-top-left-radius: 0;
    border-top-right-radius: 0;
  }
  .flight-table:not(.compact-table) > tbody > tr.note-marker-row > td.note-marker-cell {
    display: block;
    min-height: 0;
    padding: 0 var(--space-1) var(--space-1);
    border-bottom: 0;
  }
  .flight-table:not(.compact-table) > tbody > tr.note-marker-row > td::before { content: none; }
  /* #299: no Crew notes row draws in Compact at all (`ts-notes.js`), so the
     `.leg-notes-marker` / `.leg-notes-preview` wrapping #298 gave that row
     here is gone with it — dead the moment nothing in `.compact-table`
     carries those classes. */

  .flight-table:not(.compact-table) > tbody > tr > td {
    display: flex;
    align-items: center;
    /* 44px is a TAP target, so only cells that are actually tappable get it.
       Applying it to read-only cells just made every card a third taller. */
    min-height: 0;
    grid-column: 2;
    text-align: left;
    white-space: normal;
    border-right: 0;
    border-bottom: var(--hairline) solid var(--table-gridline);
    padding: var(--space-2) var(--space-3);
  }
  .flight-table:not(.compact-table) > tbody > tr > td:last-child { border-bottom: 0; }

  .flight-table > tbody > tr.leg-row-clickable > td[tabindex] {
    min-height: var(--tap-target);
  }

  /* The label the hidden <thead> used to carry. #451: the margin only
     pulls the label back across `row-label-width` — the card's own left
     inset (its `td`'s `padding-inline: var(--space-3)`, the same token
     the Position box and the Finished Last 24h/All bar use for theirs)
     is no longer cancelled out, so the label (and, on the date header
     strip that spans both columns, the date text right after it) lands
     the token's width inside the card's border box instead of flush
     against it. */
  .flight-table:not(.compact-table) > tbody > tr > td::before {
    content: attr(data-field);
    grid-column: 1;
    align-self: center;
    margin-left: calc(-1 * var(--row-label-width));
    width: var(--row-label-width);
    padding-right: var(--space-2);
    flex: 0 0 var(--row-label-width);
    font-family: var(--font-sans);
    font-size: var(--text-2xs);
    font-weight: var(--weight-semibold);
    letter-spacing: var(--tracking-caps);
    text-transform: uppercase;
    text-align: left;
    color: var(--color-text-muted);
  }
  /* Human labels rather than raw field keys. */
  .flight-table > tbody > tr > td[data-field="date"]::before { content: "Date"; }
  .flight-table > tbody > tr > td[data-field="dep"]::before { content: "Dep"; }
  .flight-table > tbody > tr > td[data-field="etd"]::before { content: "ETD"; }
  .flight-table > tbody > tr > td[data-field="arr"]::before { content: "Arr"; }
  .flight-table > tbody > tr > td[data-field="eta"]::before { content: "ETA"; }
  /* WORKLIST #419 (a): the crew's merged Times cell, its own label. */
  .flight-table > tbody > tr > td[data-field="times"]::before { content: "Times"; }
  .flight-table > tbody > tr > td[data-field="block"]::before { content: "Block"; }
  .flight-table > tbody > tr > td[data-field="pax"]::before { content: "Pax"; }
  .flight-table > tbody > tr > td[data-field="fplNumber"]::before { content: "FPL"; }
  .flight-table > tbody > tr > td[data-field="actuals"]::before { content: "Actual \2212  plan"; }
  .flight-table > tbody > tr > td[data-field="crew"]::before { content: "Crew"; }
  .flight-table > tbody > tr > td[data-field="crewNotes"]::before { content: "Crew Notes"; }

  /* Date leads the card as its header strip. */
  .flight-table > tbody > tr > td[data-field="date"] {
    grid-column: 1 / -1;
    position: static;
    background: var(--table-header-bg);
    font-weight: var(--weight-semibold);
    box-shadow: none;
  }
  /* Notes is ellipsised into one line on a wide board. On a card there is no
     column to protect, and `overflow: hidden` was clipping the field label,
     which sits in the gutter outside the cell's own box. */
  .flight-table > tbody > tr > td[data-field="crewNotes"] {
    max-width: none;
    overflow: visible;
    white-space: normal;
    text-overflow: clip;
  }

  /* Changed marker moves to the leading edge of the stacked row. */
  .flight-table > tbody > tr > td.changed {
    box-shadow: inset 3px 0 0 0 var(--cell-changed-edge);
  }

  /* ── The read-only card: four lines, not nine ──────────────────────────
     Nine labelled rows made one leg fill the screen, so the next leg — the
     thing you scroll a board to see — was never on it. On the read-only
     board the labels are redundant: a value's position says what it is.
     They go visually hidden (still announced, since the <thead> is display:
     none on a phone) and the card collapses to the shape a pilot reads:

         10 Sep                         9.8
         KEWR Newark          2100lcl/0100z
         LEMD Madrid    0924lcl +1/0724z +1
         AD/FCV/VVID                  2 pax
         Itin in Drive

     Departure over arrival, times right-aligned so the two line up under
     each other. The editable board keeps its labels. ── */
  .flight-table.is-readonly > tbody > tr:not(.duty-row):not(.leg-toolbar-row):not(.pax-row):not(.briefing-row):not(.note-marker-row) {
    grid-template-columns: 1fr auto;
  }
  .flight-table.is-readonly > tbody > tr > td::before {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: 0;
    padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }
  .flight-table.is-readonly > tbody > tr > td {
    border-bottom: 0;
    padding: var(--space-1) var(--space-3);
  }
  .flight-table.is-readonly > tbody > tr > td:empty { display: none; }

  /* WORKLIST #419 (a): `dep`/`arr`/`etd`/`eta` never occur on `is-readonly`
     any more — #414 (k) merged the first pair into `route`, this entry
     merges the second into `times` — so the card grid is keyed on the two
     merged fields directly, one row closer together than before. */
  .flight-table.is-readonly > tbody > tr > td[data-field="date"] { grid-area: 1 / 1; }
  .flight-table.is-readonly > tbody > tr > td[data-field="block"] { grid-area: 1 / 2; }
  .flight-table.is-readonly > tbody > tr > td[data-field="route"] { grid-area: 2 / 1; }
  .flight-table.is-readonly > tbody > tr > td[data-field="times"] { grid-area: 2 / 2; }
  .flight-table.is-readonly > tbody > tr > td[data-field="crew"] { grid-area: 3 / 1; }
  .flight-table.is-readonly > tbody > tr > td[data-field="pax"] { grid-area: 3 / 2; }
  /* #290: the plan, a line of its own under crew and pax — only when there
     is one to read; '—' on the crew card is a line that says nothing. */
  .flight-table.is-readonly > tbody > tr > td[data-field="fplNumber"] { grid-area: 4 / 1 / auto / -1; justify-content: flex-start; }
  .flight-table.is-readonly > tbody > tr > td.fpl-none { display: none; }
  .flight-table.is-readonly > tbody > tr > td[data-field="crewNotes"] {
    grid-area: 5 / 1 / auto / -1;
    color: var(--color-text-muted);
    border-top: var(--hairline) solid var(--table-gridline);
    margin-top: var(--space-1);
    padding-top: var(--space-2);
  }

  /* Date and block share the header strip. */
  .flight-table.is-readonly > tbody > tr > td[data-field="block"] {
    background: var(--table-header-bg);
    justify-content: flex-end;
  }
  .flight-table.is-readonly > tbody > tr > td[data-field="date"] {
    grid-column: 1;
    padding-bottom: var(--space-2);
  }
  .flight-table.is-readonly > tbody > tr > td[data-field="block"] {
    padding-bottom: var(--space-2);
  }
  /* The first field line clears the strip. */
  .flight-table.is-readonly > tbody > tr > td[data-field="route"],
.flight-table.is-readonly > tbody > tr > td[data-field="times"] {
    padding-top: var(--space-2);
  }
  .flight-table.is-readonly > tbody > tr > td[data-field="crew"],
.flight-table.is-readonly > tbody > tr > td[data-field="pax"] {
    padding-bottom: var(--space-2);
  }

  /* Ports lead; times and counts are right-aligned and align with each other. */
  .flight-table.is-readonly > tbody > tr > td[data-field="route"] {
    font-weight: var(--weight-medium);
  }
  .flight-table.is-readonly > tbody > tr > td[data-field="times"],
.flight-table.is-readonly > tbody > tr > td[data-field="pax"] {
    justify-content: flex-end;
    text-align: right;
  }

  /* ── #512 (a): the admin leg at rest reads as the crew's card ──────────
     Eleven labelled lines made one admin leg ~1,000 px on a phone. At
     rest it is the crew board's four-line read card (above), the labels
     visually hidden but still announced:

         10 Sep                         6.4
         KEWR Newark (UTC -4)  2100lcl/0100z
         LEMD Madrid (UTC +2)  0924lcl +1/…
         AD/FCV/VVID                      2

     Both clocks stay (#512 (c)); a long airport name ellipsises, never the
     time. Actual − plan, no plan and an empty Crew Notes wait for the tap;
     a plan and a leg's notes, when there are some, are lines of their own
     under the four, as on the crew card. A tap selects
     the leg (`.leg-editing`, admin-schedule.js) and that leg alone opens
     to its labelled fields with the edit bar — the rules below stop
     applying to it; every other leg stays a card. CSS only, keyed on the
     model's selection the page already draws (rule 1). */
  .flight-table.is-editable > tbody > tr[data-leg-id]:not(.leg-editing) {
    grid-template-columns: minmax(0, 1fr) auto;
  }
  .flight-table.is-editable > tbody > tr[data-leg-id]:not(.leg-editing) > td::before {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: 0;
    padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }
  .flight-table.is-editable > tbody > tr[data-leg-id]:not(.leg-editing) > td {
    min-height: 0;
    border-bottom: 0;
    padding: var(--space-1) var(--space-3);
  }
  .flight-table.is-editable > tbody > tr[data-leg-id]:not(.leg-editing) > td[data-field="actuals"],
  .flight-table.is-editable > tbody > tr[data-leg-id]:not(.leg-editing) > td.fpl-none,
  .flight-table.is-editable > tbody > tr[data-leg-id]:not(.leg-editing) > td[data-field="crewNotes"]:not(:has(.leg-notes-marker:not(.is-empty))) { display: none; }
  /* As on the crew card: the plan, when there is one (a failure's reason
     included), a line of its own under crew and pax; the leg's crew notes,
     when it has any, the last (full width, under a rule). The empty "+"
     and the "-" of no plan wait for the tap. */
  .flight-table.is-editable > tbody > tr[data-leg-id]:not(.leg-editing) > td[data-field="fplNumber"] {
    grid-area: 5 / 1 / auto / -1;
    justify-content: flex-start;
  }
  .flight-table.is-editable > tbody > tr[data-leg-id]:not(.leg-editing) > td[data-field="crewNotes"] {
    grid-area: 6 / 1 / auto / -1;
    border-top: var(--hairline) solid var(--table-gridline);
    padding-top: var(--space-1);
  }
  .flight-table.is-editable > tbody > tr[data-leg-id]:not(.leg-editing) > td[data-field="date"] { grid-area: 1 / 1; padding-bottom: var(--space-2); }
  .flight-table.is-editable > tbody > tr[data-leg-id]:not(.leg-editing) > td[data-field="block"] {
    grid-area: 1 / 2;
    background: var(--table-header-bg);
    justify-content: flex-end;
    padding-bottom: var(--space-2);
  }
  .flight-table.is-editable > tbody > tr[data-leg-id]:not(.leg-editing) > td[data-field="dep"] { grid-area: 2 / 1; padding-top: var(--space-2); }
  .flight-table.is-editable > tbody > tr[data-leg-id]:not(.leg-editing) > td[data-field="etd"] { grid-area: 2 / 2; padding-top: var(--space-2); }
  .flight-table.is-editable > tbody > tr[data-leg-id]:not(.leg-editing) > td[data-field="arr"] { grid-area: 3 / 1; }
  .flight-table.is-editable > tbody > tr[data-leg-id]:not(.leg-editing) > td[data-field="eta"] { grid-area: 3 / 2; }
  .flight-table.is-editable > tbody > tr[data-leg-id]:not(.leg-editing) > td[data-field="crew"] { grid-area: 4 / 1; padding-bottom: var(--space-2); }
  .flight-table.is-editable > tbody > tr[data-leg-id]:not(.leg-editing) > td[data-field="pax"] { grid-area: 4 / 2; padding-bottom: var(--space-2); }
  .flight-table.is-editable > tbody > tr[data-leg-id]:not(.leg-editing) > td[data-field="dep"],
  .flight-table.is-editable > tbody > tr[data-leg-id]:not(.leg-editing) > td[data-field="arr"] {
    display: block;
    min-width: 0;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
    font-weight: var(--weight-medium);
  }
  .flight-table.is-editable > tbody > tr[data-leg-id]:not(.leg-editing) > td[data-field="etd"],
  .flight-table.is-editable > tbody > tr[data-leg-id]:not(.leg-editing) > td[data-field="eta"],
  .flight-table.is-editable > tbody > tr[data-leg-id]:not(.leg-editing) > td[data-field="pax"] {
    justify-content: flex-end;
    text-align: right;
    white-space: nowrap;
  }

  /* syncFlightColumnWidths bails out below this breakpoint and clears what
     it wrote, but a stale inline width must never be able to force a 2800px
     page scroll, so the card layout also wins on specificity. #299:
     `:not(.compact-table)` — this `!important` was never excluding Compact
     the way its sibling card rules already do, so `.compact-table`'s own
     `table-layout: fixed` (above) was silently losing to this `auto` the
     whole time; #298 survived it only because wrapping content wide enough
     to stay inside a plain `width: 100%` never let it show. Nowrap content
     (#299) needs the fixed grid to actually win. */
  .flight-table:not(.compact-table),
table.leg-cols-frozen,
table.flight-table.leg-cols-frozen {
    table-layout: auto !important;
    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;
  }
  table.leg-cols-frozen td,
table.leg-cols-frozen th {
    width: auto !important;
    min-width: 0 !important;
    max-width: none !important;
  }

  /* The bar row is its own card, tucked under the leg it belongs to. */
  .flight-table > tbody > tr.leg-toolbar-row {
    display: block;
    margin-top: calc(-1 * var(--space-3) - 1px);
    border-top: 0;
    border-radius: 0 0 var(--radius-md) var(--radius-md);
    background: var(--editbar-bg);
    border-color: var(--editbar-edge);
  }
  .flight-table > tbody > tr.leg-toolbar-row > td {
    display: block;
    min-height: 0;
    border-bottom: 0;
  }
  /* No field label for a cell that is not a field. */
  .flight-table > tbody > tr.leg-toolbar-row > td::before { content: none; }
  .leg-cell-input { white-space: normal; text-overflow: clip; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   PRINT
   The third and fourth places !important is still right: a print stylesheet
   and a reduced-motion block are not competing with the screen rules, they are
   overruling them, and both are the last word by intent rather than by weight.
   ═══════════════════════════════════════════════════════════════════════════ */
@media print {
  .app-chrome,
.toolbar,
.tab-bar,
.banner,
.skip-link,
.leg-inline-bar-host,
.leg-toolbar-pad,
.no-print {
    display: none !important;
  }
  body {
    background: var(--print-paper);
    color: var(--print-ink);
    padding: var(--space-3);
    font-size: 9pt;
  }
  #flight-blocks,
#finished-root,
#in-progress-root { overflow: visible; }
  .flight-table,
.other-table { width: 100%; table-layout: auto; box-shadow: none; }
  ul.bullet-list li { padding-right: var(--space-2); }
  .flight-block,
tr { break-inside: avoid; }
  /* Amber prints as a hatched edge, since most board printers are mono. */
  .changed,
td.changed {
    background: var(--print-paper) !important;
    color: var(--print-ink) !important;
    box-shadow: none;
    border-left: 3px double var(--print-ink) !important;
    font-weight: 700;
  }
}

/* ═══════════════════════════════════════════════════════════════════════════
   MAC: platform surface details
   Shapes that tokens alone cannot express. Everything here is cosmetic —
   nothing below changes layout, focus order or behaviour.
   ═══════════════════════════════════════════════════════════════════════════ */

/* Overlay scrollbars. Safari on macOS already does this natively; this makes
   Chrome match instead of drawing a heavy Windows-style gutter. The board's
   phase roots scroll horizontally, so they are the ones that show. */
#flight-blocks::-webkit-scrollbar,
#finished-root::-webkit-scrollbar,
#in-progress-root::-webkit-scrollbar,
#other-metal-root::-webkit-scrollbar,
#location-root::-webkit-scrollbar,
.tab-bar::-webkit-scrollbar,
.airport-dropdown::-webkit-scrollbar {
  width: 11px;
  height: 11px;
}
#flight-blocks::-webkit-scrollbar-track,
#finished-root::-webkit-scrollbar-track,
#in-progress-root::-webkit-scrollbar-track,
#other-metal-root::-webkit-scrollbar-track,
#location-root::-webkit-scrollbar-track,
.tab-bar::-webkit-scrollbar-track,
.airport-dropdown::-webkit-scrollbar-track {
  background: transparent;
}
#flight-blocks::-webkit-scrollbar-thumb,
#finished-root::-webkit-scrollbar-thumb,
#in-progress-root::-webkit-scrollbar-thumb,
#other-metal-root::-webkit-scrollbar-thumb,
#location-root::-webkit-scrollbar-thumb,
.tab-bar::-webkit-scrollbar-thumb,
.airport-dropdown::-webkit-scrollbar-thumb {
  background: var(--scrollbar-thumb);
  border: 3px solid transparent;
  background-clip: content-box;
  border-radius: var(--radius-full);
}
#flight-blocks::-webkit-scrollbar-thumb:hover,
#finished-root::-webkit-scrollbar-thumb:hover,
#in-progress-root::-webkit-scrollbar-thumb:hover,
#other-metal-root::-webkit-scrollbar-thumb:hover,
#location-root::-webkit-scrollbar-thumb:hover,
.tab-bar::-webkit-scrollbar-thumb:hover,
.airport-dropdown::-webkit-scrollbar-thumb:hover {
  background: var(--scrollbar-thumb-hover);
  background-clip: content-box;
}

/* System accent on native controls (checkbox, text selection). The checkbox
   fill is a non-text surface, so true systemBlue is fine here. */
:root {
  accent-color: var(--focus-600);
}
::selection {
  background: var(--color-accent-subtle);
  color: var(--color-text);
}

/* MAC: a "prominent" push button is a filled capsule-ish rect with a hairline
   top highlight, no gradient. */
.btn-primary {
  box-shadow: var(--shadow-sm), var(--shadow-inset-top);
}

/* MAC: popovers use the larger card radius and a soft, tight shadow. */
.airport-dropdown,
.login-card {
  border-radius: var(--radius-lg);
}

/* Hotel / Airline on the duty end they belong to. */
.duty-stay:empty { display: none; }
.duty-stay {
  font-size: var(--text-sm);
  color: var(--color-text);
}
.duty-stay::before { content: " · "; color: var(--color-text-muted); }
.duty-tag {
  font-size: var(--text-2xs);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--color-text-muted);
}

/* ─── Crew notes on both boards and the Notes page (#260, #312, #377) ────
   WORKLIST #312: the Crew Notes column on the crew board and the admin
   board alike — one line a leg: the latest note's text, cut with an
   ellipsis, then a small (n) count, or a (+) where the viewer may write
   and there is no note yet. The button opens every note on the leg in
   place, in a row below the leg — WORKLIST #377 ("layout B"): one line a
   note, in fixed columns (`.note-rows` below), not the stacked block the
   old Notes page list drew (`.note-item`). Amber is `.changed`, the board's
   own rule, so a note new since the last look reads as a changed cell does.
   #811: the Notes page now draws these same lines, one box a leg. */
.leg-notes-marker {
  display: flex;
  align-items: baseline;
  gap: var(--space-1);
  width: 100%;
  min-width: 0;
  min-height: var(--control-h-sm);
  margin: 0;
  /* #360: the cell's own padding (`--table-cell-pad-x`, `th, td`) already
     sets the left edge to match the heading above it; this button's own
     padding-left was on top of that, landing the note's first character
     ~8px right of "Crew Notes". The negative margin cancels the button's
     own inset so the text starts exactly where the heading's does, the
     way `.crew-notes-btn` (location.html's own note cell) already does. */
  margin-left: calc(-1 * var(--space-2));
  padding: var(--space-1) var(--space-2);
  font: inherit;
  font-size: var(--text-sm);
  text-align: left;
  color: var(--color-text);
  background: transparent;
  border: 0;
  border-radius: var(--radius-sm);
  white-space: normal;
  cursor: pointer;
}
button.leg-notes-marker:hover { background: var(--color-surface-hover); }
.leg-notes-marker:focus-visible {
  outline: var(--focus-ring-width) solid var(--color-focus);
  outline-offset: calc(-1 * var(--focus-ring-width));
}
.leg-notes-preview {
  flex: 0 1 auto;
  min-width: 0;
  /* #458 (a): the note wraps within its cell, three lines at most (the
     display maximum, as `.fpl-reason`); the CSS ellipsis comes only at
     that maximum, never after one line. */
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 3;
  overflow: hidden;
  overflow-wrap: anywhere;
  text-align: left;
}
.leg-notes-count {
  flex: 0 0 auto;
  font-size: var(--text-xs);
  color: var(--color-text-muted);
}
.leg-notes-add {
  flex: 0 0 auto;
  font-size: var(--text-xs);
  font-weight: var(--weight-semibold);
  color: var(--color-text-muted);
}
.leg-notes-marker.changed .leg-notes-count,
.leg-notes-marker.changed .leg-notes-add { color: inherit; }
/* #377 fix round 2: `container-type: inline-size` makes the panel itself
   the sizing context every `@container` rule below measures — "the
   panel's width" the entry names, not the viewport. */
.leg-notes-panel { padding: 0 var(--space-2) var(--space-1); white-space: normal; container-type: inline-size; }

/* #377 "layout B": every note on the leg as one line in fixed columns —
   who · when · text · files · actions (📎, Edit) — sharing one CSS Grid so
   the columns line up under each other without measuring anything at
   render time. `.note-row` wraps one note's five cells; `display: contents`
   so the wrapper draws no box of its own and its children still act as
   direct children of `.note-rows`' own grid, the only way one wrapper a
   note and one shared set of column tracks both hold at once. Only the
   text column wraps (`overflow-wrap`); every other column truncates with
   an ellipsis instead — a long name or filename is cut, never pushed onto
   a second line, so a note's own row never grows past its text.
   Fix round 2: the four side columns were fixed px minimums (580px plus
   gaps) that exceeded the panel on admin at 1440 (forcing the whole
   `table-layout: auto` flight table, and the Crew Notes column's own
   ellipsis with it, wider than the page) and on the crew board at 390.
   `max-content` sizes each to its own real content instead — a name, a
   date, an icon — so only the text column (`minmax(0, 1fr)`) ever gives. */
.note-rows {
  display: grid;
  grid-template-columns: max-content max-content minmax(0, 1fr) max-content max-content;
  column-gap: var(--space-2);
  align-items: start;
}
.note-row { display: contents; }
.note-row-who,
.note-row-when,
.note-row-files,
.note-row-actions {
  min-height: 30px;
  display: flex;
  align-items: center;
  gap: var(--space-1);
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  padding: var(--space-1) 0;
  border-bottom: var(--hairline) solid var(--table-gridline);
}
.note-row-text {
  min-height: 30px;
  min-width: 0;
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-1);
  white-space: normal;
  overflow-wrap: anywhere;
  padding: var(--space-1) 0;
  border-bottom: var(--hairline) solid var(--table-gridline);
}
.note-row-actions { justify-content: flex-end; }
.note-rows > .note-row:last-child > * { border-bottom: 0; }
.note-row.changed { color: var(--cell-changed-text); }
/* Each role keeps its own named track explicitly (#377 fix round 2):
   `.note-row` still relies on DOM order elsewhere, but an explicit
   `grid-column` here means a role that renders nothing for one note
   (an empty files column, most notes) never drifts every column after
   it out of alignment with the notes around it. */
.note-row-who { grid-column: 1; }
.note-row-when { grid-column: 2; }
.note-row-text { grid-column: 3; }
.note-row-files { grid-column: 4; }
.note-row-actions { grid-column: 5; }
/* The editor a note's Edit opens spans every column — it is never one
   column's own width, unlike the note it replaces. */
.note-rows .note-row-editor { grid-column: 1 / -1; margin: var(--space-1) 0; }

/* #377 fix round 2: below ~600px of panel the five columns cannot fit
   (`.note-rows`' own minimums plus gaps outgrow it even with `max-content`
   above) — each note becomes two lines instead: who · when · actions on
   the first, the note's own text (then its files) on the second, the
   same order, still one block a note. `.note-row` stops being
   `display: contents` and becomes its own small grid so one note's two
   lines never interleave with its neighbour's — the shared `.note-rows`
   grid, above, is for the wide single-line layout only. */
@container (max-width: 37.5rem) {
  .note-rows { grid-template-columns: 1fr; row-gap: 0; }
  .note-row {
    display: grid;
    grid-template-columns: max-content max-content 1fr;
    grid-template-areas:
      "who  when actions"
      "text text text"
      "files files files";
    column-gap: var(--space-2);
    row-gap: 2px;
    padding: var(--space-1) 0;
    border-bottom: var(--hairline) solid var(--table-gridline);
  }
  .note-rows > .note-row:last-child { border-bottom: 0; }
  .note-row-who,
  .note-row-when,
  .note-row-actions,
  .note-row-text,
  .note-row-files {
    border-bottom: 0;
    min-height: 0;
    padding: 0;
  }
  .note-row-who { grid-area: who; }
  .note-row-when { grid-area: when; }
  .note-row-actions { grid-area: actions; }
  .note-row-text { grid-area: text; }
  .note-row-files { grid-area: files; }
}
/* #377 (a): the unread chip, right after the newest note's own text. */
.leg-notes-unread {
  flex: 0 0 auto;
  padding: 0 var(--space-1);
  border-radius: var(--radius-xs);
  background: var(--color-danger-surface);
  color: var(--color-danger-hover);
  font-size: var(--text-xs);
  font-weight: var(--weight-semibold);
  line-height: 1.4;
}
/* #377 (b): the note row's own 📎 upload is the same 20×20 square as the
   standard "+" (`.btn-plus` below) — scoped here, never the FPL cell's or
   Pax data's own `.att-add` (#289/#290's other callers keep their size). */
.note-row-actions .att-add {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  min-height: 0;
  padding: 0;
  vertical-align: calc((1cap - 20px) / 2);
}

/* Full: its own row under the leg, read as the bottom of the leg's row —
   no strip of its own, no rule between the two. */
.note-marker-row > .note-marker-cell {
  padding: 0 var(--space-2) var(--space-1);
  border-bottom: var(--hairline) solid var(--color-border);
  white-space: normal;
}
@media screen {
  .flight-table tbody tr:has(+ tr.note-marker-row) > td { border-bottom-color: transparent; }
}
.flight-table tbody tr.note-marker-row:hover > td,
.flight-table tbody tr.note-marker-row:nth-child(even) > td {
  background: var(--table-bg);
}
.flight-table tbody tr.row-alt + tr.note-marker-row > td {
  background: var(--table-stripe);
}
.note-item {
  padding: var(--space-3);
  border-bottom: var(--hairline) solid var(--table-gridline);
}
.note-meta {
  margin: 0 0 var(--space-1);
  font-size: var(--text-xs);
  color: var(--color-text-muted);
}
.note-item.changed .note-meta { color: var(--cell-changed-text); }
.note-place,
.note-when { font-variant-numeric: tabular-nums; }
.note-text {
  margin: 0 0 var(--space-2);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
.note-editor { margin: var(--space-2) 0; }
.note-editor-label {
  display: block;
  margin-bottom: var(--space-1);
  font-size: var(--text-xs);
  font-weight: var(--weight-semibold);
}
.note-editor-input {
  display: block;
  width: 100%;
  min-height: 4.5rem;
  font: inherit;
  resize: vertical;
}
/* #458 (b): the leg's own note is one line high and grows with its text
   (ts-notes.js sets the height); no drag handle, no scrollbar flash. */
.note-editor-input.note-editor-grow {
  min-height: 0;
  resize: none;
  overflow: hidden;
}
.note-editor-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-top: var(--space-2);
}
/* A note changed on another device while its editor was open (ts-notes.js):
   who changed it and the newer text, above the person's own typing. */
.note-conflict {
  margin: 0 0 var(--space-1);
  font-size: var(--text-xs);
  font-weight: var(--weight-semibold);
  color: var(--cell-changed-text);
}
.note-conflict-text {
  padding-left: var(--space-2);
  border-left: 3px solid var(--cell-changed-edge);
}

/* ── #473: the admin board's Actual − plan column ───────────────────────
   Three short lines (BLK, FLT, FUEL), each actual − plan; one beyond the
   owner's limit (15 min, 5 % fuel) is bold in the danger colour, with the
   words "over the limit" for a screen reader; "Early off" flags a block off
   more than 30 min before ETD. */
.flight-table td[data-field="actuals"] {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: var(--text-xs);
  white-space: nowrap;
  text-align: left;
}
/* #512 (c): the fifth heading since Dep · ETD and Arr · ETA span two each. */
.flight-table.is-editable th:nth-child(5) { text-align: left; }
.flight-table.is-editable th:nth-child(5),
.flight-table td[data-field="actuals"] { padding-left: var(--space-2); padding-right: var(--space-2); }
.act-line { display: block; }
.act-line .act-k { display: inline-block; min-width: 2.6em; color: var(--color-text-muted); }
.act-line.act-hl .act-v { color: var(--color-danger); font-weight: 700; }
.act-line.act-early,
.act-line.act-bypass,
.act-line.act-missing { color: var(--color-danger); font-weight: 700; }

/* ── #290: the FPL column, its chip and the paperclips ──────────────────
   One narrow column (#272: Safari squeezes a wide board): the word, the
   "changed" chip beside it, and a 📎 disclosure whose list opens OVER the
   rows below rather than widening the cell. */
.flight-table td[data-field="fplNumber"] {
  text-align: center;
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: var(--text-sm);
  position: relative;
}
.fpl-chip {
  display: inline-block;
  margin-left: var(--space-1);
  padding: 0 var(--space-1);
  border: 1px solid var(--color-danger-border);
  border-radius: var(--radius-xs);
  background: var(--color-danger-surface);
  color: var(--color-danger-hover);
  font-family: var(--font-sans);
  font-size: var(--text-xs);
  font-weight: var(--weight-semibold);
  line-height: 1.4;
}
/* #443: a failed job says 'Failed' in the danger ink, its reason beneath in
   the muted one — three lines at most in the narrow column, the whole on
   hover (`title`). */
.fpl-text.is-failed { color: var(--color-danger-hover); font-weight: var(--weight-semibold); }
.fpl-reason {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 3;
  overflow: hidden;
  font-family: var(--font-sans);
  font-size: var(--text-xs);
  color: var(--color-text-muted);
  white-space: normal;
}
.fpl-reason:empty { display: none; }
/* #452 fix round 1: a cut reason is the hover box's trigger (`TS.hoverBox`);
   a short one stays plain text. */
.fpl-reason.is-cut { cursor: help; text-decoration: underline dotted; text-underline-offset: 2px; }
/* #443 fix round 2: the reason never widens the board. `.flight-table` is
   `width: max-content`, so an unwrapped sentence's max-content set the FPL
   column — and through the shared grid every table and the selected leg's
   action bar (1656px at 1440, Add Hotel clipped). Contained, it wraps in
   the column the word and the header size, and is never narrower than 8rem. */
.fpl-reason {
  contain: inline-size;
  min-width: 8rem;
}
/* #452 fix round 1 (c): the reason sits inside its cell at every width.
   It arrives after the shared grid measured the columns, so its own 8rem
   could not widen the column and ran past the cell's edge; the failed
   cell carries the 8rem itself (known at draw, from the model's word), and
   the reason fills the cell's box and wraps there. "Failed" stays one
   line above it. `!important` answers the tablet band's `min-width: 0
   !important` on every cell (below), which would otherwise squeeze the
   column to "Failed"'s width. */
.flight-table > tbody > tr > td.fpl-cell.fpl-failed { min-width: 8rem !important; }
.fpl-cell .fpl-reason { min-width: 0; max-width: 100%; }
.fpl-text.is-failed { white-space: nowrap; }
/* The phone card's cell is a flex row: the reason takes a line of its own
   under "Failed", after the word and its 📎, as it does in the table. */
@media (max-width: 40rem) {
  .flight-table:not(.compact-table) > tbody > tr > td.fpl-cell { flex-wrap: wrap; }
  .flight-table:not(.compact-table) > tbody > tr > td.fpl-cell > .fpl-reason { flex: 0 0 100%; order: 1; }
  .flight-table:not(.compact-table) > tbody > tr > td.fpl-cell > .fpl-plan-actions { order: 2; }
}
/* #443 fix round 2: a plan button that cannot be pressed looks it — no
   fill, no lift, a dashed edge, the muted ink. */
.btn:disabled,
.btn:disabled:hover,
.btn:disabled:active,
.btn[aria-disabled="true"],
.btn[aria-disabled="true"]:hover,
.btn[aria-disabled="true"]:active,
button.btn[data-leg-act^="plan-"]:disabled,
button.btn[data-leg-act^="plan-"]:disabled:hover,
button.btn[data-leg-act^="plan-"]:disabled:active {
  color: var(--color-text-muted);
  background: transparent;
  border-style: dashed;
  box-shadow: none;
  transform: none;
  cursor: not-allowed;
}
.att { display: inline-flex; flex-wrap: wrap; align-items: center; gap: var(--space-1) var(--space-2); }
.note-item > .att { display: flex; margin-top: var(--space-1); }
.att-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-1); }
.att-item { display: flex; align-items: center; gap: var(--space-2); }
.att-link { overflow-wrap: anywhere; }
.att-status:empty { display: none; }
.att-status { font-size: var(--text-xs); color: var(--color-text-muted); }
.att-compact { display: inline-block; margin-left: var(--space-1); vertical-align: middle; }
.att-compact > summary {
  cursor: pointer;
  list-style: none;
  display: inline-block;
  min-width: 1.75rem;
  padding: 0 var(--space-1);
  border-radius: var(--radius-xs);
}
.att-compact > summary::-webkit-details-marker { display: none; }
.att-compact > .att-pop {
  position: absolute;
  z-index: 5;
  right: 0;
  top: 100%;
  min-width: 14rem;
  max-width: 22rem;
  padding: var(--space-2) var(--space-3);
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-2);
  background: var(--color-surface-raised);
  color: var(--color-text);
  border: 1px solid var(--table-gridline);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-md);
  white-space: normal;
  text-align: left;
  font-family: var(--font-sans);
}
/* #828 (d): the plan's 📎 in a leg row's lines form (C6's phone row, admin-schedule.js seatPlanClip; the
   form an open leg keeps as its head) opens its list in the line, under the 📎, never over the open row's
   boxes (the owner, Safari full window: the white box covered the ETA). */
.or-ph .att-compact[data-att-kind="flightplan"][open] { display: inline-flex; flex-direction: column; align-items: flex-start; vertical-align: top; }
.or-ph .att-compact[data-att-kind="flightplan"] > .att-pop {
  position: static;
  min-width: 0;
  margin-top: var(--space-1);
  box-shadow: none;
}
/* While the 📎 is in the lines form, the table's cell keeps its width with a hidden placeholder, so the
   table's measure of its columns (C6's linesFit) is the same in both forms. */
.att-compact.att-seat { visibility: hidden; min-width: 1.75rem; padding: 0 var(--space-1); }
@media print {
  .att-compact, .att-add, .att-remove { display: none; }
}

/* #475: an airport code the airport list lacks (the owner's SALG), and a
   time read on UTC because of it — drawn by `TS.markUnknownAirport`
   (ts-board.js) on the admin board, the crew board's Full view, location
   and days away until an admin adds the airport. Never in Compact. The
   look (amber) tokens, the board's and the banner's "needs a look" (#805
   round 9, S8-37 (c)): red is the error scale (TEMPLATE.md), and an airport
   the list lacks is no error. */
.airport-unknown {
  color: var(--color-changed-text);
  text-decoration: underline dotted var(--color-changed-edge);
  text-underline-offset: 2px;
}
.airport-unknown-mark {
  display: inline-block;
  margin-left: var(--space-1);
  padding: 0 0.25em;
  border: 1px solid currentColor;
  border-radius: 4px;
  font-size: 0.75em;
  font-weight: 600;
  line-height: 1.3;
  text-decoration: none;
}

/* #508 (j): a glossary abbreviation inside a heading keeps its title (and the
   popover) but draws no dotted underline; the legend lists the terms. */
th abbr.ts-abbr,
h2 abbr.ts-abbr { text-decoration: none; }

/* ─── #516 (b) ───
   Other metal under 40rem — each row a read-only-looking card, the
   labels from the hidden <thead>'s own words, as the board's `.flight-table`
   does at this width (styles.css). Editing is the same cells. */
@media (max-width: 40rem) {
  .other-table { display: block; width: 100%; max-width: none; table-layout: auto; box-shadow: none; border: 0; background: transparent; }
  .other-table > caption { display: block; }
  .other-table > colgroup,
  .other-table > thead { display: none; }
  .other-table > tbody { display: block; }
  .other-table > tbody > tr {
    display: block;
    margin-bottom: var(--space-3);
    background: var(--table-bg);
    border: var(--hairline) solid var(--color-border);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-sm);
    overflow: hidden;
  }
  .other-table > tbody > tr > td[data-field] {
    display: flex;
    align-items: baseline;
    gap: var(--space-2);
    width: auto;
    white-space: normal;
    overflow-wrap: anywhere;
    min-height: var(--tap-target);
    align-items: center;
    text-align: left;
  }
  .other-table > tbody > tr > td[data-field]::before {
    flex: 0 0 10em;
    text-align: left;
    overflow-wrap: normal;
    word-break: keep-all;
    font-family: var(--font-sans);
    font-size: var(--text-2xs);
    font-weight: var(--weight-semibold);
    letter-spacing: var(--tracking-caps);
    text-transform: uppercase;
    color: var(--color-text-muted);
  }
  .other-table > tbody > tr > td[data-field="date"]::before { content: "Date"; }
  .other-table > tbody > tr > td[data-field="type"]::before { content: "Type"; }
  .other-table > tbody > tr > td[data-field="destination"]::before { content: "Destination"; }
  .other-table > tbody > tr > td[data-field="pax"]::before { content: "Pax"; }
  .other-table > tbody > tr > td[data-field="lead"]::before { content: "Lead"; }
  .other-table > tbody > tr > td[data-field="order"]::before { content: "Order"; }
  /* An editing row: the label above, the input full width and a tap-sized
     control, as the stacked fields of the other forms. */
  .other-table tr.leg-editing > td[data-field] { flex-wrap: wrap; align-items: stretch; }
  .other-table tr.leg-editing > td[data-field]::before { flex: 1 0 100%; }
  .other-table tr.leg-editing > td[data-field] .leg-cell-input {
    flex: 1 1 100%;
    width: 100%;
    min-height: var(--tap-target);
    box-sizing: border-box;
  }
}

/* ── #632 (STUDIO ANSWER C1 bar inset): the one bar's place on every page ──
   C1's bar, a child of <body>, runs the window's full width, outside the
   body's side padding: it steps out by exactly that padding (the body's own
   rules above, read here and never changed: they are #613's), and pads
   itself with --page-gutter both sides (blocks/shell.css), so its first
   item's left edge and ☰'s right edge sit the gutter from the window edge.
   No breakpoint of its own: the steps follow the body's. */
body > .sh-bar {
  width: auto;
  margin-inline: calc(-1 * (var(--space-5) + env(safe-area-inset-left))) calc(-1 * (var(--space-5) + env(safe-area-inset-right)));
  padding-inline: calc(var(--page-gutter) + env(safe-area-inset-left)) calc(var(--page-gutter) + env(safe-area-inset-right));
}
@media (max-width: 64rem) { body > .sh-bar { margin-inline: calc(-1 * var(--space-3)); } }
@media (max-width: 40rem) { body > .sh-bar { margin-inline: calc(-1 * var(--space-2)); } }
