/* ──────────────────────────────────────────────────────────────────────────
   Hadar — web pay tools (private prototype)

   Cockpit theme, the same tokens the Android app uses (ui/theme/Color.kt).
   They are named the same on purpose: when a screen here is compared with the
   phone, "surfaceVariant" has to mean the same colour in both places or the
   comparison is worthless.

   Phone-first. Every wider screen gets MORE ROOM, never a different design —
   the whole question this prototype exists to answer is whether the phone
   version of this reads well in a browser.
   ────────────────────────────────────────────────────────────────────────── */

:root {
  --background:      #090C12;
  --surface:         #131B2E;
  --surface-variant: #1E2A45;
  --primary:         #4A9FFF;
  /* The same accent split into its parts, so a fill can be mixed at a varying
     strength in code — the seniority heat grid shades every square by how
     senior it is. CSS cannot take a channel apart, so it has to be written
     out. ⚠️ Change it with --primary above or the grid drifts off-theme. */
  --primary-rgb:     74, 159, 255;
  --on-surface:      #C5CFE0;
  --on-surface-dim:  rgba(197, 207, 224, 0.55);
  --on-surface-faint:rgba(197, 207, 224, 0.35);
  --outline:         rgba(197, 207, 224, 0.14);
  --error:           #FF8A80;
  --good:            #7CE2B0;

  --radius:      18px;
  --radius-sm:   12px;
  --nav-height:  62px;

  color-scheme: dark;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  background: var(--background);
  color: var(--on-surface);
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  font-size: 15px;
  line-height: 1.45;
  -webkit-text-size-adjust: 100%;
}

body {
  /* The bottom nav is fixed, so the page needs a matching floor or the last
     card hides behind it. The home-bar inset is added on top, not instead. */
  padding-bottom: calc(var(--nav-height) + env(safe-area-inset-bottom, 0px) + 16px);
}

/* ── Top bar ─────────────────────────────────────────────────────────────── */

.topbar {
  position: sticky;
  top: 0;
  z-index: 20;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  padding: 14px 16px;
  padding-top: calc(14px + env(safe-area-inset-top, 0px));
  background: var(--surface);
  border-bottom: 1px solid var(--outline);
}

.topbar h1 {
  margin: 0;
  font-size: 17px;
  font-weight: 600;
  letter-spacing: 0.22em;
  color: var(--on-surface);
}

.topbar .mark { color: var(--primary); }

.topbar button {
  position: absolute;
  right: 12px;
  top: calc(10px + env(safe-area-inset-top, 0px));
}

/* ── Layout ──────────────────────────────────────────────────────────────── */

main {
  max-width: 900px;
  margin: 0 auto;
  padding: 16px;
  /* Breathing room under the pinned nav. MEASURED 2026-09-22, at full scroll, on all seven
     screens (then seven tabs; now three groups, same seven panels — the figure is per
     PANEL, so regrouping them did not change it): the last card cleared the bar by 32px
     without this and by 96px with it. So nothing
     was ever cut off or unreachable — this is comfort, not a fix. It is kept because 32px
     puts the final card almost against the bar, and because a future tab with a taller last
     element would be the one to actually need it. `test_web_pages.py` guards the real
     failure, which is a gap going NEGATIVE. */
  padding-bottom: calc(80px + env(safe-area-inset-bottom, 0px));
}

.panel[hidden] { display: none !important; }
.group[hidden] { display: none !important; }

h2 {
  margin: 26px 0 10px;
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--on-surface-dim);
}

h2:first-child { margin-top: 4px; }

p.note {
  margin: 8px 2px 0;
  font-size: 13px;
  color: var(--on-surface-dim);
}

/* ── Cards ───────────────────────────────────────────────────────────────── */

.card {
  background: linear-gradient(180deg, var(--surface) 0%, var(--surface-variant) 100%);
  border-radius: var(--radius);
  padding: 16px;
  margin-bottom: 14px;
}

.card.flat { background: var(--surface); }

.card h3 {
  margin: 0 0 12px;
  font-size: 15px;
  font-weight: 600;
  color: var(--on-surface);
}

.card h3 .sub {
  display: block;
  font-size: 12px;
  font-weight: 400;
  color: var(--on-surface-dim);
  letter-spacing: 0;
  margin-top: 2px;
}

/* A small chip beside a heading, for a card most pilots can walk past — the
   reserve month on the 117 tab. Sits INLINE with the title rather than under
   it, so "only if you fly reserve" is read as part of the heading and not as
   the first line of the explanation below it. */
.card h3 .opt {
  display: inline-block;
  margin-left: 8px;
  padding: 1px 8px;
  border-radius: 999px;
  background: var(--surface-variant);
  color: var(--on-surface-dim);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0;
  vertical-align: 1px;
}

/* ── Hero result ─────────────────────────────────────────────────────────── */

.hero {
  background: linear-gradient(160deg, #1B3A66 0%, var(--surface-variant) 60%, var(--surface) 100%);
  border-radius: var(--radius);
  padding: 20px 18px;
  margin-bottom: 14px;
}

.hero .money {
  font-size: 38px;
  font-weight: 700;
  letter-spacing: -0.02em;
  color: #fff;
  font-variant-numeric: tabular-nums;
  line-height: 1.1;
}

.hero .money-label {
  font-size: 12px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.6);
  margin-bottom: 4px;
}

.hero .rotation-id {
  font-size: 13px;
  color: rgba(255, 255, 255, 0.72);
  margin-bottom: 14px;
}

.chips {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 16px;
}

.chip {
  background: rgba(255, 255, 255, 0.09);
  border-radius: 999px;
  padding: 6px 13px;
  font-size: 13px;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.chip b {
  font-weight: 600;
  color: #fff;
}

.chip .k {
  color: rgba(255, 255, 255, 0.6);
  margin-right: 5px;
}

/* ── Inputs ──────────────────────────────────────────────────────────────── */

label.field {
  display: block;
  margin-bottom: 12px;
}

label.field > span {
  display: block;
  font-size: 12px;
  letter-spacing: 0.04em;
  color: var(--on-surface-dim);
  margin: 0 0 5px 3px;
}

/* ⚠️ EVERY input type this page uses has to be named here. A type that is left
   out does not inherit anything — it falls all the way through to the browser's
   own control, which on this near-black page is a light grey box with a light
   border, looking like a different app got pasted in. `color-scheme: dark` on
   :root fixes the little calendar and clock glyphs inside them, but not the box
   itself. Found 2026-09-20 on the new number and time filters; the two DATE
   fields — date of hire, and the logbook's rotation date — had been rendering
   that way already and are fixed by the same line. Add the type here whenever a
   new kind of field appears. */
input[type="text"],
input[type="password"],
input[type="number"],
input[type="date"],
input[type="time"],
select,
textarea {
  width: 100%;
  background: var(--background);
  color: var(--on-surface);
  border: 1px solid var(--outline);
  border-radius: var(--radius-sm);
  padding: 11px 13px;
  font-size: 15px;
  font-family: inherit;
}

input:focus, select:focus, textarea:focus {
  outline: none;
  border-color: var(--primary);
}

textarea {
  min-height: 190px;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 12.5px;
  line-height: 1.35;
  /* A rotation printout is fixed-width art: let it scroll sideways rather than
     re-wrapping every line into nonsense. */
  white-space: pre;
  overflow-wrap: normal;
  overflow-x: auto;
}

select {
  appearance: none;
  background-image: linear-gradient(45deg, transparent 50%, var(--on-surface-dim) 50%),
                    linear-gradient(135deg, var(--on-surface-dim) 50%, transparent 50%);
  background-position: calc(100% - 18px) 50%, calc(100% - 13px) 50%;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
  padding-right: 34px;
}

button {
  background: var(--primary);
  color: #041426;
  border: none;
  border-radius: 999px;
  padding: 12px 22px;
  font-size: 15px;
  font-weight: 600;
  font-family: inherit;
  cursor: pointer;
}

button:disabled { opacity: 0.45; cursor: default; }

button.ghost {
  background: transparent;
  color: var(--on-surface-dim);
  border: 1px solid var(--outline);
  padding: 8px 15px;
  font-size: 13px;
  font-weight: 500;
}

.actions {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  margin-top: 4px;
}

/* ── The profile strip ───────────────────────────────────────────────────── */

.profile-strip {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  background: var(--surface);
  border-radius: var(--radius-sm);
  padding: 9px 12px;
  margin-bottom: 16px;
  font-size: 13px;
  color: var(--on-surface-dim);
  cursor: pointer;
}

.profile-strip b { color: var(--on-surface); font-weight: 600; }
.profile-strip .edit { margin-left: auto; color: var(--primary); }

.profile-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 0 12px;
}

/* ── Tables ──────────────────────────────────────────────────────────────── */

.table-wrap { overflow-x: auto; margin: 0 -4px; }

table {
  width: 100%;
  border-collapse: collapse;
  font-size: 13.5px;
}

th {
  text-align: right;
  font-weight: 500;
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--on-surface-faint);
  padding: 0 6px 7px;
  white-space: nowrap;
}

td {
  padding: 7px 6px;
  text-align: right;
  border-top: 1px solid var(--outline);
  white-space: nowrap;
  /* Digits keep an equal width so a column of times or dollars lines up down the
     page, while the font stays proportional. Same rule as the app's pay rows. */
  font-variant-numeric: tabular-nums;
}

th:first-child, td:first-child { text-align: left; padding-left: 2px; }
th:last-child,  td:last-child  { padding-right: 2px; }

/* Letters don't benefit from tabular figures — a route column needs a monospaced
   font to line up at all. */
td.route, td.flight {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 12.5px;
}

td.dim  { color: var(--on-surface-dim); }

/* An unrecognised row, shown exactly as the card printed it. Left-aligned and
   monospaced because it is a quotation from the document, not a value. */
td.raw-row {
  text-align: left;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 11.5px;
  color: var(--on-surface-dim);
  white-space: pre;
}
td.mark { color: var(--primary); font-weight: 600; width: 1.4em; padding-right: 0; }

tr.total td {
  border-top: 1px solid var(--on-surface-faint);
  font-weight: 600;
  color: #fff;
}

/* ── Tiles ───────────────────────────────────────────────────────────────
   The three headline figures and the premium-pay grid, laid out the way the
   phone app lays them out (PayScreen.kt's TimecardSummaryCard): three across,
   then a four-wide grid of premium lines. Sized by fraction rather than fixed
   width so the row shrinks together on a narrow phone instead of one tile
   being starved while its neighbours keep their dp. */

.tiles {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 8px;
  margin-bottom: 8px;
}

.tile {
  background: var(--surface-variant);
  border-radius: var(--radius-sm);
  padding: 11px 12px;
  min-width: 0;
}

.tile .k {
  display: block;
  font-size: 11px;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--on-surface-dim);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.tile .v {
  display: block;
  margin-top: 3px;
  font-size: 19px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: #fff;
}

.tiles.premium { grid-template-columns: repeat(4, 1fr); }

.tiles.premium .tile { background: var(--surface); padding: 9px 10px; }
.tiles.premium .tile .v { font-size: 15px; }
.tiles.premium .tile.empty .v { color: var(--on-surface-faint); font-weight: 500; }

.bar {
  display: flex;
  align-items: center;
  gap: 12px;
  background: var(--surface-variant);
  border-radius: var(--radius-sm);
  padding: 13px 15px;
  margin-top: 12px;
}

.bar .k {
  font-size: 11.5px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--on-surface-dim);
}

.bar .v {
  margin-left: auto;
  font-size: 21px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: #fff;
}

.bar.accent { background: rgba(74, 159, 255, 0.14); }
.bar.accent .v { color: var(--primary); }

/* ── Rows of label/value ─────────────────────────────────────────────────── */

.rows { display: flex; flex-direction: column; }

.row {
  display: flex;
  align-items: baseline;
  gap: 10px;
  padding: 8px 0;
  border-top: 1px solid var(--outline);
}

.row:first-child { border-top: none; }
.row .k { color: var(--on-surface-dim); font-size: 13.5px; }
.row .v {
  margin-left: auto;
  font-variant-numeric: tabular-nums;
  font-weight: 600;
  white-space: nowrap;
}

.row.winner { color: #fff; }
.row.winner .k { color: var(--on-surface); }
.row.winner .v { color: var(--primary); }

.row .tag {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--primary);
  border: 1px solid var(--primary);
  border-radius: 999px;
  padding: 1px 8px;
}

/* ── One rotation the month is still waiting for ──────────────────────────
   Deliberately a small card per rotation rather than a bare stack of text
   boxes: a pilot pasting four of these has to see at a glance which one they
   are on and which are already counted. */
.rot-need {
  border: 1px solid var(--outline);
  border-radius: var(--radius-sm);
  padding: 12px;
  margin: 10px 0;
  background: linear-gradient(180deg, var(--surface) 0%, var(--surface-variant) 100%);
}

.rot-need.done {
  /* Counted rotations recede so the eye lands on what is still wanted. Faded,
     never hidden — a pilot has to be able to see what the total is built on. */
  opacity: .72;
  background: none;
}

.rot-need .rot-head {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 8px;
  flex-wrap: wrap;
}

.rot-need .rot-head .when { opacity: .6; font-size: .86rem; }
.rot-need .rot-head .tag { margin-left: auto; }

.rot-need .tag.waiting {
  /* Amber, not red: a rotation still to paste is a step left to take, not a
     fault. Nothing is broken — the pilot simply has not got to it yet. */
  color: #FFB347;
  border-color: rgba(255, 179, 71, 0.45);
}

.rot-need textarea { min-height: 92px; }

/* A total that is knowingly short of a whole month. Marked down its left edge
   so the figure reads as still filling in at a glance, without dulling it into
   something that looks broken. */
.hero.provisional { border-left: 3px solid #FFB347; }

/* A field that wants the whole row rather than one column of the grid. */
.profile-grid .field.wide { grid-column: 1 / -1; }
.profile-grid .field.wide em {
  font-style: normal;
  opacity: .55;
  font-weight: 400;
}

/* ── Notices ─────────────────────────────────────────────────────────────── */

.notice {
  border-radius: var(--radius-sm);
  padding: 12px 14px;
  margin-bottom: 12px;
  font-size: 13.5px;
  background: rgba(74, 159, 255, 0.10);
  border: 1px solid rgba(74, 159, 255, 0.35);
}

.notice.warn {
  background: rgba(255, 179, 71, 0.10);
  border-color: rgba(255, 179, 71, 0.4);
}

.notice.bad {
  background: rgba(255, 138, 128, 0.10);
  border-color: rgba(255, 138, 128, 0.42);
}

/* Good news. The only notice that is not blue, amber or red — a restore that
   worked is the one moment on this page worth being cheerful about. */
.notice.good {
  background: rgba(124, 226, 176, 0.10);
  border-color: rgba(124, 226, 176, 0.38);
}

.notice.hint {
  background: rgba(74, 159, 255, 0.08);
  border-color: rgba(74, 159, 255, 0.3);
}

/* ── Profile setup (gate-setup) ─────────────────────────────────────────
   The onboarding profile form shown once after a new pilot saves their
   recovery code. Matches the phone's OnboardingScreen / PilotProfileFields. */

/* Named with label.field too, or the older `label.field > span` rule wins and
   paints these grey while the seat label (not inside a label) stays blue. */
.field-label-accent,
label.field > span.field-label-accent {
  display: block;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--primary);
  margin: 0 0 5px 3px;
}

.field-group {
  margin-bottom: 12px;
}

.seat-toggle {
  display: flex;
  gap: 10px;
}

.seat-btn {
  flex: 1;
  text-align: center;
  background: transparent;
  color: var(--on-surface);
  border: 1px solid var(--outline);
  border-radius: var(--radius-sm);
  padding: 13px 8px;
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
}

.seat-btn.active {
  background: var(--primary);
  color: #041426;
  border-color: var(--primary);
}

.field-pair {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 12px;
}

.field-pair label.field { margin-bottom: 0; }

.privacy-note {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  margin: 8px 0 4px;
  color: var(--on-surface-dim);
  font-size: 13px;
  line-height: 1.4;
}

.setup-gap {
  font-size: 13px;
  color: var(--on-surface-dim);
  min-height: 20px;
  margin-top: 4px;
}

/* ── Welcome banner ─────────────────────────────────────────────────────── */

.welcome-banner {
  background: rgba(74, 159, 255, 0.10);
  border: 1px solid rgba(74, 159, 255, 0.25);
  border-radius: var(--radius-sm);
  padding: 14px 16px;
  margin-bottom: 14px;
  font-size: 13.5px;
  color: var(--on-surface);
}

.welcome-banner b { color: #fff; }

.welcome-banner a {
  color: var(--primary);
  text-decoration: none;
}

.welcome-banner .dismiss {
  display: inline-block;
  margin-top: 8px;
  font-size: 12px;
  color: var(--on-surface-dim);
  cursor: pointer;
  background: none;
  border: none;
  padding: 0;
  font-family: inherit;
}

.notice b { display: block; margin-bottom: 3px; color: #fff; }

/* ── Raw answer ──────────────────────────────────────────────────────────── */

details.raw {
  margin-top: 6px;
  background: var(--surface);
  border-radius: var(--radius-sm);
  padding: 11px 14px;
}

details.raw summary {
  cursor: pointer;
  font-size: 13px;
  color: var(--on-surface-dim);
  list-style: none;
}

details.raw summary::-webkit-details-marker { display: none; }
details.raw summary::before { content: "▸ "; color: var(--primary); }
details.raw[open] summary::before { content: "▾ "; }

details.raw pre {
  margin: 12px 0 2px;
  max-height: 60vh;
  overflow: auto;
  font-size: 11.5px;
  line-height: 1.4;
  color: var(--on-surface-dim);
}

/* ── Bottom nav ──────────────────────────────────────────────────────────── */

/* One line of the reserve tab's standing card: label, optional tag and value on the top
   row, and the sentence explaining WHICH RULE produced it on its own line underneath. The
   shared .row helper puts that sentence inline after the label, where these particular ones
   wrapped and lost their ending. */
.res-line { padding: 11px 0; border-top: 1px solid var(--outline); }
.res-line:first-child { border-top: none; padding-top: 4px; }
.res-line .top { display: flex; align-items: baseline; gap: 9px; }
.res-line .top .k { font-size: 13.5px; color: var(--on-surface-dim); }
.res-line .top .v {
  margin-left: auto;
  font-size: 18px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--primary);
}
.res-line .why {
  margin-top: 4px;
  font-size: 12.5px;
  line-height: 1.45;
  color: var(--on-surface-faint);
}

/* ── Sub-tabs ──────────────────────────────────────────────────
   The bar below moves between the three destinations; this row moves between the
   screens inside one. Drawn to match the app's Material TabRow (PayScreen.kt and
   LogbookScreen.kt both use one): equal-width tabs, the selected one in primary
   with a 2px underline sitting ON the divider, the rest at 55% onSurface. Same
   shape in both places, because the whole point of sharing token names with
   Color.kt is that the two can be held up against each other.

   ⚠ Deliberately NOT sticky. showTab() scrolls to the top on every change, so the
   row is already in view the moment it is wanted, and a second sticky strip under
   the sticky top bar is two things competing for the same edge of the screen. */

nav.subtabs {
  display: flex;
  margin: 0 0 18px;
  border-bottom: 1px solid var(--outline);
}

nav.subtabs button {
  flex: 1;
  min-width: 0;
  background: none;
  border: none;
  border-radius: 0;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  padding: 11px 6px 9px;
  font-size: 13px;
  font-weight: 500;
  letter-spacing: 0.01em;
  color: var(--on-surface-dim);
  /* Three short words in a row that can be as narrow as 320px ÷ 3. Nothing here
     is long enough to need it today, but a future name that is gets cut with an
     ellipsis rather than wrapping the row to two lines. */
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

nav.subtabs button[aria-selected="true"] {
  color: var(--primary);
  font-weight: 600;
  border-bottom-color: var(--primary);
}

nav.bottom {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 30;
  display: flex;
  background: var(--surface);
  border-top: 1px solid var(--outline);
  padding-bottom: env(safe-area-inset-bottom, 0px);
}

nav.bottom button {
  flex: 1;
  background: none;
  border: none;
  border-radius: 0;
  color: var(--on-surface-dim);
  font-size: 11.5px;
  font-weight: 500;
  letter-spacing: 0.02em;
  padding: 9px 4px 10px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
}

nav.bottom button .pill {
  display: block;
  width: 46px;
  height: 26px;
  border-radius: 999px;
  display: grid;
  place-items: center;
  font-size: 15px;
  line-height: 1;
}

/* ⚠️ THE NARROW-PHONE OVERRIDE THAT USED TO LIVE HERE HAS BEEN REMOVED, 2026-09-22,
   and it is worth knowing why rather than adding it back.

   It shrank the pills because SEVEN tabs left a 46px pill inside a 59px button with
   6.5px of air each side, which read as cramped. The bar now holds FOUR, so at the
   narrowest phone (320px) each button is 80px and the same pill has 17px of air.
   The override would now be shrinking type for no reason at all.

   Nothing here was ever a fix: the buttons are `flex: 1`, so however many there are
   they divide the width evenly and the bar has never overflowed at any width. If a
   future destination makes five or six, re-measure — do not assume this note
   still covers it. */

nav.bottom button[aria-selected="true"] { color: var(--primary); }
nav.bottom button[aria-selected="true"] .pill {
  background: rgba(74, 159, 255, 0.15);
}

/* ── The two legal links ──────────────────────────────────────────────
   Quiet on purpose. They have to be findable and they have to not compete with the
   one thing the sign-up card exists to make a pilot read, which is their code. */
.legal-links {
  margin: 14px 0 0;
  font-size: 12.5px;
  color: var(--on-surface-faint);
}

.legal-links a {
  color: var(--on-surface-dim);
  text-decoration: none;
  border-bottom: 1px solid var(--outline);
}

.legal-links a:hover { color: var(--primary); border-bottom-color: var(--primary); }

.legal-links span { padding: 0 6px; }

/* ── Busy state ──────────────────────────────────────────────────────────── */

.working {
  display: inline-block;
  font-size: 13px;
  color: var(--on-surface-dim);
}

/* ── Logbook ─────────────────────────────────────────────────────────────
   The logbook is the one screen that keeps things, so it needs two shapes the
   pay tools never did: a source switch (a rotation vs a PAS), and a table whose
   rows can be opened and written in. */

.seg {
  display: flex;
  gap: 6px;
  background: var(--background);
  border: 1px solid var(--outline);
  border-radius: 999px;
  padding: 4px;
  margin-bottom: 14px;
}

.seg button {
  flex: 1;
  background: none;
  border: none;
  border-radius: 999px;
  color: var(--on-surface-dim);
  font-size: 13px;
  font-weight: 500;
  padding: 8px 14px;
}

.seg button[aria-selected="true"] {
  background: rgba(74, 159, 255, 0.15);
  color: var(--primary);
}

/* The logbook's four totals. The stock .tiles grid is three across, which
   leaves a fourth sitting alone on a second row — two-by-two on a phone,
   four across once there is room for it (see the wide media query). */
.tiles.four { grid-template-columns: repeat(2, 1fr); }

/* The month headings down the saved logbook. */
.lb-period {
  display: flex;
  align-items: baseline;
  gap: 10px;
  margin: 18px 0 6px;
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.04em;
  color: var(--primary);
}

.lb-period .when { margin-left: auto; font-weight: 400; color: var(--on-surface-dim); }

/* A leg's own line. The whole row opens, so it has to look openable. */
tr.lb-leg { cursor: pointer; }
tr.lb-leg:hover td { background: rgba(74, 159, 255, 0.06); }
tr.lb-leg td.open-mark { color: var(--primary); width: 1.2em; padding-right: 0; }

/* Takeoff / landing marks. A logged one is the accent; an unlogged one is a
   faint dash, so a row you have not filled in reads as unfinished rather than
   as a leg you did not fly. */
td.tl { width: 2.2em; font-weight: 600; }
td.tl.on  { color: var(--primary); }
td.tl.off { color: var(--on-surface-faint); }

/* The opened drawer under a leg. */
tr.lb-detail > td { padding: 0 2px 14px; }

.lb-drawer {
  background: var(--surface);
  border-radius: var(--radius-sm);
  padding: 13px 14px;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 11px 12px;
}

.lb-drawer .wide { grid-column: 1 / -1; }

.lb-drawer label.check {
  display: flex;
  align-items: center;
  gap: 9px;
  font-size: 13.5px;
  color: var(--on-surface);
  background: var(--background);
  border: 1px solid var(--outline);
  border-radius: var(--radius-sm);
  padding: 10px 12px;
}

.lb-drawer label.check input { width: 17px; height: 17px; accent-color: var(--primary); margin: 0; }
.lb-drawer label.check .cond { margin-left: auto; font-size: 11.5px; color: var(--on-surface-dim); }

.lb-drawer input[type="text"], .lb-drawer select { width: 100%; }

.lb-drawer .drop {
  grid-column: 1 / -1;
  display: flex;
  justify-content: flex-end;
}

.lb-drawer .drop button {
  background: none;
  border: 1px solid rgba(255, 138, 128, 0.4);
  color: var(--error);
  font-size: 12.5px;
  padding: 7px 13px;
}

/* The preview of what a paste found, before it is kept. */
.lb-found { border-left: 3px solid var(--primary); }
.lb-found .dupe { color: var(--on-surface-faint); }

@media (max-width: 420px) {
  .lb-drawer { grid-template-columns: 1fr; }
}

@media (min-width: 680px) {
  .hero .money { font-size: 46px; }
  main { padding: 22px 20px; }
  .tiles.four { grid-template-columns: repeat(4, 1fr); }
}

/* ── The door ────────────────────────────────────────────────────────────
   The recovery code is shown exactly once, so the one card that shows it has
   to look like the most important thing on the screen and read cleanly enough
   to be copied onto paper by hand. Monospace with wide letter-spacing, and
   big enough that a 6 and a G cannot be confused at arm's length. */

.code-show {
  font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
  font-size: clamp(17px, 5.4vw, 26px);
  letter-spacing: 0.09em;
  font-weight: 600;
  color: #fff;
  background: var(--background);
  border: 1px solid rgba(74, 159, 255, 0.4);
  border-radius: var(--radius-sm);
  padding: 16px 12px;
  margin-bottom: 12px;
  text-align: center;
  /* A long code must wrap between groups rather than overflow a narrow phone,
     and it must stay selectable so "copy" is not the only way to take it. */
  word-break: break-word;
  user-select: all;
  -webkit-user-select: all;
}

/* The code goes in the sign-in box the same way it comes out of the other card:
   same face, same spacing, so what was written down matches what is typed. */
#code-input {
  font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}
#code-input::placeholder { text-transform: none; letter-spacing: normal; }

/* Off the screen, but still a real laid-out field.
   ⚠️ NOT `display: none` and NOT the `hidden` attribute. Two things read these
   inputs and both skip anything display:none — the browser's password manager,
   deciding whether this is a login form worth remembering, and the file picker,
   which will not open from a button if its input was never laid out. Shrinking
   them to nothing (width/height 0) trips the same check in some browsers, so
   they stay full-size and are simply parked outside the viewport. */
.offscreen {
  position: absolute;
  left: -9999px;
  top: auto;
  opacity: 0;
  pointer-events: none;
}

.check {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  margin: 4px 0 12px;
  font-size: 13.5px;
  color: var(--on-surface-dim);
  cursor: pointer;
}

.check input {
  /* Big enough to hit with a thumb — this is the tick that stands between a
     pilot and losing their logbook, so it must not be a 13px default box. */
  width: 20px;
  height: 20px;
  margin: 1px 0 0;
  flex: 0 0 auto;
  accent-color: var(--primary);
}

button[disabled] {
  opacity: 0.45;
  cursor: not-allowed;
}

/* ── The month's trips ───────────────────────────────────────────────────── */

/* The place a package is dropped or tapped. Deliberately a large target rather
   than a file button beside a label: on a phone this is a thumb tap, and on the
   desktop it is where a file is dragged from a downloads folder. */
.droparea {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 5px;
  min-height: 110px;
  padding: 20px 16px;
  margin: 4px 0 2px;
  text-align: center;
  cursor: pointer;
  border: 1.5px dashed var(--outline);
  border-radius: var(--radius-sm);
  background: var(--background);
  color: var(--on-surface-dim);
  transition: border-color 120ms ease, background 120ms ease;
}

.droparea b { color: var(--on-surface); font-size: 15px; font-weight: 600; }
.droparea span { font-size: 13px; }

.droparea:hover,
.droparea:focus-visible {
  border-color: var(--primary);
  outline: none;
}

/* While a file is actually over it. The fill is what confirms the drop will
   land here rather than in the tab behind it. */
.droparea.over {
  border-color: var(--primary);
  border-style: solid;
  background: rgba(74, 159, 255, 0.08);
}

/* How far through the pages it is. Only a PDF reports progress — a ZIP is read
   in one go — so this appears and disappears by format, which is correct: an
   empty bar that never moves is worse than no bar at all. */
.prog {
  height: 6px;
  margin: 10px 0 2px;
  border-radius: 3px;
  background: var(--surface-variant);
  overflow: hidden;
}

.prog span {
  display: block;
  height: 100%;
  border-radius: 3px;
  background: var(--primary);
  transition: width 220ms ease;
}

tr.bid-trip { cursor: pointer; }
tr.bid-trip:hover td { background: rgba(74, 159, 255, 0.06); }

/* The flags on a trip — holiday, duty-limit pay, a sit, a deadhead. Small and
   quiet: they mark a row as worth opening, they are not the row's content. */
td.marks {
  font-size: 11px;
  letter-spacing: 0.4px;
  color: var(--primary);
  white-space: nowrap;
}

.bid-drawer {
  background: var(--surface);
  border-radius: var(--radius-sm);
  padding: 13px 14px;
}

.bid-drawer .note { margin: 13px 0 5px; }

/* The legs inside an opened trip. Tighter than the outer table on purpose —
   eight columns of a six-leg trip have to fit a phone without a sideways
   scroll inside a sideways scroll. */
.bid-drawer table { font-size: 12.5px; }
.bid-drawer td, .bid-drawer th { padding: 6px 8px; }

/* A button that reads as a link. Used where the action belongs inside a
   sentence or a row rather than sitting on its own as a filled control. */
button.linkish {
  background: none;
  border: none;
  padding: 0 2px;
  font: inherit;
  font-size: 13px;
  color: var(--primary);
  cursor: pointer;
  text-decoration: underline;
}

button.linkish:hover { opacity: 0.8; }

/* The filter row needs wider columns than the profile row it borrows its grid
   from. A time field is not a text field: the browser draws its own hour /
   minute / AM-PM spinners and a clear button inside it, and all of that has a
   floor of roughly 170px before it starts clipping the AM/PM off the end. At
   the shared 150px minimum the two check-in fields were cut on a desktop —
   found by looking at a real package on screen, not by reading the rule. */
#bid-filters .profile-grid {
  grid-template-columns: repeat(auto-fit, minmax(178px, 1fr));
  gap: 0 14px;
}

#bid-filters input[type="time"],
#bid-filters input[type="number"] {
  width: 100%;
  min-width: 0;
}

/* The two flags sit on one line under the grid rather than in it — they are
   yes/no, and a full grid cell each leaves a hole beside them. */
#bid-filters .actions label.check { margin: 0 18px 0 0; }

/* ── The 117 tab ─────────────────────────────────────────────────────────── */

/* A kept rotation: what it is, when it was, and the way to get rid of it. */
.kept-rot {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 9px 0;
  border-top: 1px solid var(--outline);
}

.kept-rot:first-child { border-top: none; }

.kept-rot .kept-name {
  font-weight: 600;
  color: var(--on-surface);
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 13.5px;
  /* A long route must shorten rather than push the Remove button off the row. */
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.kept-rot .kept-when {
  margin-left: auto;
  font-size: 12px;
  color: var(--on-surface-dim);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.kept-rot button { flex: none; }

/* One rolling limit. */
.gauge { padding: 12px 0 14px; border-top: 1px solid var(--outline); }
.gauge:first-child { border-top: none; padding-top: 2px; }

.gauge-head {
  display: flex;
  align-items: baseline;
  gap: 10px;
  margin-bottom: 7px;
}

.gauge-head .k { font-size: 13.5px; color: var(--on-surface); }
.gauge-head .win { color: var(--on-surface-dim); font-size: 12px; }

.gauge-head .v {
  margin-left: auto;
  font-weight: 700;
  font-size: 17px;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.gauge-head .of {
  font-weight: 500;
  font-size: 12.5px;
  color: var(--on-surface-dim);
}

.gauge-track {
  height: 7px;
  border-radius: 999px;
  background: var(--surface-variant);
  overflow: hidden;
}

/* The flat fallback. app.js mixes in --error over the top by how close the
   gauge is to a breach; a browser without color-mix keeps exactly this. */
.gauge-fill {
  height: 100%;
  border-radius: 999px;
  background: var(--primary);
  transition: width 240ms ease;
}

.gauge-foot {
  margin-top: 6px;
  font-size: 12px;
  color: var(--on-surface-dim);
}

.gauge.over .gauge-head .v,
.gauge.over .gauge-foot { color: var(--error); }

/* A window nothing is loaded for: an empty track, and no figure to mistake. */
.gauge.uncovered .gauge-head .v { color: var(--on-surface-faint); font-weight: 500; }
.gauge.uncovered .gauge-track { background: var(--surface-variant); opacity: .45; }

/* Duty periods and the ledgers behind each gauge. Tabular figures keep the
   digits in line down a column; the route column is monospaced because tabular
   figures do nothing for LETTERS. */
.duty-table { font-size: 13px; }
.duty-table td.num { font-variant-numeric: tabular-nums; white-space: nowrap; }
.duty-table td.route {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  white-space: nowrap;
}
.duty-table td.state { color: var(--on-surface-dim); font-size: 12px; white-space: nowrap; }
.duty-table tr.over td { color: var(--error); }

details.ledger { margin: 4px 0 10px; }
details.ledger > summary {
  cursor: pointer;
  padding: 8px 0;
  font-size: 13.5px;
  color: var(--primary);
}
details.ledger[open] > summary { margin-bottom: 4px; }

/* Six tabs, not five (the 117 tab, 2026-09-21). At 360px a fixed 46px pill plus
   "Timecard" at 11.5px no longer fits across six columns, so on a narrow screen
   the pill gives up its width first and the label shrinks a little — sized to
   fit rather than left to clip. */
@media (max-width: 400px) {
  nav.bottom button { font-size: 10.5px; padding-left: 2px; padding-right: 2px; }
  nav.bottom button .pill { width: 38px; }
}

/* ── Home: the sky over the day ───────────────────────────────────────────
   Full-bleed imagery that breaks out of the page's 16px gutter — the same
   pattern Apple Weather, Google Maps and Uber use for a map or a picture,
   rather than boxing a satellite loop inside the padded card style the text
   below it uses. Everything else on the card floats ON the picture. */

.wx {
  position: relative;
  /* Out through main's padding on three sides, and on a wide screen it simply
     stops at main's own 900px — MORE room, never a different design. */
  margin: -16px -16px 14px;
  height: 46vh;
  max-height: 420px;
  min-height: 220px;
  background: #000;
  overflow: hidden;
  /* The swipe changes airport, so the browser must not also read it as a
     sideways scroll of the page. Up and down still scroll normally. */
  touch-action: pan-y;
}

.wx[hidden] { display: none; }

.wx-stage { position: absolute; inset: 0; }

/* Every frame of the loop is its own element, stacked; the ticker shows one.
   A short fade means a loop of six pictures reads as motion rather than as a
   slideshow clicking over. */
.wx-frame {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  transition: opacity 120ms linear;
}

/* The three pieces of furniture, in the three corners the dots leave free.
   Each sits on its own dark scrim so white cloud underneath cannot swallow it. */
.wx-label,
.wx-age,
.wx-credit {
  position: absolute;
  z-index: 2;
  background: rgba(9, 12, 18, 0.62);
  border-radius: 999px;
  color: var(--on-surface);
  font-size: 12px;
  line-height: 1;
}

.wx-label {
  top: 12px;
  left: 12px;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 7px 10px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.wx-label strong { color: #fff; font-size: 13px; letter-spacing: 0.08em; }

.wx-layers { display: flex; gap: 2px; }

.wx-layers button {
  background: none;
  border: none;
  border-radius: 999px;
  padding: 4px 8px;
  color: var(--on-surface-dim);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.08em;
}

.wx-layers button[aria-selected="true"] {
  background: rgba(74, 159, 255, 0.22);
  color: var(--primary);
}

.wx-age {
  top: 12px;
  right: 12px;
  border: none;
  padding: 7px 10px;
  color: var(--on-surface-dim);
}

.wx-age[disabled] { opacity: .85; }
.wx-age[hidden] { display: none; }

/* A licence obligation, not decoration: Meteosat's and NASA's terms require the
   credit wherever their imagery is drawn. */
.wx-credit {
  bottom: 10px;
  left: 12px;
  padding: 5px 9px;
  font-size: 10.5px;
  color: var(--on-surface-dim);
  max-width: 70%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.wx-dots {
  position: absolute;
  z-index: 2;
  bottom: 10px;
  left: 0;
  right: 0;
  display: flex;
  justify-content: center;
  gap: 7px;
}

.wx-dot {
  width: 7px;
  height: 7px;
  padding: 0;
  border: none;
  border-radius: 999px;
  background: rgba(197, 207, 224, 0.45);
}

.wx-dot.on { background: #fff; width: 18px; }

/* The ATIS text itself. Collapsed it is the one live line the field is saying;
   open it is every product, in the order a pilot briefs in. */
details.wx-text > summary {
  cursor: pointer;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--on-surface);
  /* Two lines and no more while closed — a full ATIS is a paragraph, and a
     glance card that grows to twelve lines is not a glance. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

details.wx-text[open] > summary { display: none; }

.wx-sec { margin-top: 10px; }
.wx-sec:first-child { margin-top: 2px; }

.wx-sec .k {
  display: block;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.08em;
  color: var(--primary);
  margin-bottom: 2px;
}

.wx-sec pre {
  margin: 0;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 12.5px;
  line-height: 1.5;
  white-space: pre-wrap;
  word-break: break-word;
  color: var(--on-surface);
}

/* The radar map fills the same box the pictures do, and sits under the same
   furniture. `.maplibregl-canvas` is sized by the library itself; what matters
   here is that the container is the full card and that nothing inside it can
   take a gesture — the map is inert on Home by design (see app.js). */
.wx-map {
  position: absolute;
  inset: 0;
  background: #090C12;
}

.wx-map[hidden] { display: none; }
.wx-stage[hidden] { display: none; }

/* MapLibre's own controls are not used here — no zoom buttons, no compass, no
   attribution box of its own (the credit line on the card carries it). */
.wx-map .maplibregl-control-container { display: none; }

/* ── The duty clock, and the layover clock it becomes ─────────────────────
   ⚠️ ONE COLOUR THROUGHOUT, at every phase, on purpose. Delta owns duty-time
   enforcement; this is a reference. A clock that turns red as the hours run
   out is telling a pilot to do something, and crossing the scheduled ceiling
   is not the end of the day — it is the end of the part that needs nobody's
   agreement. Even the under-floor flag is the accent colour, not an error one:
   it names a fact, it does not raise an alarm. */

.card.duty h3 { display: flex; align-items: center; gap: 8px; }

.pill-kind {
  margin-left: auto;
  padding: 2px 8px;
  border-radius: 999px;
  background: var(--surface-variant);
  color: var(--on-surface-dim);
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 0.08em;
}

.duty-big {
  display: flex;
  align-items: flex-end;
  gap: 10px;
  margin: 2px 0 10px;
}

.duty-big b {
  font-size: 40px;
  font-weight: 700;
  line-height: 1;
  color: var(--primary);
  font-variant-numeric: tabular-nums;
}

.duty-big > span {
  font-size: 13px;
  color: var(--on-surface-dim);
  padding-bottom: 4px;
}

.duty-big .unit {
  color: var(--primary);
  font-size: 15px;
  letter-spacing: 0.06em;
}

/* Under a regulatory floor. Accent, never error — see the note above. */
.duty-flag {
  margin-left: auto;
  align-self: center;
  padding: 2px 8px;
  border-radius: 999px;
  background: rgba(74, 159, 255, 0.15);
  color: var(--primary);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.04em;
}

/* Elapsed against the SCHEDULED maximum. Same fill at every fraction. */
.duty-bar {
  height: 6px;
  border-radius: 3px;
  background: rgba(197, 207, 224, 0.12);
  overflow: hidden;
  margin-bottom: 10px;
}

.duty-bar i { display: block; height: 100%; background: var(--primary); }

.duty-line {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 13px;
  color: var(--on-surface);
  font-variant-numeric: tabular-nums;
  margin-bottom: 4px;
}

.duty-line .duty-lims { margin-left: auto; display: flex; gap: 10px; white-space: nowrap; }
.duty-line .duty-lims span:last-child { color: var(--on-surface-dim); }
.duty-report em { color: var(--primary); font-style: normal; font-size: 11px; }

/* The reference readout: label, figure, and the floor it is measured against.
   Fixed columns so the figures line up down the card. */
.duty-refs { margin: 10px 0 2px; border-top: 1px solid var(--outline); }

.duty-ref {
  display: flex;
  align-items: baseline;
  gap: 10px;
  padding: 7px 0 0;
}

.duty-ref .k { width: 108px; flex: none; font-size: 13px; color: var(--on-surface); }
.duty-ref .v {
  width: 58px;
  flex: none;
  text-align: right;
  font-size: 15px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--on-surface);
}
.duty-ref.under .v { color: var(--primary); }
.duty-ref .n { font-size: 11.5px; color: var(--on-surface-dim); }

/* An editable anchor: a small time field with the clock it is on named beside
   it. ⚠️ The zone label is not decoration — block-in, in-room and pickup are
   typed on the clock where the pilot is STANDING, while report stays on the
   base clock like everything else on this page. */
.duty-edit {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: 8px;
}

.duty-edit label { display: flex; align-items: center; gap: 8px; margin: 0; flex: none; }
.duty-edit label > span {
  width: 92px;
  font-size: 12.5px;
  color: var(--on-surface-dim);
  margin: 0;
}
.duty-edit label > span em {
  font-style: normal;
  display: block;
  font-size: 10.5px;
  color: var(--on-surface-faint);
  letter-spacing: 0.04em;
}
.duty-edit input[type="time"] { width: 116px; padding: 7px 10px; font-size: 14px; }
.duty-note { font-size: 12px; color: var(--on-surface-dim); }

/* The one-tap actions. Tinted rather than alarming: ending duty is a good
   state, and arriving at the hotel is a better one. */
.duty-act {
  display: block;
  width: 100%;
  margin-top: 12px;
  padding: 11px 10px;
  border: none;
  border-radius: 10px;
  background: rgba(74, 159, 255, 0.12);
  color: var(--primary);
  font-size: 13px;
  font-weight: 600;
}

button.linky {
  background: none;
  border: none;
  padding: 0;
  color: var(--primary);
  font-size: 12px;
}

.duty-resume { display: block; margin-top: 12px; color: var(--on-surface-dim); }

/* The report, read as a line and corrected in place — never both at once. */
.duty-reportline {
  font-size: 13px;
  color: var(--on-surface);
  font-variant-numeric: tabular-nums;
}

/* ── Picking a trip's departure dates ──────────────────────────────────────
   A bid package prints a pattern, not a diary, so a repeating trip offers
   several real dates and the pilot takes the ones they want. The chips live
   inside the trip's own drawer, which is why they are sized to sit under a
   phone-width table rather than as a row of buttons across a page.

   ⚠️ A taken chip is the ACCENT COLOUR FILLED, not a tick added beside it: at
   this size a tick is a smudge, and "which of these six am I on" has to be
   answerable at a glance without reading. Same reason a taken ROW is marked in
   its leading cell — the count, not a checkbox, because the honest answer to
   "is this trip in my bid" is a number when the trip repeats. */
.datechips {
  display: flex;
  flex-wrap: wrap;
  gap: 7px;
  margin: 2px 0 4px;
}

.datechips .datechip {
  background: var(--surface-variant);
  color: var(--on-surface);
  border: 1px solid transparent;
  border-radius: 999px;
  padding: 7px 13px;
  font-size: 12.5px;
  font-weight: 500;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  cursor: pointer;
}

.datechips .datechip:hover { border-color: var(--outline); }

.datechips .datechip.on {
  background: var(--primary);
  color: #041426;                     /* the same ink every filled control uses */
  border-color: var(--primary);
}

/* This departure runs through a day the pilot asked to keep. A DASHED OUTLINE,
   not a dimming — dimmed reads as "you cannot have this", and the whole point is
   that they still can. Survives being taken: a chip that is both avoided and
   picked keeps the filled accent and gains the outline. */
.datechips .datechip.avoided {
  border-style: dashed;
  border-color: var(--primary);
}

/* How many of this trip's departures are in the bid. Blank when none are, so a
   list of 400 trips stays a list and not a column of noughts. */
td.tookmark {
  width: 34px;
  color: var(--primary);
  font-size: 12px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

tr.bid-trip.taken td { background: rgba(74, 159, 255, 0.07); }

/* ── The month, with a bar per picked trip ─────────────────────────────────
   Google Calendar / Outlook month view, which is where pilots already know
   this pattern from. Every row is the same seven-column grid, so the day
   numbers, the bars and the "+n" overflow all line up in one column per day
   without a single absolute position or a pixel of arithmetic in JavaScript.

   ⚠️ The bar colours are ROTATIONS OF THE ACCENT, not a palette. Six slots,
   keyed by rotation number, so the same trip bid on three dates is one colour
   everywhere — and if the accent is ever changed, all six move with it rather
   than six hardcoded hues drifting away from the theme. */
.cal {
  margin: 4px 0 14px;
}

.cal-row {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 2px;
}

.cal-head span {
  text-align: center;
  font-size: 10.5px;
  letter-spacing: 0.08em;
  color: var(--on-surface-faint);
  padding-bottom: 3px;
}

.cal-week { margin-bottom: 7px; }

.cal-num {
  text-align: center;
  font-size: 11.5px;
  font-variant-numeric: tabular-nums;
  color: var(--on-surface-dim);
  padding: 2px 0 3px;
}

/* A day outside the bid window is a real day the pilot simply cannot be given
   a trip on — dimmed, never blank, because blank reads as "no such day". */
.cal-num.out { color: rgba(197, 207, 224, 0.18); }

/* The break between two picked trips falls here. NOT "two bars share this square":
   two trips on one day can leave twelve legal hours between them, and two trips on
   consecutive days can be the pair that cannot both be flown. Marked quietly and
   named in full under the table — a bid is a preference list, so this is
   information, not an alarm, and never a red state. */
.cal-num.tight {
  color: var(--primary);
  font-weight: 600;
}

.cal-lane { margin-bottom: 2px; }

.cal-bar {
  height: 15px;
  line-height: 15px;
  background: var(--primary);
  color: #041426;
  font-size: 10px;
  font-style: normal;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  text-align: center;
  overflow: hidden;
  white-space: nowrap;
  /* Square where the trip runs on into the week before or after, rounded where
     it genuinely begins and ends — the clipped edge has to LOOK clipped or a
     four-day trip across a Saturday reads as two short ones. */
  border-radius: 0;
}

.cal-bar.opens  { border-top-left-radius: 7px;  border-bottom-left-radius: 7px; }
.cal-bar.closes { border-top-right-radius: 7px; border-bottom-right-radius: 7px; }

.cal-bar.s1 { filter: hue-rotate(38deg); }
.cal-bar.s2 { filter: hue-rotate(76deg); }
.cal-bar.s3 { filter: hue-rotate(-46deg); }
.cal-bar.s4 { filter: hue-rotate(128deg); }
.cal-bar.s5 { filter: hue-rotate(-92deg); }

/* Past the third lane a bar is not drawn at all — four hairlines in a
   phone-width cell is a smear, not information — so it becomes a count. */
.cal-more span {
  text-align: center;
  font-size: 9.5px;
  color: var(--on-surface-faint);
}

/* ── The days to avoid ──────────────────────────────────────────────────────
   The bid window as a grid of buttons. Same seven-column grid the picked-trip
   calendar uses, so the two read as the same month — but every cell here is a
   control, where every cell there is a picture. */

.avoid { margin-top: 14px; }

.avoid-head {
  display: flex;
  align-items: baseline;
  gap: 8px;
  margin-bottom: 6px;
}

.avoid-head > span:first-child {
  font-size: 12px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--on-surface-dim);
}

.avoid-head .sub {
  font-size: 12px;
  color: var(--on-surface-dim);
}

.avoid-head .linkish { margin-left: auto; }

.avoid-row {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 3px;
  margin-bottom: 3px;
}

.avoid-head-row span {
  text-align: center;
  font-size: 10.5px;
  letter-spacing: 0.06em;
  color: var(--on-surface-dim);
  padding-bottom: 2px;
}

/* 40px tall: a finger target, not a date on a page. Below about 36 the miss
   rate on a phone climbs sharply, and a mis-tap here silently narrows the list. */
.avoid-day {
  height: 40px;
  border: 1px solid var(--outline);
  border-radius: 9px;
  background: transparent;
  color: var(--on-surface);
  font: inherit;
  font-size: 13px;
  cursor: pointer;
  padding: 0;
}

/* Avoided: FILLED in the accent, the same way a taken date chip is filled rather
   than ticked. A struck-through or greyed day would read as "not available",
   which is the opposite of what the pilot just said. */
.avoid-day.on {
  background: var(--primary);
  border-color: var(--primary);
  color: #041426;
  font-weight: 600;
}

/* Outside the bid window: a real day, just not one a trip can be awarded on, so
   it is shown and not tappable. Blank would read as "no such day". */
.avoid-day.out {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 40px;
  font-size: 13px;
  color: rgba(197, 207, 224, 0.18);
}

/* ── Where you stand ──────────────────────────────────────────────────────
   The seniority tab. Two figures and a bar, and the whole design is about
   which of them is the headline.

   A pilot's SYSTEM number is a fact whatever category they sit in — it is what
   ALPA publishes and what a pilot quotes. Their standing INSIDE a category is
   the number they actually feel, and it is the one that changes when they
   switch seat or fleet. So the system number is the header, set small and in
   the accent, and the category standing gets the gradient card underneath it.
   A pilot with no bid category (leave, training, instructing, management) sees
   the header alone, which is the truth: they do not stand anywhere. */

.sen-head { margin-bottom: 14px; }

.sen-asof {
  font-size: 13px;
  font-weight: 500;
  color: var(--on-surface-dim);
}

.sen-number {
  margin-top: 2px;
  font-size: 22px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--primary);
}

.sen-of {
  margin-top: 1px;
  font-size: 13px;
  font-variant-numeric: tabular-nums;
  color: var(--on-surface-dim);
}

.hero.sen-hero { padding-bottom: 18px; }

.sen-sub { font-size: 13px; color: rgba(255, 255, 255, 0.72); }

.sen-top {
  font-size: 40px;
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1.1;
  color: #fff;
  font-variant-numeric: tabular-nums;
}

.sen-rank {
  font-size: 15px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: rgba(255, 255, 255, 0.86);
}

/* Filled from the LEFT, so a longer bar means a more junior pilot — the same
   direction the "Top 64.2%" above it reads. Filling from the right would put
   the senior end of the list on the right of the bar while the number above
   said the opposite, and nothing on screen would resolve the contradiction. */
.sen-bar {
  margin-top: 14px;
  height: 6px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.16);
  overflow: hidden;
}

.sen-bar span {
  display: block;
  height: 100%;
  border-radius: 999px;
  background: var(--primary);
}

/* ── The seat a pilot without one picks ───────────────────────────────────
   About one pilot in nine holds no bid domicile — leave, training, the
   instructor corps, management — and every rank on this screen is withheld
   from them. This card is what sits where their standing would be: an offer
   to measure them against a seat, rather than a grey notice explaining why
   there is nothing to see.

   ⚠️ NATIVE <select>, deliberately, and not this project's own
   BelowAnchorDropdownMenu. The phone rule exists because Material 3 flips its
   menu above the anchor near the bottom of a screen; a browser's own select
   does not have that problem, because a phone hands it to the platform, which
   draws a full-height wheel over everything. It is also the biggest, most
   familiar target available and needs no positioning code at all. */

.sen-pick {
  background: linear-gradient(180deg, var(--surface) 0%, var(--surface-variant) 100%);
  border-radius: var(--radius);
  padding: 16px;
  margin: 14px 0;
}

.sen-pick-head {
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--primary);
}

.sen-pick-why {
  margin: 6px 0 0;
  font-size: 13px;
  line-height: 18px;
  color: var(--on-surface-dim);
}

.sen-pick-ask {
  margin-top: 14px;
  font-size: 16px;
  font-weight: 700;
  color: var(--on-surface);
}

.sen-pick-hint {
  margin: 4px 0 0;
  font-size: 12px;
  line-height: 17px;
  color: var(--on-surface-dim);
}

/* ⚠️ EVERY FIELD IS WEIGHTED, never a fixed px — the rule the pay rows
   learned the hard way. These wrap to their own line rather than being
   starved: three fields across a 320px phone would leave the equipment code
   with nothing, and a code is exactly the kind of value that then wraps into
   two stacked halves. */
.sen-pick-fields {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-top: 12px;
}

.sen-pick-field {
  flex: 1 1 104px;
  min-width: 104px;
  display: block;
}

/* ⚠️ THE SEAT FIELD IS WIDER THAN THE OTHER TWO, AND IT HAS TO BE. Base and
   equipment are three-character codes; the seat is the words "First Officer",
   which needs about 95px of text before any padding. Sized like its
   neighbours it WRAPPED — "First" over "Officer" — which grew that one box
   taller than the row and clipped it. Caught by a screenshot, not by a check:
   the check asked about the <select>s and the button, and the box this
   happened in is neither.

   The wide basis is what makes it drop to its own full-width line on a narrow
   phone instead of being starved on a shared one, and sit beside the other
   two wherever there is room. Measure before shrinking it. */
.sen-pick-field.wide {
  flex: 1 1 172px;
  min-width: 172px;
}

.sen-pick-field > span {
  display: block;
  /* The label sits a few px in, to line up optically with the content start
     of the rounded control below it — a flush-left label against a rounded
     box reads as misaligned even though it technically is not. */
  padding-left: 4px;
  margin-bottom: 4px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: var(--on-surface-dim);
}

/* ⚠️ 44px tall and padded wide. A rounded control eats into the text near its
   edges that a square-cornered default assumes is straight, so the horizontal
   padding is larger than Material's default rather than equal to it. */
.sen-pick-field select,
.sen-pick-set {
  width: 100%;
  min-height: 44px;
  padding: 0 14px;
  border-radius: 12px;
  border: 1px solid var(--outline);
  background: var(--background);
  color: var(--on-surface);
  font: inherit;
  font-size: 15px;
  font-weight: 600;
  /* Room for the platform's own arrow without it landing on the text. */
  padding-right: 32px;
}

.sen-pick-field select:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 1px;
}

/* A part the LIST already settles — shown, not asked for, so the pilot can
   see it came off their own row rather than being guessed. */
.sen-pick-set {
  display: flex;
  align-items: center;
  border-style: dashed;
  color: var(--on-surface-dim);
  /* No platform arrow on this one, so the room a select reserves for it is
     given back to the text instead. */
  padding-right: 14px;
  white-space: nowrap;
}

.sen-pick-go {
  display: block;
  width: 100%;
  min-height: 48px;
  margin-top: 14px;
  border: 0;
  border-radius: 12px;
  background: var(--primary);
  color: #041426;                     /* the same ink every filled control uses */
  font: inherit;
  font-size: 15px;
  font-weight: 700;
  cursor: pointer;
}

/* ⚠️ Dimmed, not hidden, while the fields are incomplete. A button that
   vanishes leaves a pilot unsure whether there is a next step at all. */
.sen-pick-go[disabled] {
  opacity: 0.45;
  cursor: default;
}

.sen-pick-cancel {
  display: block;
  width: 100%;
  min-height: 44px;
  margin-top: 8px;
  border: 0;
  background: none;
  color: var(--primary);
  font: inherit;
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
}

.sen-pick-foot {
  margin: 10px 0 0;
  font-size: 11px;
  line-height: 15px;
  color: var(--on-surface-dim);
}

/* ── "This is an estimate" ────────────────────────────────────────────────
   ⚠️ THE ONE THING STANDING BETWEEN A PILOT ON LEAVE AND READING A
   HYPOTHETICAL AS THEIR STANDING. Everything under this banner is a rank in a
   seat they do NOT hold. It carries the accent and sits directly above the
   headline it qualifies, never below it and never folded away. */

.sen-est {
  display: flex;
  align-items: flex-start;
  flex-wrap: wrap;
  gap: 8px;
  padding: 10px 12px;
  margin-bottom: 10px;
  border-radius: 12px;
  border: 1px solid rgba(var(--primary-rgb), 0.45);
  background: rgba(var(--primary-rgb), 0.10);
}

.sen-est-tag {
  flex: 0 0 auto;
  padding: 2px 7px;
  border-radius: 999px;
  background: var(--primary);
  color: #041426;                     /* the same ink every filled control uses */
  font-size: 10px;
  font-weight: 800;
  letter-spacing: 0.06em;
}

.sen-est-text {
  flex: 1 1 160px;
  font-size: 12px;
  line-height: 17px;
  color: var(--on-surface);
}

.sen-est-change {
  flex: 0 0 auto;
  min-height: 32px;
  padding: 0 10px;
  border: 1px solid var(--primary);
  border-radius: 999px;
  background: none;
  color: var(--primary);
  font: inherit;
  font-size: 12px;
  font-weight: 700;
  cursor: pointer;
}

/* ── How you moved since last month ───────────────────────────────────────
   One headline that IS the answer, with the reasoning folded away one tap
   below it — the shape Credit Karma, Strava and Gmail's own summaries use.
   Collapsed by default, because the breakdown runs past twenty lines once the
   junior movement is spelled out, and the headline already answered the
   question the pilot opened the tab with.

   ⚠️ A gain celebrates in the accent; the rare backward move is MUTED, never
   red. A pilot slipping a few spots because someone senior transferred in has
   done nothing wrong and is being told a fact, not warned. */

.sen-move {
  background: linear-gradient(180deg, var(--surface) 0%, var(--surface-variant) 100%);
  border-radius: var(--radius);
  margin: 14px 0;
  overflow: hidden;
  /* The accent stripe the phone's card carries down its left edge. The 3px is
     ALWAYS reserved, transparent when there is nothing to open, so the text
     never shifts sideways between the two states. */
  border-left: 3px solid transparent;
}

/* ⚠️ The stripe advertises that something is FOLDED AWAY — it is not about
   whether the month went well. Only the disclosure card ever carries it, and
   it goes out once the card is open, because by then it has been answered. */
details.sen-move { border-left-color: var(--primary); }
details.sen-move[open] { border-left-color: transparent; }

.sen-move > summary {
  cursor: pointer;
  list-style: none;
  padding: 14px;
}

/* Safari and Chrome draw their own triangle on a summary; the chevron below is
   ours and sits where the eye already expects it. */
.sen-move > summary::-webkit-details-marker { display: none; }

.sen-move.solo { padding: 14px; }

.sen-move-head {
  display: flex;
  align-items: center;
  gap: 10px;
}

.sen-move-icon {
  font-size: 18px;
  line-height: 1;
  color: var(--on-surface-dim);
}

.sen-move.up .sen-move-icon { color: var(--primary); }

/* The one element allowed to take the leftover width, so the pill on the right
   keeps its own content width at any phone size rather than being squeezed. */
.sen-move-line {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
}

.sen-move-big {
  font-size: 17px;
  font-weight: 700;
  color: var(--on-surface-dim);
}

.sen-move.up .sen-move-big { color: var(--primary); }

.sen-move-since { font-size: 12px; color: var(--on-surface-dim); }

.sen-move-pill {
  flex: 0 0 auto;
  border-radius: 8px;
  padding: 4px 10px;
  font-size: 12px;
  font-weight: 600;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
  background: var(--surface-variant);
  color: var(--on-surface-dim);
}

.sen-move-pill.up {
  background: rgba(74, 159, 255, 0.15);
  color: var(--primary);
}

.sen-move-body { padding: 0 14px 14px; }

.sen-move-seg {
  margin: 12px 0 6px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.8px;
  color: rgba(197, 207, 224, 0.75);
}

.sen-move-seg span { font-weight: 400; letter-spacing: 0; color: var(--on-surface-dim); }

/* The count column is a fixed width with tabular figures so the signs and
   digits stack in a straight line down the card — the same treatment the pay
   rows get. The reason beside it wraps rather than truncating: not cutting a
   reason in half is the entire point of listing them. */
.sen-move-row {
  display: flex;
  align-items: baseline;
  gap: 12px;
  padding: 2px 0;
  font-size: 13px;
  color: var(--on-surface);
}

.sen-move-row > .n {
  flex: 0 0 auto;
  min-width: 38px;
  text-align: right;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--on-surface-dim);
}

.sen-move-row > .n.up { color: var(--primary); }

/* 38 + 12 puts a sub-count's first digit directly under the "j" of "joined" —
   a nested list, not a second indented column. */
.sen-move-sub { padding-left: 50px; }
.sen-move-sub .sen-move-row { font-size: 12px; color: var(--on-surface-dim); }
.sen-move-sub .sen-move-row > .n { min-width: 22px; text-align: left; }

.sen-move-total {
  margin: 10px 0 0;
  font-size: 12px;
  font-variant-numeric: tabular-nums;
  color: var(--on-surface-dim);
}

.sen-move-why {
  margin: 6px 0 0;
  font-size: 12px;
  line-height: 1.45;
  color: var(--on-surface-dim);
}

/* ── "Which month is this list?" ──────────────────────────────────────────

   Shown only when neither the file nor its name says which month it is. It is
   a question, not an error, so it is drawn in the accent rather than the error
   colour — nothing has gone wrong and the list read perfectly. */

.sen-ask {
  margin-top: 12px;
  padding: 14px;
  border-radius: var(--radius-sm);
  border-left: 3px solid var(--primary);
  background: var(--surface-variant);
}

.sen-ask h4 {
  margin: 0 0 4px;
  font-size: 15px;
  font-weight: 700;
  color: var(--on-surface);
}

.sen-ask .note { margin: 0 0 10px; }

/* The month needs the room — "September" is twice the width of a year — so it
   takes what is left while the year keeps its own content width. Sized by
   weight rather than a guessed width, so the row holds at any phone size. */
.sen-ask-row {
  display: flex;
  gap: 10px;
  align-items: flex-end;
  margin-bottom: 12px;
}

.sen-ask-row .field:first-child { flex: 1 1 auto; min-width: 0; }
.sen-ask-row .field:last-child  { flex: 0 0 auto; }

.sen-ask button { width: 100%; }

/* ── What if you switched… ────────────────────────────────────────────────

   Where a pilot would stand in a seat they do not hold. The movement card
   above is about time; this is about choice, so nothing here carries an arrow
   or a direction — every figure is simply where they would land today.

   ⚠️ Nothing is pressable. On the phone every row and square opens that
   category's roster; the web has no roster view yet, so none of this is given
   a pointer cursor or a hover lift. A control that looks pressable and does
   nothing is worse than a plain figure. */

.sen-sec {
  margin: 22px 0 4px;
  font-size: 15px;
  font-weight: 700;
  color: var(--on-surface);
}

/* The one what-if that changes a single thing about the pilot's own seat, so
   it gets a card of its own rather than a line in a list. */
.sen-swap {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 2px 12px;
  align-items: baseline;
  background: linear-gradient(180deg, var(--surface) 0%, var(--surface-variant) 100%);
  border-radius: var(--radius);
  padding: 14px;
  margin: 8px 0 4px;
}

.sen-swap-seat { font-size: 17px; font-weight: 700; color: var(--primary); }

.sen-swap-top {
  font-size: 17px;
  font-weight: 700;
  text-align: right;
  font-variant-numeric: tabular-nums;
}

.sen-swap-where,
.sen-swap-rank { font-size: 12px; color: var(--on-surface-dim); }

.sen-swap-rank { text-align: right; font-variant-numeric: tabular-nums; }

/* A list of options, best first.

   ⚠️ Every column that competes for width is WEIGHTED, not given a fixed dp
   — the rule the app's own pay rows learned the hard way. The label takes
   what is left; the percent and the rank keep their content width with
   tabular figures so the digits stack down the column. */
.sen-opts {
  background: linear-gradient(180deg, var(--surface) 0%, var(--surface-variant) 100%);
  border-radius: var(--radius);
  padding: 6px 14px;
  margin: 0 0 4px;
}

.sen-opt {
  display: flex;
  align-items: baseline;
  gap: 10px;
  padding: 6px 0;
  font-size: 13px;
  border-top: 1px solid var(--outline);
}

.sen-opt:first-child { border-top: 0; }

.sen-opt > .l {
  flex: 1 1 auto;
  min-width: 0;
  font-weight: 600;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.sen-opt > .p {
  flex: 0 0 auto;
  min-width: 52px;
  text-align: right;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.sen-opt > .r {
  flex: 0 0 auto;
  min-width: 88px;
  text-align: right;
  font-size: 12px;
  color: var(--on-surface-dim);
  font-variant-numeric: tabular-nums;
}

/* The pilot's own row, marked the way a map marks where you are standing. */
.sen-opt.here > .l { color: var(--primary); }

.sen-opt > .you {
  flex: 0 0 auto;
  border-radius: 6px;
  padding: 1px 6px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.4px;
  text-transform: uppercase;
  background: rgba(74, 159, 255, 0.15);
  color: var(--primary);
}

/* Folded away, and carrying the same accent stripe as the movement card —
   it means the same thing in both places: there is more here, one tap down. */
details.sen-where {
  background: linear-gradient(180deg, var(--surface) 0%, var(--surface-variant) 100%);
  border-radius: var(--radius);
  border-left: 3px solid var(--primary);
  margin: 12px 0;
  overflow: hidden;
}

details.sen-where[open] { border-left-color: transparent; }

.sen-where > summary { cursor: pointer; list-style: none; padding: 14px; }
.sen-where > summary::-webkit-details-marker { display: none; }
.sen-where .sen-move-big { color: var(--on-surface); }
.sen-where .sen-opts { background: none; padding: 0; }

/* ⚠️ The grid SCROLLS SIDEWAYS rather than shrinking to fit. Eight fleet
   columns squeezed into a 412px screen would drop the percent figures below
   the size everything else on the page is set in, and these are numbers a
   pilot reads off carefully. The base column stays pinned so a row can never
   be read against the wrong airport. */
.sen-grid-wrap {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  margin: 10px 0 0;
}

.sen-grid {
  border-collapse: separate;
  border-spacing: 3px;
  font-size: 11px;
  font-variant-numeric: tabular-nums;
}

.sen-grid th {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 11px;
  font-weight: 600;
  color: var(--on-surface-dim);
  padding: 0 4px;
}

.sen-grid tbody th {
  position: sticky;
  left: 0;
  z-index: 1;
  text-align: left;
  color: var(--on-surface);
  background: var(--surface);
}

.sen-grid td { padding: 0; }

.sen-grid td > span {
  display: block;
  min-width: 46px;
  padding: 7px 4px;
  border-radius: 6px;
  text-align: center;
  font-weight: 600;
  color: var(--on-surface);
  /* The fill itself is written per square in code — it varies with how senior
     the pilot would be there, which CSS has no way to work out. */
  border: 1.5px solid transparent;
}

/* A base and fleet nobody flies. Hollow and dashed so it can never be misread
   as "top 0%", which is the most senior thing the grid could possibly say. */
.sen-grid td.none > span {
  background: rgba(30, 42, 69, 0.4);
  color: var(--on-surface-faint);
}

.sen-grid td.here > span { border-color: var(--primary); }

.sen-grid-note {
  margin: 8px 0 0;
  font-size: 11px;
  line-height: 1.5;
  color: var(--on-surface-dim);
}

.sen-grid-note strong { color: var(--on-surface); font-weight: 600; }

/* ── Your number, to the day you retire ───────────────────────────────────
   A scrubber over a fixed readout, the retirement wave underneath it, and the
   year-by-year table under that. One control moves all of it. */

.sen-scrub-card {
  background: linear-gradient(180deg, var(--surface) 0%, var(--surface-variant) 100%);
  border-radius: var(--radius);
  padding: 14px 16px 10px;
  margin: 8px 0 4px;
}

.sen-scrub-read {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 2px 12px;
  align-items: baseline;
}

.sen-scrub-when {
  font-size: 13px;
  font-weight: 600;
  color: var(--on-surface-dim);
  font-variant-numeric: tabular-nums;
}

/* The caption is a chip rather than more words, so "now" and "retires" read
   as a marker on the timeline instead of part of the date. It is EMPTY on an
   ordinary stop, and an empty chip must take no room at all — otherwise the
   date shuffles sideways every time the thumb passes one. */
.sen-scrub-cap:not(:empty) {
  display: inline-block;
  margin-left: 7px;
  padding: 1px 7px;
  border-radius: 999px;
  background: rgba(var(--primary-rgb), 0.18);
  color: var(--primary);
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.sen-scrub-num {
  grid-column: 1;
  font-size: 26px;
  font-weight: 800;
  line-height: 1.15;
  font-variant-numeric: tabular-nums;
  color: var(--on-surface);
}

.sen-scrub-pct {
  grid-column: 2;
  grid-row: 2;
  font-size: 12px;
  font-weight: 600;
  text-align: right;
  color: var(--primary);
  font-variant-numeric: tabular-nums;
}

/* ⚠️ The track is styled in BOTH vendor dialects and the rules cannot be
   merged into one selector list: a browser that does not know one of the
   pseudo-elements throws the whole rule away, so a combined selector would
   silently leave the slider unstyled in whichever engine lost the toss. */
.sen-scrub {
  display: block;
  width: 100%;
  margin: 14px 0 2px;
  background: transparent;
  -webkit-appearance: none;
  appearance: none;
  /* A full-height grab area. The visible track is 4px; a 4px target is not
     something a thumb can find, so the control is padded out to 28px and the
     track drawn inside it. */
  height: 28px;
  cursor: pointer;
}

.sen-scrub:focus-visible { outline: 2px solid var(--primary); outline-offset: 3px; }

.sen-scrub::-webkit-slider-runnable-track {
  height: 4px;
  border-radius: 999px;
  background: rgba(var(--primary-rgb), 0.22);
}

.sen-scrub::-moz-range-track {
  height: 4px;
  border-radius: 999px;
  background: rgba(var(--primary-rgb), 0.22);
}

.sen-scrub::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 20px;
  height: 20px;
  margin-top: -8px;          /* centres a 20px thumb on a 4px track */
  border-radius: 50%;
  background: var(--primary);
  border: 3px solid var(--surface);
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4);
}

.sen-scrub::-moz-range-thumb {
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: var(--primary);
  border: 3px solid var(--surface);
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4);
}

.sen-scrub-ends {
  display: flex;
  justify-content: space-between;
  font-size: 10.5px;
  color: var(--on-surface-dim);
  font-variant-numeric: tabular-nums;
}

/* ── The wave ────────────────────────────────────────────────────────────── */

.sen-wave-card {
  background: linear-gradient(180deg, var(--surface) 0%, var(--surface-variant) 100%);
  border-radius: var(--radius);
  padding: 12px 14px 10px;
  margin: 0 0 4px;
}

.sen-wave-read {
  display: flex;
  align-items: center;
  gap: 12px;
  min-height: 34px;
}

.sen-wave-year {
  font-size: 20px;
  font-weight: 800;
  font-variant-numeric: tabular-nums;
  color: var(--on-surface);
  /* Fixed, because it is four digits forever and the lines beside it must not
     shuffle left and right as the thumb moves. */
  width: 52px;
  flex: 0 0 auto;
}

.sen-wave-lines { min-width: 0; flex: 1 1 auto; }

/* ⚠️ Both lines clip rather than wrap. A second line here makes the card
   taller mid-drag, which shoves the chart under the thumb down the page while
   the finger is on it. */
.sen-wave-ahead,
.sen-wave-co {
  display: block;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

.sen-wave-ahead { font-size: 13.5px; font-weight: 600; color: var(--primary); }
.sen-wave-co { font-size: 11px; color: var(--on-surface-dim); }

/* Every year always visible — see the note in app.js. The bars share the
   width rather than scrolling, so the shape of the wave is the thing you see
   first. */
.sen-wave {
  display: flex;
  align-items: flex-end;
  gap: 2px;
  height: 74px;
  margin-top: 10px;
}

.sen-wave-bar {
  position: relative;
  flex: 1 1 0;
  min-width: 0;
  height: 100%;
  display: flex;
  align-items: flex-end;
  border-radius: 2px;
}

/* The company total, faint; the senior-to-you part solid INSIDE it. */
.sen-wave-bar .co {
  display: flex;
  align-items: flex-end;
  width: 100%;
  border-radius: 2px 2px 0 0;
  background: rgba(var(--primary-rgb), 0.22);
}

.sen-wave-bar .ah {
  display: block;
  width: 100%;
  border-radius: 2px 2px 0 0;
  background: rgba(var(--primary-rgb), 0.72);
}

/* A part year, dimmed — it is short because the window cuts it, not because
   fewer people retire. The readout names the range in words as well. */
.sen-wave-bar.part .co { background: rgba(var(--primary-rgb), 0.12); }
.sen-wave-bar.part .ah { background: rgba(var(--primary-rgb), 0.38); }

/* ⚠️ THE SELECTED YEAR IS MARKED UNDER THE BAR, NEVER BEHIND IT — and this
   was a real fault, caught by looking at a screenshot rather than by any
   check. A full-height tint behind the column reads as PART OF THE COLUMN: a
   below-average year like 2035 (363 retirements) drew a block taller than
   every real bar in the chart, so the one year the pilot had just selected
   looked like the peak of the wave. The readout beside it said 363 and the
   picture said "tallest", which is the exact inverse of the truth.
   Nothing that marks a selection here may have a HEIGHT, because height is
   the only thing this chart uses to mean a number. */
.sen-wave-bar.on .ah,
.sen-wave-bar.on.part .ah { background: var(--primary); }
.sen-wave-bar.on .co { background: rgba(var(--primary-rgb), 0.34); }

.sen-wave-bar.on::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: -4px;
  height: 3px;
  border-radius: 999px;
  background: var(--primary);
}

.sen-wave-axis {
  display: flex;
  gap: 2px;
  margin-top: 5px;
}

.sen-wave-axis span {
  flex: 1 1 0;
  min-width: 0;
  font-size: 9.5px;
  color: var(--on-surface-dim);
  font-variant-numeric: tabular-nums;
  text-align: center;
  /* ⚠️ A four-digit year is wider than its own 16px column, so it must be
     allowed to spill into the empty columns either side instead of wrapping
     into two lines of two digits. The columns beside a labelled one are blank
     by construction — see the every-fifth-year rule in app.js. */
  overflow: visible;
  white-space: nowrap;
}

.sen-proj-head {
  margin: 10px 0 0;
  font-size: 13px;
  font-weight: 600;
  color: var(--primary);
}

/* ── Year by year ────────────────────────────────────────────────────────── */

.sen-projs {
  background: linear-gradient(180deg, var(--surface) 0%, var(--surface-variant) 100%);
  border-radius: var(--radius);
  padding: 4px 14px;
  margin: 0 0 4px;
}

/* ⚠️ EVERY COLUMN THAT COMPETES FOR WIDTH IS WEIGHTED, never a fixed px —
   the rule the app's own pay rows learned the hard way. A row of hardcoded
   widths is tuned for one screen and starves whichever column was left to
   soak up the remainder on a narrower one. The bar is the flexible one here
   because it is the only column with nothing to read in it. */
.sen-proj-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 7px 0;
  border-top: 1px solid var(--outline);
  font-size: 13px;
}

.sen-proj-row:first-child { border-top: 0; }

/* The two ends of the story — where you are now, and where you get off. */
.sen-proj-row.mark { font-weight: 700; }
.sen-proj-row.mark .d,
.sen-proj-row.mark .n { color: var(--primary); }

.sen-proj-row .d {
  flex: 0 0 auto;
  width: 62px;
  font-size: 12px;
  font-weight: 600;
  color: var(--on-surface-dim);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.sen-proj-row .n {
  flex: 0 0 auto;
  width: 62px;
  font-weight: 700;
  color: var(--on-surface);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.sen-proj-row .b {
  flex: 1 1 auto;
  min-width: 24px;
  height: 5px;
  border-radius: 999px;
  background: rgba(var(--primary-rgb), 0.18);
  overflow: hidden;
}

.sen-proj-row .b i {
  display: block;
  height: 100%;
  border-radius: 999px;
  background: var(--primary);
}

.sen-proj-row .p {
  flex: 0 0 auto;
  width: 66px;
  text-align: right;
  font-size: 12px;
  font-weight: 600;
  color: var(--primary);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* Hidden until the toggle opens them. The retirement row is never one of
   these — it stays in view in both states. */
.sen-proj-row.late { display: none; }
.sen-projs.all .sen-proj-row.late { display: flex; }

/* The row IS the button, in the same place in both states, so the thumb lands
   in the same spot twice. */
.sen-proj-more {
  display: block;
  width: 100%;
  min-height: 44px;
  padding: 10px 0;
  border: 0;
  border-top: 1px solid var(--outline);
  background: none;
  color: var(--primary);
  font: inherit;
  font-size: 12px;
  font-weight: 600;
  cursor: pointer;
  text-align: center;
}

.sen-proj-more .open { display: none; }
.sen-projs.all .sen-proj-more .shut { display: none; }
.sen-projs.all .sen-proj-more .open { display: inline; }

/* ⚠️ FIVE DESTINATIONS, RE-MEASURED 2026-09-22 — see the note beside
   nav.bottom. Four buttons left a 46px pill in an 82px button at a 412px
   phone; five leave 64px at the narrowest phone anyone still carries (320px),
   which is 9px of air each side and holds. What does NOT hold at that width is
   the word "Seniority" at 11.5px, which needs about 56px and has 56px — so it
   is the label, not the pill, that goes first. Below 360px the type steps down
   rather than the word wrapping onto a second line and shoving the bar taller.
   If a SIXTH destination is ever added, measure again; do not assume this
   covers it. */
@media (max-width: 359px) {
  nav.bottom button { font-size: 10px; padding-left: 2px; padding-right: 2px; }
  nav.bottom button .pill { width: 40px; }
}

/* ── A category's roster, over the page ──────────────────────────────────────
   Tapping a standing, a what-if row or a square in the grid opens the list it
   was worked out from. A SHEET rather than a page of its own: the pilot is
   part-way down a long screen comparing things, looks one category up, and
   goes back — a drill-down would lose their place behind it. Same shape as
   tapping a place in Google Maps or a track in Spotify. */

/* ⚠️ The page behind is frozen while the sheet is up, or a scroll gesture that
   runs off the end of the list starts moving the page underneath it and the
   pilot loses the position they were reading. */
body.sheet-open { overflow: hidden; }

/* ⚠️ The [hidden] rule has to be spelled out. `display: flex` below beats the
   browser's own `[hidden] { display: none }`, so without this the sheet would
   sit over every screen in the app from the moment the page loaded. Same
   reason .panel[hidden] and .wx[hidden] are written out above. */
.sheet[hidden] { display: none; }

.sheet {
  position: fixed;
  inset: 0;
  z-index: 60;                 /* above the bottom bar, which the sheet covers */
  display: flex;
  align-items: flex-end;
  justify-content: center;
}

.sheet-back {
  position: absolute;
  inset: 0;
  background: rgba(9, 12, 18, 0.72);
}

.sheet-card {
  position: relative;
  width: 100%;
  max-width: 560px;
  /* Tall, but never the whole screen — the strip of dimmed page left showing
     above it is what says "this is over something", and it is also the target
     a pilot presses to dismiss without hunting for the ✕. */
  height: 88vh;
  display: flex;
  flex-direction: column;
  background: var(--surface);
  border-radius: var(--radius) var(--radius) 0 0;
  border-top: 1px solid var(--outline);
  box-shadow: 0 -12px 40px rgba(0, 0, 0, 0.5);
  animation: sheet-rise 180ms ease-out;
}

@keyframes sheet-rise { from { transform: translateY(14px); opacity: 0; } }

/* Nothing slides for anyone who has asked their device for less movement. */
@media (prefers-reduced-motion: reduce) {
  .sheet-card { animation: none; }
}

.sheet-head {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 16px 12px 12px 20px;
  border-bottom: 1px solid var(--outline);
}

.sheet-titles { flex: 1; min-width: 0; }

.sheet-head h3 {
  margin: 0;
  font-size: 18px;
  font-weight: 700;
  color: var(--on-surface);
}

#sen-roster-sub {
  margin-top: 3px;
  font-size: 12.5px;
  color: var(--on-surface-dim);
  font-variant-numeric: tabular-nums;
}

/* 40px square: the smallest target that is still comfortable, and the size
   every platform's own close button lands on. */
.sheet-x {
  flex: none;
  width: 40px;
  height: 40px;
  padding: 0;
  font-size: 15px;
  line-height: 1;
  color: var(--on-surface-dim);
  background: var(--surface-variant);
  border: 0;
  border-radius: 50%;
  cursor: pointer;
}

.sheet-x:hover { color: var(--on-surface); }

.sheet-body {
  flex: 1;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  padding: 12px 20px calc(20px + env(safe-area-inset-bottom, 0px));
}

.sen-roster-note {
  margin: 0 0 12px;
  padding: 10px 12px;
  border-radius: var(--radius-sm);
  background: rgba(var(--primary-rgb), 0.10);
  font-size: 12.5px;
  line-height: 1.55;
  color: var(--on-surface);
}

.sen-roster-jump { margin: 0 0 10px; }

.sen-roster-jump button {
  padding: 7px 14px;
  font-size: 13px;
  font-weight: 600;
  color: var(--primary);
  background: rgba(var(--primary-rgb), 0.12);
  border: 0;
  border-radius: 999px;
  cursor: pointer;
}

/* ⚠️ FIXED LAYOUT, and it is not a preference. Left to size itself, the table
   widens to fit its longest cell — which is the pilot's own row, a full name
   plus a YOU chip plus a PROJECTED chip. It then overflowed the sheet and the
   list scrolled SIDEWAYS: the seniority column slid off the left edge and the
   two date columns ran into each other with no gap. Four columns whose content
   is bounded, sized here once, and the one that varies (the name) truncates. */
.sen-roster-tbl {
  width: 100%;
  table-layout: fixed;
  border-collapse: collapse;
  font-size: 13px;
}

.sen-roster-tbl th {
  position: sticky;
  top: -12px;                  /* cancels the body's own top padding */
  z-index: 1;
  padding: 6px 0;
  text-align: left;
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.04em;
  color: var(--on-surface-dim);
  background: var(--surface);
  border-bottom: 1px solid var(--outline);
}

.sen-roster-tbl th:nth-child(3),
.sen-roster-tbl th:nth-child(4) { text-align: right; }

.sen-roster-tbl td {
  padding: 9px 0;
  border-bottom: 1px solid rgba(197, 207, 224, 0.06);
  vertical-align: middle;
}

/* The seniority number and the two dates are read down the column, so they get
   tabular figures — the same rule the rest of the app's number columns use. */
.sen-roster-tbl td.s,
.sen-roster-tbl th:first-child {
  width: 58px;
  font-variant-numeric: tabular-nums;
  font-weight: 500;
}

/* The one column that has to give. A name is the only thing here with no
   bound, so it takes what is left and truncates — everything else is a number
   or a date and must be read whole. */
.sen-roster-tbl td.n {
  padding-right: 8px;
  overflow: hidden;
  white-space: nowrap;
}

/* ⚠️ The chips hold their size (`flex: none`) and the name gives way. `min-width: 0`
   is what lets it: without it a flex item refuses to shrink below its own content,
   so the name would win and push the chips out of the cell again. */
.sen-roster-tbl td.n .me-cell {
  display: flex;
  align-items: center;
  gap: 0;
  min-width: 0;
}

.sen-roster-tbl td.n .nm {
  min-width: 0;
  flex: 0 1 auto;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.sen-roster-tbl td.n .sen-you,
.sen-roster-tbl td.n .sen-proj { flex: none; }

/* ⚠️ 90px, measured against "03 AUG 2021" at 11.5px with a gap in front — the
   two date columns ran into each other at 84px and read as one number. The
   full year is deliberate: a hire date and a retirement date sit side by side
   sixty years apart, where two digits stop being obvious. */
.sen-roster-tbl td.d,
.sen-roster-tbl th:nth-child(3),
.sen-roster-tbl th:nth-child(4) {
  width: 90px;
  padding-left: 6px;
  text-align: right;
  font-size: 11.5px;
  color: var(--on-surface-dim);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* ⚠️ A BAR, NOT A NAME. Everyone on this list is on the roster Delta
   publishes, so this is restraint rather than secrecy — but the width tracks
   the real name's length, which reads as "a real name, deliberately hidden"
   instead of a row of identical placeholders, while giving away nothing but
   roughly how long it is. */
.sen-bar-name {
  display: inline-block;
  height: 11px;
  /* Never wider than the column it sits in — the width below is set per row
     from the real name's length, and on a narrow phone the longest of them
     would otherwise push the table wide again. */
  max-width: 100%;
  border-radius: 6px;
  background: rgba(197, 207, 224, 0.16);
  vertical-align: middle;
}

.sen-roster-tbl tr.me td {
  background: rgba(var(--primary-rgb), 0.12);
}

.sen-roster-tbl tr.me td.s,
.sen-roster-tbl tr.me td.n { color: var(--primary); font-weight: 700; }

.sen-you,
.sen-proj {
  display: inline-block;
  margin-left: 6px;
  padding: 1px 5px;
  border-radius: 4px;
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.03em;
  vertical-align: middle;
}

.sen-you { color: var(--background); background: var(--primary); }

/* ⚠️ Says the rank is where they WOULD land, not a seat they hold. Every place
   a slotted-in row appears carries it — the row, and the count above the
   list — because either one alone reads as an awarded position. */
.sen-proj {
  color: var(--primary);
  background: rgba(var(--primary-rgb), 0.18);
  text-transform: uppercase;
}

/* ── Doors ───────────────────────────────────────────────────────────────────
   Anything that opens a roster. Until this existed the rows and squares were
   deliberately inert, because a control that looks pressable and does nothing
   is worse than a plain figure — so if a door is ever taken away again, take
   this class off with it. */
.sen-door { cursor: pointer; }

.sen-door:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
}

.sen-opt.sen-door:hover { background: rgba(var(--primary-rgb), 0.08); }
.sen-swap.sen-door:hover,
.hero.sen-door:hover { filter: brightness(1.08); }
.sen-grid td.sen-door:hover > span { outline: 1px solid var(--primary); }

/* The roster's four columns at the narrowest phone still in use. The dates are
   the only thing that can give without a number becoming unreadable: the side
   gutter comes in, and the date type steps down a half point rather than the
   name column being starved to nothing. */
@media (max-width: 359px) {
  .sheet-body { padding-left: 12px; padding-right: 12px; }
  .sen-roster-tbl td.d,
  .sen-roster-tbl th:nth-child(3),
  .sen-roster-tbl th:nth-child(4) { width: 84px; font-size: 11px; }
}
