/* ==========================================================================
   Flight statistics platform - design system
   --------------------------------------------------------------------------
   Section 105 sets the one stylistic requirement: a high-quality world of
   white and blue tones, professional, modern, technical, with maps and
   statistics at the centre. Other colours may appear occasionally where they
   help.

   Every colour below is computed rather than chosen by eye.

     Chart marks come from the validated data-visualisation palette and were
     re-validated against this site's own surfaces - a blue-tinted white and a
     blue-tinted navy - with tools/validate_palette.php. The route-frequency
     ramp starts one step darker than the reference default because the
     reference's lightest step measures only 1.96:1 against this site's map
     plane, below the 2:1 ordinal floor.

     Interface ink and accents were checked with tools/check_contrast.php:
     4.5:1 for body text, 3:1 for large text and non-text marks. Three
     light-mode greys were darkened until they cleared it.

   Both tools are in the repository, so any future colour change can be
   re-checked rather than re-guessed.
   ========================================================================== */

:root {
  color-scheme: light;

  /* -- surfaces ------------------------------------------------------- */
  --plane:          #f4f7fa;   /* the page behind the cards            */
  --surface:        #ffffff;   /* cards, panels, charts                */
  --surface-sunken: #f7fafd;   /* table heads, inset panels            */
  --surface-raised: #ffffff;
  --surface-accent: #e9f1fc;   /* selected rows, soft accent fills     */
  --map-plane:      #f4f7fa;

  /* -- ink ------------------------------------------------------------ */
  --ink:            #0b1f33;
  --ink-secondary:  #425c75;
  --ink-muted:      #587490;
  --ink-faint:      #7690a8;
  --ink-on-accent:  #ffffff;

  /* -- lines ---------------------------------------------------------- */
  --line:           #e4ecf4;   /* hairlines, gridlines                 */
  --line-strong:    #c2d2e1;   /* borders that must read as a boundary */
  --ring:           rgba(11, 31, 51, 0.10);

  /* -- accent --------------------------------------------------------- */
  --accent:         #0f5fb8;
  --accent-strong:  #0a4b96;
  --accent-soft:    #e4eefc;
  --accent-ring:    rgba(15, 95, 184, 0.28);

  /* -- status (fixed, never themed; always paired with icon + label) -- */
  --good:           #0a7c0a;
  --warning:        #fab219;
  --serious:        #ec835a;
  --critical:       #b42d2d;
  --good-soft:      #e7f6e7;
  --warning-soft:   #fdf3dd;
  --critical-soft:  #fbe9e9;

  /* -- chart series: validated categorical palette, fixed order ------ */
  --series-1:  #2a78d6;   /* blue    */
  --series-2:  #eb6834;   /* orange  */
  --series-3:  #1baf7a;   /* aqua    */
  --series-4:  #eda100;   /* yellow  */
  --series-5:  #e87ba4;   /* magenta */
  --series-6:  #008300;   /* green   */
  --series-7:  #4a3aa7;   /* violet  */
  --series-8:  #e34948;   /* red     */

  /* Day and night: categorical slots 2 and 1. Warm for daylight, blue for
     night - the pair measures dE 33.6 unsimulated, 24.7 under protanopia. */
  --day:    var(--series-2);
  --night:  var(--series-1);

  /* -- sequential blue ramp: magnitude, light to dark ----------------- */
  --seq-1: #6da7ec;
  --seq-2: #3987e5;
  --seq-3: #256abf;
  --seq-4: #184f95;
  --seq-5: #0d366b;

  /* -- chart chrome --------------------------------------------------- */
  --grid:      #e4ecf4;
  --axis:      #c2d2e1;
  --chart-ink: var(--ink-muted);

  /* -- shape and rhythm ---------------------------------------------- */
  --r-sm: 6px;
  --r-md: 9px;
  --r-lg: 13px;
  --r-pill: 999px;

  --gutter: 16px;
  --shell: 1320px;

  --shadow-sm: 0 1px 2px rgba(11, 31, 51, 0.04);
  --shadow-md: 0 1px 3px rgba(11, 31, 51, 0.06), 0 8px 24px -12px rgba(11, 31, 51, 0.10);
  --shadow-lg: 0 2px 6px rgba(11, 31, 51, 0.07), 0 18px 48px -20px rgba(11, 31, 51, 0.18);

  /* -- type ----------------------------------------------------------- */
  --font: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;

  --fs-2xs:  0.6875rem;
  --fs-xs:   0.75rem;
  --fs-sm:   0.8125rem;
  --fs-base: 0.9375rem;
  --fs-md:   1rem;
  --fs-lg:   1.125rem;
  --fs-xl:   1.3125rem;
  --fs-2xl:  1.625rem;
  --fs-3xl:  2.125rem;
  --fs-hero: 2.75rem;
}

/* Dark mode is a selected set of steps against the dark surface, not an
   inverted light mode. Declared twice: the media query follows the operating
   system, the attribute follows the viewer's own toggle, and the toggle wins
   in both directions. */
@media (prefers-color-scheme: dark) {
  :root:where(:not([data-theme="light"])) {
    color-scheme: dark;

    --plane:          #0b1626;
    --surface:        #0e1e33;
    --surface-sunken: #0c1a2c;
    --surface-raised: #122540;
    --surface-accent: #16304f;
    --map-plane:      #0b1626;

    --ink:            #eef4fb;
    --ink-secondary:  #a8bdd4;
    --ink-muted:      #8aa2ba;
    --ink-faint:      #6d859e;
    --ink-on-accent:  #06121f;

    --line:           #1b2f49;
    --line-strong:    #2a4360;
    --ring:           rgba(255, 255, 255, 0.10);

    --accent:         #6fadf5;
    --accent-strong:  #9cc8fb;
    --accent-soft:    #16304f;
    --accent-ring:    rgba(111, 173, 245, 0.32);

    --good:           #2fbf2f;
    --warning:        #fab219;
    --serious:        #ec835a;
    --critical:       #f07070;
    --good-soft:      #102c15;
    --warning-soft:   #2f2611;
    --critical-soft:  #34191b;

    --series-1: #3987e5;
    --series-2: #d95926;
    --series-3: #199e70;
    --series-4: #c98500;
    --series-5: #d55181;
    --series-6: #008300;
    --series-7: #9085e9;
    --series-8: #e66767;

    --seq-1: #184f95;
    --seq-2: #256abf;
    --seq-3: #3987e5;
    --seq-4: #6da7ec;
    --seq-5: #b7d3f6;

    --grid: #1b2f49;
    --axis: #2a4360;

    --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.30);
    --shadow-md: 0 1px 3px rgba(0, 0, 0, 0.36), 0 8px 24px -12px rgba(0, 0, 0, 0.55);
    --shadow-lg: 0 2px 6px rgba(0, 0, 0, 0.40), 0 18px 48px -20px rgba(0, 0, 0, 0.65);
  }
}

:root[data-theme="dark"] {
  color-scheme: dark;

  --plane:          #0b1626;
  --surface:        #0e1e33;
  --surface-sunken: #0c1a2c;
  --surface-raised: #122540;
  --surface-accent: #16304f;
  --map-plane:      #0b1626;

  --ink:            #eef4fb;
  --ink-secondary:  #a8bdd4;
  --ink-muted:      #8aa2ba;
  --ink-faint:      #6d859e;
  --ink-on-accent:  #06121f;

  --line:           #1b2f49;
  --line-strong:    #2a4360;
  --ring:           rgba(255, 255, 255, 0.10);

  --accent:         #6fadf5;
  --accent-strong:  #9cc8fb;
  --accent-soft:    #16304f;
  --accent-ring:    rgba(111, 173, 245, 0.32);

  --good:           #2fbf2f;
  --warning:        #fab219;
  --serious:        #ec835a;
  --critical:       #f07070;
  --good-soft:      #102c15;
  --warning-soft:   #2f2611;
  --critical-soft:  #34191b;

  --series-1: #3987e5;
  --series-2: #d95926;
  --series-3: #199e70;
  --series-4: #c98500;
  --series-5: #d55181;
  --series-6: #008300;
  --series-7: #9085e9;
  --series-8: #e66767;

  --seq-1: #184f95;
  --seq-2: #256abf;
  --seq-3: #3987e5;
  --seq-4: #6da7ec;
  --seq-5: #b7d3f6;

  --grid: #1b2f49;
  --axis: #2a4360;

  --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.30);
  --shadow-md: 0 1px 3px rgba(0, 0, 0, 0.36), 0 8px 24px -12px rgba(0, 0, 0, 0.55);
  --shadow-lg: 0 2px 6px rgba(0, 0, 0, 0.40), 0 18px 48px -20px rgba(0, 0, 0, 0.65);
}

/* ==========================================================================
   Base
   ========================================================================== */

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

body {
  margin: 0;
  min-height: 100vh;
  background: var(--plane);
  color: var(--ink);
  font-family: var(--font);
  font-size: var(--fs-base);
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  display: flex;
  flex-direction: column;
}

/* `hidden` means hidden, whatever class the element also wears. Without this, any rule
   that sets `display` - `.stack--sm { display: grid }`, `.row { display: flex }` - quietly
   beats the attribute, because the browser's own [hidden] rule is the weakest there is.
   That is how the airport search results stayed on screen after one was picked, and how
   the bulk upload's "replace the track" box showed on every row. */
[hidden] { display: none !important; }

h1, h2, h3, h4 {
  margin: 0;
  font-weight: 640;
  letter-spacing: -0.014em;
  line-height: 1.25;
  color: var(--ink);
}

h1 { font-size: var(--fs-2xl); }
h2 { font-size: var(--fs-xl); }
h3 { font-size: var(--fs-md); }
h4 { font-size: var(--fs-base); }

p { margin: 0 0 0.85em; }
p:last-child { margin-bottom: 0; }

a {
  color: var(--accent);
  text-decoration: none;
  text-underline-offset: 2px;
}
a:hover { text-decoration: underline; }

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: 3px;
}

hr {
  border: 0;
  border-top: 1px solid var(--line);
  margin: 24px 0;
}

code, kbd, samp {
  font-family: var(--mono);
  font-size: 0.92em;
}

/* Numbers that sit in columns must align; standalone display figures use the
   default proportional figures. */
.num, table td, table th, .axis-label {
  font-variant-numeric: tabular-nums;
}

.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

.skip-link {
  position: absolute;
  left: -9999px;
  top: 8px;
  z-index: 200;
  background: var(--surface);
  color: var(--ink);
  padding: 10px 16px;
  border: 1px solid var(--line-strong);
  border-radius: var(--r-md);
}
.skip-link:focus { left: 8px; }

/* ==========================================================================
   Shell, header, navigation
   ========================================================================== */

.shell {
  width: 100%;
  max-width: var(--shell);
  margin: 0 auto;
  padding: 0 var(--gutter);
}

.site-header {
  position: sticky;
  top: 0;
  z-index: 60;
  background: color-mix(in srgb, var(--surface) 88%, transparent);
  backdrop-filter: saturate(150%) blur(10px);
  border-bottom: 1px solid var(--line);
}

@supports not (backdrop-filter: blur(1px)) {
  .site-header { background: var(--surface); }
}

.site-header__inner {
  display: flex;
  align-items: center;
  gap: 20px;
  min-height: 58px;
}

.wordmark {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  color: var(--ink);
  font-weight: 680;
  font-size: var(--fs-md);
  letter-spacing: -0.02em;
  white-space: nowrap;
}
.wordmark:hover { text-decoration: none; }

.wordmark__glyph {
  display: grid;
  place-items: center;
  width: 28px;
  height: 28px;
  border-radius: 8px;
  background: linear-gradient(145deg, var(--accent), var(--accent-strong));
  color: #fff;
  flex: none;
}
.wordmark__glyph svg { width: 17px; height: 17px; display: block; }

.nav {
  display: flex;
  align-items: center;
  gap: 2px;
  margin-left: auto;
  overflow-x: auto;
  scrollbar-width: none;
}
.nav::-webkit-scrollbar { display: none; }

.nav a {
  position: relative;
  padding: 8px 11px;
  border-radius: var(--r-sm);
  color: var(--ink-secondary);
  font-size: var(--fs-sm);
  font-weight: 540;
  white-space: nowrap;
}
.nav a:hover {
  background: var(--surface-sunken);
  color: var(--ink);
  text-decoration: none;
}
.nav a[aria-current="page"] {
  color: var(--accent);
  background: var(--accent-soft);
}

.header-tools {
  display: flex;
  align-items: center;
  gap: 6px;
  flex: none;
}

/* Signing out is a POST, so it needs a form - but a form wrapper would become a flex
   item of its own and push the button out of line with its neighbours. This lets the
   button sit in the header's row as though the form were not there. */
.header-tools__form { display: contents; }

/* The icon carries the meaning on a narrow screen, where the word does not fit beside
   five other controls. It is never the icon alone for a screen reader: the button keeps
   its title, and the label is hidden visually rather than removed. */
@media (max-width: 560px) {
  .header-tools .btn__label {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }
}

/* At phone width the wordmark, the nav and the tools came to seven pixels more than the
   screen, and the whole page scrolled sideways because of it. The gaps give way; the
   wordmark is left at its own size, and the nav goes on scrolling inside itself. */
@media (max-width: 560px) {
  .site-header__inner { gap: 10px; }
}

/* The private area is visually distinct so it is never mistaken for a page a
   visitor can see. */
.site-header--private {
  border-bottom-color: var(--accent);
  box-shadow: inset 0 -2px 0 var(--accent);
}

.main {
  flex: 1 0 auto;
  padding: 28px 0 56px;
}

.site-footer {
  border-top: 1px solid var(--line);
  background: var(--surface);
  padding: 22px 0;
  color: var(--ink-muted);
  font-size: var(--fs-sm);
}
.site-footer__inner {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 20px;
  align-items: center;
  justify-content: space-between;
}

/* ==========================================================================
   Page furniture
   ========================================================================== */

.page-head {
  display: flex;
  flex-wrap: wrap;
  gap: 14px 24px;
  align-items: flex-end;
  justify-content: space-between;
  margin-bottom: 22px;
}

.page-head__text { min-width: 0; }

.eyebrow {
  display: block;
  font-size: var(--fs-xs);
  font-weight: 620;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--ink-muted);
  margin-bottom: 5px;
}

.page-head h1 { font-size: var(--fs-3xl); }

.lede {
  margin: 8px 0 0;
  color: var(--ink-secondary);
  font-size: var(--fs-md);
  max-width: 68ch;
}

.section-head {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 18px;
  align-items: baseline;
  justify-content: space-between;
  margin: 0 0 14px;
}
.section-head h2 { font-size: var(--fs-lg); }
.section-head__note {
  color: var(--ink-muted);
  font-size: var(--fs-sm);
}

/* minmax(0, 1fr) rather than the implicit auto track.
   An auto track's minimum is its items' min-content, so one wide thing inside -
   a seven-column table of nowrap figures - pushes the track past the container
   and every sibling stretches with it. On a phone that is a page that scrolls
   sideways. Pinning the minimum to 0 lets the track match the container and
   leaves the overflow to the scroll container that is meant to handle it. */
.stack     { display: grid; gap: 18px; grid-template-columns: minmax(0, 1fr); }
.stack--lg { display: grid; gap: 30px; grid-template-columns: minmax(0, 1fr); }
.stack--sm { display: grid; gap: 10px; grid-template-columns: minmax(0, 1fr); }

.row {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  align-items: center;
}
.row--between { justify-content: space-between; }
.row--end     { justify-content: flex-end; }

/* ==========================================================================
   Cards
   ========================================================================== */

.card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-sm);
  min-width: 0;
}

.card__head {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 16px;
  align-items: baseline;
  justify-content: space-between;
  padding: 16px 18px;
  border-bottom: 1px solid var(--line);
}
.card__head h2, .card__head h3 { font-size: var(--fs-md); }
.card__head--plain { border-bottom: 0; padding-bottom: 0; }

.card__body { padding: 18px; }
.card__body--flush { padding: 0; }
.card__foot {
  padding: 13px 18px;
  border-top: 1px solid var(--line);
  color: var(--ink-muted);
  font-size: var(--fs-sm);
}

/* min(Npx, 100%) rather than a bare Npx: below the stated minimum the track would
   otherwise stay that wide and overflow a narrow screen. */
.grid {
  display: grid;
  gap: 18px;
}
.grid--2 { grid-template-columns: repeat(auto-fit, minmax(min(320px, 100%), 1fr)); }
.grid--3 { grid-template-columns: repeat(auto-fit, minmax(min(260px, 100%), 1fr)); }
.grid--4 { grid-template-columns: repeat(auto-fit, minmax(min(210px, 100%), 1fr)); }
.grid > * { min-width: 0; }

.span-2 { grid-column: span 2; }
@media (max-width: 900px) { .span-2 { grid-column: auto; } }

/* Main content with a fixed-width companion panel - the track selector beside the
   map, the review editor beside its queue. Collapses to one column on a narrow
   screen, where the panel belongs under the thing it controls. */
.with-aside {
  display: grid;
  gap: 18px;
  grid-template-columns: minmax(0, 1fr) 328px;
  align-items: start;
}
.with-aside--narrow { grid-template-columns: minmax(0, 1fr) 272px; }
@media (max-width: 1080px) {
  .with-aside, .with-aside--narrow { grid-template-columns: minmax(0, 1fr); }
}

.scroll-panel {
  max-height: 520px;
  overflow-y: auto;
  border-top: 1px solid var(--line);
}

/* A checkbox sized for a list row rather than for a form.
   Deliberately `input.check`: the same name is also worn by the label that wraps a
   checkbox and its text, and a bare `.check` sized that label to 16px, which pushed
   its wording out from under it. */
input.check {
  width: 16px;
  height: 16px;
  min-height: 0;
  accent-color: var(--accent);
  cursor: pointer;
}

/* ==========================================================================
   Statistics tiles
   ========================================================================== */

.tiles {
  display: grid;
  gap: 1px;
  grid-template-columns: repeat(auto-fit, minmax(min(176px, 100%), 1fr));
  background: var(--line);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  overflow: hidden;
}

.tile {
  background: var(--surface);
  padding: 15px 17px 17px;
  display: flex;
  flex-direction: column;
  gap: 3px;
  min-width: 0;
}

.tile__label {
  font-size: var(--fs-xs);
  font-weight: 580;
  letter-spacing: 0.055em;
  text-transform: uppercase;
  color: var(--ink-muted);
}

.tile__value {
  font-size: var(--fs-2xl);
  font-weight: 640;
  letter-spacing: -0.026em;
  line-height: 1.12;
  color: var(--ink);
}
.tile__value--sm { font-size: var(--fs-xl); }

.tile__meta {
  font-size: var(--fs-sm);
  color: var(--ink-muted);
}

.tile--hero { grid-column: span 2; }
.tile--hero .tile__value { font-size: var(--fs-hero); }
@media (max-width: 640px) {
  .tile--hero { grid-column: auto; }
  .tile--hero .tile__value { font-size: var(--fs-3xl); }
}

.unit {
  font-size: 0.56em;
  font-weight: 560;
  color: var(--ink-muted);
  letter-spacing: 0;
  margin-left: 2px;
}

/* A value that was never measured. Section 66: not available is not zero. */
.na {
  color: var(--ink-faint);
  font-variant-numeric: normal;
}

/* ==========================================================================
   Definition lists (flight and airport detail)
   ========================================================================== */

.facts {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(190px, 100%), 1fr));
  gap: 1px;
  margin: 0;
  background: var(--line);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  overflow: hidden;
}

.fact {
  background: var(--surface);
  padding: 11px 14px;
  min-width: 0;
}
.fact dt {
  font-size: var(--fs-xs);
  font-weight: 580;
  letter-spacing: 0.045em;
  text-transform: uppercase;
  color: var(--ink-muted);
  margin-bottom: 2px;
}
.fact dd {
  margin: 0;
  font-size: var(--fs-md);
  font-weight: 560;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
}
.fact dd small {
  display: block;
  font-size: var(--fs-xs);
  font-weight: 440;
  color: var(--ink-muted);
  font-variant-numeric: normal;
}

/* ==========================================================================
   Tables
   ========================================================================== */

.table-wrap {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  /* So the wrapper cannot be forced wider than its parent by the table inside it;
     the table then scrolls within it, which is the whole point of the wrapper. */
  min-width: 0;
  max-width: 100%;
}

table.data {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--fs-sm);
}

table.data th,
table.data td {
  padding: 9px 13px;
  text-align: left;
  border-bottom: 1px solid var(--line);
  white-space: nowrap;
}

table.data thead th {
  position: sticky;
  top: 0;
  z-index: 2;
  background: var(--surface-sunken);
  color: var(--ink-muted);
  font-size: var(--fs-xs);
  font-weight: 620;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  border-bottom: 1px solid var(--line-strong);
}

table.data tbody tr:hover { background: var(--surface-sunken); }
table.data tbody tr:last-child td { border-bottom: 0; }

table.data td.right, table.data th.right { text-align: right; }
table.data td.wrap { white-space: normal; min-width: 180px; }

table.data a { font-weight: 560; }

th.sortable > a {
  color: inherit;
  display: inline-flex;
  align-items: center;
  gap: 4px;
}
th.sortable > a:hover { color: var(--accent); text-decoration: none; }
th[aria-sort] > a { color: var(--accent); }

/* Headings that sort the table in the browser (assets/js/lib/sortable.js).
   The padding moves from the cell onto the button so the whole heading is the hit
   target rather than just the few pixels under the words. */
table.data th.th--sortable { padding: 0; }

.th__sort {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  width: 100%;
  padding: 9px 13px;
  background: none;
  border: 0;
  color: inherit;
  font: inherit;
  letter-spacing: inherit;
  text-transform: inherit;
  text-align: inherit;
  cursor: pointer;
}
table.data th.right .th__sort { justify-content: flex-end; }
.th__sort:hover { color: var(--accent); }
.th__sort:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

/* A caret that only appears once the column is doing something, so a table at rest
   stays as quiet as it was before. */
.th__arrow {
  width: 0;
  height: 0;
  flex: none;
  border-left: 3.5px solid transparent;
  border-right: 3.5px solid transparent;
  border-top: 4px solid currentColor;
  opacity: 0;
  transition: opacity 0.12s ease;
}
.th__sort:hover .th__arrow { opacity: 0.4; }
th[aria-sort] .th__sort { color: var(--accent); }
th[aria-sort] .th__arrow { opacity: 1; }
th[aria-sort="ascending"] .th__arrow { transform: rotate(180deg); }

@media (prefers-reduced-motion: reduce) {
  .th__arrow { transition: none; }
}

.table-note {
  padding: 11px 14px;
  color: var(--ink-muted);
  font-size: var(--fs-sm);
}

/* ==========================================================================
   Controls
   ========================================================================== */

.btn {
  --btn-bg: var(--surface);
  --btn-ink: var(--ink);
  --btn-line: var(--line-strong);

  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  padding: 8px 15px;
  min-height: 36px;
  border: 1px solid var(--btn-line);
  border-radius: var(--r-md);
  background: var(--btn-bg);
  color: var(--btn-ink);
  font: inherit;
  font-size: var(--fs-sm);
  font-weight: 580;
  cursor: pointer;
  white-space: nowrap;
  transition: background-color 0.13s ease, border-color 0.13s ease, color 0.13s ease;
}
.btn:hover { background: var(--surface-sunken); text-decoration: none; }
.btn:disabled, .btn[aria-disabled="true"] {
  opacity: 0.5;
  cursor: not-allowed;
}

.btn--primary {
  --btn-bg: var(--accent);
  --btn-ink: var(--ink-on-accent);
  --btn-line: var(--accent);
}
.btn--primary:hover {
  --btn-bg: var(--accent-strong);
  --btn-line: var(--accent-strong);
}

.btn--soft {
  --btn-bg: var(--accent-soft);
  --btn-ink: var(--accent);
  --btn-line: transparent;
}

.btn--danger {
  --btn-bg: var(--critical-soft);
  --btn-ink: var(--critical);
  --btn-line: color-mix(in srgb, var(--critical) 32%, transparent);
}
.btn--danger:hover {
  --btn-bg: var(--critical);
  --btn-ink: #fff;
  --btn-line: var(--critical);
}

.btn--ghost {
  --btn-bg: transparent;
  --btn-line: transparent;
  --btn-ink: var(--ink-secondary);
}
.btn--ghost:hover { --btn-bg: var(--surface-sunken); --btn-ink: var(--ink); }

.btn--sm { min-height: 30px; padding: 5px 11px; font-size: var(--fs-xs); }
.btn--lg { min-height: 44px; padding: 11px 22px; font-size: var(--fs-md); }
.btn--icon { padding: 7px; width: 36px; min-height: 36px; }
.btn--block { width: 100%; }

.btn__icon { width: 15px; height: 15px; flex: none; display: block; }

/* -- form fields ------------------------------------------------------ */

.field { display: grid; gap: 5px; min-width: 0; }

.field > label,
.field-label {
  font-size: var(--fs-xs);
  font-weight: 600;
  letter-spacing: 0.045em;
  text-transform: uppercase;
  color: var(--ink-muted);
}

.field__help {
  font-size: var(--fs-xs);
  color: var(--ink-muted);
}
.field__error {
  font-size: var(--fs-xs);
  color: var(--critical);
  font-weight: 560;
}

input[type="text"], input[type="password"], input[type="number"],
input[type="search"], input[type="date"], input[type="time"],
input[type="email"], select, textarea {
  width: 100%;
  padding: 8px 11px;
  min-height: 36px;
  border: 1px solid var(--line-strong);
  border-radius: var(--r-md);
  background: var(--surface);
  color: var(--ink);
  font: inherit;
  font-size: var(--fs-base);
}

input:focus, select:focus, textarea:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-ring);
}

input::placeholder, textarea::placeholder { color: var(--ink-faint); }

input[aria-invalid="true"], select[aria-invalid="true"] {
  border-color: var(--critical);
}

textarea { min-height: 84px; resize: vertical; line-height: 1.5; }

/* A filed route is read token by token, so it is set in a face where every character is
   the same width and 0 cannot be mistaken for O. */
textarea.mono, input.mono {
  font-family: var(--font-mono, ui-monospace, "SFMono-Regular", "Cascadia Mono",
               "Liberation Mono", Consolas, monospace);
  font-size: var(--fs-sm);
  letter-spacing: 0.01em;
}

/* A time or code field: monospaced, tabular, sized to its content. */
input.tight {
  font-family: var(--mono);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.04em;
  text-align: center;
}
input.tight--time { max-width: 108px; }
input.tight--code { max-width: 92px; text-transform: uppercase; }

/* A crew field holds a code or a name, so it is wider than a code box and is never
   upper-cased by the stylesheet - that would make "Künzel" look like a code. */
input.tight--crew { max-width: 190px; }

/* The seat's roster abbreviation, in front of its name on the field label: CMD, F/O,
   HCC. It is what the crew list actually calls the position, so it leads. */
.crew-code {
  display: inline-block;
  margin-right: 5px;
  padding: 1px 5px;
  border-radius: var(--r-sm, 4px);
  background: var(--accent-soft);
  color: var(--accent-strong);
  font-weight: 700;
  letter-spacing: 0.02em;
}

/* ==========================================================================
   Click spots
   --------------------------------------------------------------------------
   Sections 3, 5, 6 and 61 ask for aircraft type, registration, flight purpose
   and passenger count to be chosen by clicking rather than typed, with as few
   clicks as possible. These are radio inputs underneath, so keyboard
   navigation and screen readers get real semantics; only the visual is a chip.
   ========================================================================== */

.spots {
  display: flex;
  flex-wrap: wrap;
  gap: 7px;
}

.spots--grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(54px, 1fr));
  gap: 6px;
}

.spot { position: relative; display: flex; min-width: 0; }

.spot input {
  position: absolute;
  inset: 0;
  width: 100%; height: 100%;
  margin: 0;
  opacity: 0;
  cursor: pointer;
}

.spot span {
  display: flex;
  flex: 1;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 9px 15px;
  min-height: 40px;
  border: 1px solid var(--line-strong);
  border-radius: var(--r-md);
  background: var(--surface);
  color: var(--ink-secondary);
  font-size: var(--fs-sm);
  font-weight: 580;
  text-align: center;
  transition: background-color 0.12s ease, border-color 0.12s ease, color 0.12s ease;
  user-select: none;
}

.spot input:hover + span {
  border-color: var(--accent);
  color: var(--ink);
}

.spot input:focus-visible + span {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.spot input:checked + span {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--ink-on-accent);
}

.spot input:disabled + span {
  opacity: 0.38;
  cursor: not-allowed;
  background: var(--surface-sunken);
}
.spot input:disabled { cursor: not-allowed; }

.spot--num span { padding: 9px 6px; font-variant-numeric: tabular-nums; }
.spot--wide span { padding: 11px 18px; }

.spot small {
  display: block;
  font-size: var(--fs-2xs);
  font-weight: 460;
  opacity: 0.78;
}

/* ==========================================================================
   Segmented switch (UTC / Local, 2D / 3D)
   ========================================================================== */

.segmented {
  display: inline-flex;
  padding: 2px;
  gap: 2px;
  background: var(--surface-sunken);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
}

.segmented button,
.segmented a {
  padding: 5px 12px;
  border: 0;
  border-radius: calc(var(--r-md) - 3px);
  background: transparent;
  color: var(--ink-secondary);
  font: inherit;
  font-size: var(--fs-xs);
  font-weight: 600;
  cursor: pointer;
  white-space: nowrap;
}
.segmented button:hover, .segmented a:hover {
  color: var(--ink);
  text-decoration: none;
}
.segmented [aria-pressed="true"],
.segmented [aria-current="true"],
.segmented [aria-current="page"] {
  background: var(--surface);
  color: var(--accent);
  box-shadow: var(--shadow-sm);
}

/* ==========================================================================
   Filter bar - one row above the content (section 63)
   ========================================================================== */

.filters {
  display: flex;
  flex-wrap: wrap;
  gap: 12px 14px;
  /* Top-aligned, so every label sits on one line and every control below it lines up.
     Bottom alignment made the one field carrying help text drag its input upwards. */
  align-items: start;
  padding: 14px 16px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-sm);
}

/* Basis below the width they end up at, because flex wraps on the basis and then
   grows what is left on the line: a tighter basis fits the checkbox and the button
   onto the second row instead of leaving a third row holding one button. */
.filters .field { flex: 1 1 158px; }
.filters .field--wide { flex: 1 1 260px; }
.filters .field--range { flex: 1 1 170px; }

/* "from - to" as one control: two boxes that cannot be separated by a line break. */
.range-pair { display: flex; align-items: center; gap: 7px; }
.range-pair input { width: 100%; min-width: 0; }
.range-pair__dash { color: var(--ink-faint, var(--ink-muted)); }

/* Checkboxes and buttons carry no label of their own, so they are pushed down by the
   height of one label plus its gap to sit level with the inputs beside them. */
.filters__checks {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  /* Never squeezed: a two-word label broken over three lines is what this replaced. */
  flex: 0 0 auto;
  white-space: nowrap;
  gap: 4px 18px;
  /* one label (17px) + its gap (5px), plus half the difference between an input and a
     checkbox row, so the tick sits on the middle of the boxes beside it */
  padding-top: 36px;
}
.filters__actions {
  display: flex;
  gap: 8px;
  margin-left: 4px;   /* in flow, not pushed right: a lone button on its own row is
                         75px of empty bar for nothing */
  padding-top: 25px;  /* one label plus its gap: the button tops meet the input tops */
}

@media (max-width: 640px) {
  .filters__checks { padding-top: 2px; }
  .filters__actions { margin-left: 0; padding-top: 4px; }
}

.chips { display: flex; flex-wrap: wrap; gap: 6px; }

.chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 3px 5px 3px 10px;
  border-radius: var(--r-pill);
  background: var(--accent-soft);
  color: var(--accent);
  font-size: var(--fs-xs);
  font-weight: 600;
}
.chip a {
  display: grid;
  place-items: center;
  width: 17px; height: 17px;
  border-radius: 50%;
  color: inherit;
  opacity: 0.68;
}
.chip a:hover { opacity: 1; background: rgba(0, 0, 0, 0.08); text-decoration: none; }

/* ==========================================================================
   Badges
   ========================================================================== */

.badge {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 2px 8px;
  border-radius: var(--r-pill);
  background: var(--surface-sunken);
  border: 1px solid var(--line);
  color: var(--ink-secondary);
  font-size: var(--fs-xs);
  font-weight: 600;
  white-space: nowrap;
  line-height: 1.6;
}

.badge--accent   { background: var(--accent-soft);   border-color: transparent; color: var(--accent); }
.badge--good     { background: var(--good-soft);     border-color: transparent; color: var(--good); }
.badge--warning  { background: var(--warning-soft);  border-color: transparent; color: #7a5200; }
.badge--critical { background: var(--critical-soft); border-color: transparent; color: var(--critical); }

:root[data-theme="dark"] .badge--warning { color: var(--warning); }
@media (prefers-color-scheme: dark) {
  :root:where(:not([data-theme="light"])) .badge--warning { color: var(--warning); }
}

.badge__dot {
  width: 7px; height: 7px;
  border-radius: 50%;
  background: currentColor;
  flex: none;
}

/* An estimated rather than measured figure (sections 31, 90). */
.badge--estimated {
  background: transparent;
  border-style: dashed;
  color: var(--ink-muted);
}

/* ==========================================================================
   Notices
   ========================================================================== */

.notice {
  display: flex;
  gap: 11px;
  padding: 13px 15px;
  border: 1px solid var(--line);
  border-left: 3px solid var(--ink-faint);
  border-radius: var(--r-md);
  background: var(--surface);
  font-size: var(--fs-sm);
  color: var(--ink-secondary);
}
.notice__icon { flex: none; width: 17px; height: 17px; margin-top: 1px; }
.notice__body { min-width: 0; }
.notice strong { color: var(--ink); font-weight: 620; }

.notice--info     { border-left-color: var(--accent);   background: var(--accent-soft); }
.notice--good     { border-left-color: var(--good);     background: var(--good-soft); }
.notice--warning  { border-left-color: var(--warning);  background: var(--warning-soft); }
.notice--critical { border-left-color: var(--critical); background: var(--critical-soft); }

.empty {
  display: grid;
  gap: 10px;
  justify-items: center;
  text-align: center;
  padding: 52px 24px;
  color: var(--ink-muted);
}
.empty svg { width: 38px; height: 38px; color: var(--ink-faint); }
.empty h3 { color: var(--ink); }
.empty p { max-width: 46ch; }

/* ==========================================================================
   Maps
   ========================================================================== */

.map {
  position: relative;
  width: 100%;
  height: clamp(340px, 56vh, 660px);
  background: var(--map-plane);
  border-radius: var(--r-lg);
  overflow: hidden;
}
.map--tall  { height: clamp(420px, 72vh, 860px); }
.map--short { height: clamp(240px, 36vh, 400px); }
.map--flush { border-radius: 0; }

.map canvas { display: block; }

/* The globe sits over the flat map in the same box. While it is showing, Leaflet's
   panes and controls are taken out of the layout: leaving them would keep the flat
   map rendering underneath and float its zoom buttons over the globe. */
.map__globe {
  position: absolute;
  inset: 0;
  z-index: 900;
}
.map__globe--failed {
  display: grid;
  place-items: center;
  background: var(--surface);
}

.map--globe > .leaflet-pane,
.map--globe > .leaflet-control-container {
  display: none;
}

/* Zoom and full screen (lib/mapcontrols.js). One stack in the top left for both the flat
   map and the globe, above the globe's canvas, styled like the bar Leaflet used to draw. */
.map-ctl {
  position: absolute;
  top: 10px;
  left: 10px;
  z-index: 1000;
  display: grid;
  gap: 8px;
}
.map-ctl__group {
  display: grid;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-sm);
  overflow: hidden;
}
.map-ctl__group button {
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  padding: 0;
  border: 0;
  background: var(--surface);
  color: var(--ink);
  font: 600 18px/1 var(--font);
  cursor: pointer;
}
.map-ctl__group button + button { border-top: 1px solid var(--line); }
.map-ctl__group button:hover { background: var(--surface-sunken); }
.map-ctl__group button:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

/* In full screen the box is the screen. The browser sizes a :fullscreen element to the
   viewport by itself; the rules here only drop the rounded corners, give it the page
   background, and let a map inside a wrapper grow to fill what the wrapper now is. */
.map:fullscreen,
.map.is-pseudo-fullscreen { border-radius: 0; height: 100%; }
.map-shell:fullscreen,
.map-shell.is-pseudo-fullscreen { background: var(--plane); padding: 12px; }
.map-shell.is-fullscreen .map { height: 100%; }

.is-pseudo-fullscreen {
  position: fixed !important;
  inset: 0 !important;
  z-index: 10000 !important;
  width: 100vw !important;
  height: 100vh !important;
  height: 100dvh !important;
  max-width: none !important;
  margin: 0 !important;
  border-radius: 0 !important;
}
.has-pseudo-fullscreen,
.has-pseudo-fullscreen body { overflow: hidden; }

/* The departure / arrival / flight number / registration fields beside the track map. */
.track-filters {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 8px 10px;
}
.track-filters .field { margin: 0; }
.track-filters input { text-transform: uppercase; }
.track-filters input::placeholder { text-transform: none; }

/* --- The map of several people (friends, teams) ------------------------------ */

.people-map { padding-top: 0; padding-bottom: 0; overflow: hidden; }
.people-map__bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px 14px;
  padding: 12px 18px !important;
  border-bottom: 1px solid var(--line);
}
.people-map__status { margin-left: auto; font-size: var(--fs-xs); color: var(--ink-muted); }
.people-map__body {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 270px;
  padding: 0 !important;
}
.people-map__canvas { border-radius: 0; }
.people-map__side {
  border-left: 1px solid var(--line);
  padding: 14px 16px;
  max-height: clamp(420px, 72vh, 860px);
  overflow: auto;
}
.people-map__title {
  font-size: var(--fs-2xs);
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--ink-muted);
  margin: 0 0 8px;
}
.people-map.is-fullscreen .people-map__body { height: calc(100% - 58px); }
.people-map.is-fullscreen .people-map__canvas { height: 100%; }
.people-map.is-fullscreen .people-map__side { max-height: none; }
.people-map:fullscreen { background: var(--surface); padding: 0; }
@media (max-width: 900px) {
  .people-map__body { grid-template-columns: minmax(0, 1fr); }
  .people-map__side { border-left: 0; border-top: 1px solid var(--line); max-height: none; }
}

.toggle-group { display: inline-flex; gap: 6px; }
.toggle-group [aria-pressed="true"] {
  background: var(--surface-accent);
  border-color: var(--accent);
  color: var(--accent);
}

.people-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 2px; }
.people-list__label {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 7px 6px;
  border-radius: var(--r-sm, 6px);
  cursor: pointer;
}
.people-list__label:hover { background: var(--surface-sunken); }
.people-list__text { display: grid; min-width: 0; }
.people-list__text small {
  color: var(--ink-muted);
  font-size: var(--fs-2xs);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.people-list__item.is-empty { opacity: 0.6; }
.people-list__item.is-empty .people-list__label { cursor: default; }

/* A suggestion that is what the field already holds: a crew name, a time read from the
   track. Marked, so it is plain which of the offered values is the one in use. */
.spots .btn[aria-pressed="true"],
.time-suggest[aria-pressed="true"] {
  background: var(--surface-accent);
  border-color: var(--accent);
  color: var(--accent);
}
.time-suggest { margin-top: 6px; justify-self: start; }

/* --- Duties ------------------------------------------------------------------ */

.duty-times { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 14px 22px; }
.duty-times__group { border: 0; padding: 0; margin: 0; }
.duty-times__group .field-label { margin-bottom: 6px; display: block; }
.duty-times input[type="date"],
.duty-times input[type="time"] { width: auto; min-width: 0; }
tr.is-inside td { background: color-mix(in srgb, var(--surface-accent) 55%, transparent); }

/* --- The team podium and comparison bars ------------------------------------- */

.podium {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  align-items: end;
  gap: 10px;
  max-width: 560px;
  margin: 8px auto 0;
}
.podium__place { display: grid; justify-items: center; gap: 6px; text-align: center; min-width: 0; }
.podium__name { font-weight: 650; overflow-wrap: anywhere; }
.podium__value { font-size: var(--fs-sm); color: var(--ink-secondary); font-variant-numeric: tabular-nums; }
.podium__block {
  width: 100%;
  display: grid;
  place-items: center;
  border-radius: var(--r-md) var(--r-md) 0 0;
  color: #fff;
  font-size: 1.6rem;
  font-weight: 750;
  box-shadow: inset 0 -3px 0 rgba(0, 0, 0, 0.12);
}
.podium__place--1 .podium__block { height: 132px; background: #c9a227; }
.podium__place--2 .podium__block { height: 100px; background: #9aa7b4; }
.podium__place--3 .podium__block { height: 76px;  background: #b87333; }
.podium__place.is-vacant { visibility: hidden; }
.podium__swatch { width: 10px; height: 10px; border-radius: 3px; display: inline-block; margin-right: 5px; }

.team-bars { list-style: none; margin: 0; padding: 0; display: grid; gap: 10px; }
.team-bars__row {
  display: grid;
  grid-template-columns: minmax(0, 7.5rem) minmax(0, 1fr) auto;
  align-items: center;
  gap: 10px;
  font-size: var(--fs-sm);
}
.team-bars__name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--ink-secondary); }
.team-bars__track { height: 10px; border-radius: 5px; background: var(--surface-sunken); overflow: hidden; }
.team-bars__fill { display: block; height: 100%; border-radius: 5px; min-width: 2px; }
.team-bars__value { font-variant-numeric: tabular-nums; font-weight: 600; white-space: nowrap; }

.map__overlay {
  position: absolute;
  z-index: 500;
  background: color-mix(in srgb, var(--surface) 93%, transparent);
  backdrop-filter: blur(8px);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-md);
  font-size: var(--fs-xs);
}
.map__overlay--tl { top: 12px; left: 12px; }
.map__overlay--tr { top: 12px; right: 12px; }
.map__overlay--bl { bottom: 12px; left: 12px; }
.map__overlay--br { bottom: 12px; right: 12px; }

.map__legend { padding: 11px 13px; max-width: 230px; }
.map__legend h4 {
  font-size: var(--fs-2xs);
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--ink-muted);
  margin-bottom: 7px;
}

.legend-scale { display: grid; gap: 4px; }
.legend-row {
  display: flex;
  align-items: center;
  gap: 8px;
  color: var(--ink-secondary);
  font-variant-numeric: tabular-nums;
}
.legend-swatch {
  width: 20px;
  height: 3px;
  border-radius: 2px;
  flex: none;
}
.legend-swatch--block { height: 11px; width: 11px; border-radius: 3px; }

/* Leaflet needs its own attribution styling to stay in the design. */
.leaflet-container {
  background: var(--map-plane) !important;
  font-family: var(--font) !important;
  font-size: var(--fs-xs) !important;
}
.leaflet-control-attribution {
  background: color-mix(in srgb, var(--surface) 86%, transparent) !important;
  color: var(--ink-faint) !important;
  font-size: var(--fs-2xs) !important;
  padding: 1px 6px !important;
}
.leaflet-control-attribution a { color: var(--ink-muted) !important; }
.leaflet-bar {
  border: 1px solid var(--line) !important;
  box-shadow: var(--shadow-sm) !important;
}
.leaflet-bar a {
  background: var(--surface) !important;
  color: var(--ink) !important;
  border-bottom-color: var(--line) !important;
}
.leaflet-bar a:hover { background: var(--surface-sunken) !important; }
.leaflet-popup-content-wrapper, .leaflet-popup-tip {
  background: var(--surface) !important;
  color: var(--ink) !important;
  border-radius: var(--r-md) !important;
  box-shadow: var(--shadow-lg) !important;
}
.leaflet-popup-content { margin: 11px 13px !important; font-size: var(--fs-sm) !important; }

/* The optional OpenStreetMap raster layer is desaturated so the drawn routes stay
   the most prominent thing on the map; in dark mode it is inverted to match. */
.map__tiles--osm { filter: saturate(0.45) brightness(1.04); }

:root[data-theme="dark"] .map__tiles--osm {
  filter: invert(1) hue-rotate(180deg) saturate(0.5) brightness(0.86);
}
@media (prefers-color-scheme: dark) {
  :root:where(:not([data-theme="light"])) .map__tiles--osm {
    filter: invert(1) hue-rotate(180deg) saturate(0.5) brightness(0.86);
  }
}

/* ==========================================================================
   Charts
   --------------------------------------------------------------------------
   Charts are inline SVG built by app JS. These rules cover the shared chrome:
   recessive gridlines, thin marks, a crosshair and a tooltip.
   ========================================================================== */

.chart {
  position: relative;
  width: 100%;
}
.chart svg { display: block; width: 100%; height: auto; overflow: visible; }

.chart .grid-line {
  stroke: var(--grid);
  stroke-width: 1;
  shape-rendering: crispEdges;
}
.chart .axis-line {
  stroke: var(--axis);
  stroke-width: 1;
  shape-rendering: crispEdges;
}
.chart .axis-label {
  fill: var(--ink-muted);
  font-size: 11px;
  font-family: var(--font);
}
.chart .axis-title {
  fill: var(--ink-muted);
  font-size: 10px;
  font-weight: 620;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  /* The 100 % reference line runs straight through this title whenever the scale tops
     out near 100. Drawing the surface colour behind the glyphs knocks the line out
     from under them - the same trick the data points already use. */
  paint-order: stroke;
  stroke: var(--surface);
  stroke-width: 4px;
  stroke-linejoin: round;
}
.chart .series-line {
  fill: none;
  stroke-width: 2;
  stroke-linejoin: round;
  stroke-linecap: round;
}
.chart .series-area { stroke: none; }
.chart .series-point { stroke: var(--surface); stroke-width: 2; }
.chart .crosshair {
  stroke: var(--ink-faint);
  stroke-width: 1;
  stroke-dasharray: 3 3;
  pointer-events: none;
}
.chart .bar { stroke: none; }
.chart .bar-label {
  fill: var(--ink-secondary);
  font-size: 11px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}
.chart .value-label {
  fill: var(--ink);
  font-size: 11px;
  font-weight: 620;
  font-variant-numeric: tabular-nums;
}

.chart__tooltip {
  position: absolute;
  z-index: 20;
  pointer-events: none;
  min-width: 128px;
  padding: 8px 10px;
  background: var(--surface);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-lg);
  font-size: var(--fs-xs);
  opacity: 0;
  transition: opacity 0.1s ease;
}
.chart__tooltip[data-visible="true"] { opacity: 1; }
.chart__tooltip dt {
  color: var(--ink-muted);
  font-size: var(--fs-2xs);
  letter-spacing: 0.05em;
  text-transform: uppercase;
}
.chart__tooltip dd {
  margin: 0 0 5px;
  font-weight: 620;
  font-variant-numeric: tabular-nums;
}
.chart__tooltip dd:last-child { margin-bottom: 0; }

.legend {
  display: flex;
  flex-wrap: wrap;
  gap: 5px 16px;
  margin-top: 12px;
  font-size: var(--fs-xs);
  color: var(--ink-secondary);
}
.legend__item { display: inline-flex; align-items: center; gap: 6px; }
.legend__swatch {
  width: 10px; height: 10px;
  border-radius: 2px;
  flex: none;
}

/* A horizontal magnitude bar used inline in tables (flight levels, airports). */
.meter {
  display: flex;
  align-items: center;
  gap: 9px;
  min-width: 132px;
}
.meter__track {
  position: relative;
  flex: 1;
  height: 7px;
  border-radius: 4px;
  background: var(--surface-accent);
  overflow: hidden;
}
.meter__fill {
  position: absolute;
  inset: 0 auto 0 0;
  border-radius: 4px;
  background: var(--seq-3);
}
.meter__value {
  font-size: var(--fs-xs);
  font-weight: 620;
  font-variant-numeric: tabular-nums;
  color: var(--ink-secondary);
  min-width: 46px;
  text-align: right;
}

/* Two-part proportion bar (day/night, eastbound/westbound). A 2px surface gap
   between the segments keeps them from reading as one mark. */
.split-bar {
  display: flex;
  height: 9px;
  border-radius: 5px;
  overflow: hidden;
  background: var(--surface-sunken);
  gap: 2px;
}
.split-bar__part { height: 100%; }

/* ==========================================================================
   Flight route glyph
   ========================================================================== */

.route {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.route__arrow { color: var(--ink-faint); flex: none; }
.route code {
  font-family: var(--font);
  letter-spacing: 0.01em;
}

/* ==========================================================================
   Tabs
   ========================================================================== */

.tabs {
  display: flex;
  gap: 3px;
  border-bottom: 1px solid var(--line);
  overflow-x: auto;
  scrollbar-width: none;
}
.tabs::-webkit-scrollbar { display: none; }

.tabs a {
  padding: 9px 13px;
  border-bottom: 2px solid transparent;
  color: var(--ink-secondary);
  font-size: var(--fs-sm);
  font-weight: 580;
  white-space: nowrap;
  margin-bottom: -1px;
}
.tabs a:hover { color: var(--ink); text-decoration: none; }
.tabs a[aria-current="page"] {
  color: var(--accent);
  border-bottom-color: var(--accent);
}

/* ==========================================================================
   Import queue (section 92)
   ========================================================================== */

.queue { display: grid; gap: 8px; }

.queue__item {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 11px 14px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  font-size: var(--fs-sm);
}
.queue__item[data-current="true"] {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-ring);
}
.queue__name {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-weight: 560;
}
.queue__route { color: var(--ink-muted); font-variant-numeric: tabular-nums; }

/* ==========================================================================
   Login
   ========================================================================== */

.auth {
  display: grid;
  place-items: center;
  min-height: 78vh;
  padding: 24px var(--gutter);
}
.auth__card {
  width: 100%;
  max-width: 394px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-md);
  padding: 30px;
}
.auth__card h1 { font-size: var(--fs-xl); margin-bottom: 6px; }
.auth__card .lede { font-size: var(--fs-sm); margin-bottom: 22px; }

/* ==========================================================================
   Pagination
   ========================================================================== */

.pager {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
  justify-content: space-between;
  padding-top: 4px;
}
.pager__info { color: var(--ink-muted); font-size: var(--fs-sm); }
.pager__links { display: flex; gap: 5px; }

/* ==========================================================================
   Provenance markers (sections 65 and 91)
   ========================================================================== */

.origin {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: var(--fs-2xs);
  font-weight: 620;
  letter-spacing: 0.055em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
.origin::before {
  content: "";
  width: 5px; height: 5px;
  border-radius: 50%;
  background: currentColor;
}
.origin--csv        { color: var(--seq-3); }
.origin--detected   { color: var(--series-3); }
.origin--calculated { color: var(--ink-muted); }
.origin--manual     { color: var(--series-7); }
.origin--override   { color: var(--series-2); }

/* ==========================================================================
   Print
   ========================================================================== */

@media print {
  .site-header, .site-footer, .filters, .btn, .segmented { display: none !important; }
  body { background: #fff; }
  .card { break-inside: avoid; box-shadow: none; }
}

/* ==========================================================================
   Multi-user extension
   --------------------------------------------------------------------------
   Everything below is new with the extension and reuses the tokens above
   rather than introducing colours of its own - extension section 82 asks for
   the existing design to carry over, not for a second design system.
   ========================================================================== */

/* --- Platform front page ------------------------------------------------ */

.hero {
  padding: 34px 0 26px;
  border-bottom: 1px solid var(--line);
  margin-bottom: 26px;
}
.hero h1 { font-size: var(--fs-hero); line-height: 1.1; margin: 0 0 10px; }
.hero__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-top: 20px;
}

/* --- Profiles ----------------------------------------------------------- */

.profile-chip {
  display: inline-flex;
  align-items: baseline;
  gap: 7px;
  padding: 5px 11px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--surface-sunken);
  font-size: var(--fs-sm);
  text-decoration: none;
  color: inherit;
  /* Never pushes the navigation off a narrow header. */
  min-width: 0;
}
.profile-chip:hover { border-color: var(--line-strong); }
.profile-chip__name { font-weight: 600; }
.profile-chip__handle { color: var(--ink-muted); }

.profile-list { list-style: none; margin: 0; padding: 0; }
.profile-list__item {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 11px 0;
  border-bottom: 1px solid var(--line);
}
.profile-list__item:last-child { border-bottom: 0; }
.profile-list__name { font-weight: 600; margin-right: 7px; }
.profile-list__handle { color: var(--ink-muted); font-variant-numeric: tabular-nums; }

/* --- Forms -------------------------------------------------------------- */

/* A row of fields with the button at the end, wrapping on a phone. */
.form-row {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 12px;
  margin-bottom: 16px;
}
.form-row .field { flex: 1 1 200px; min-width: 0; margin: 0; }
.form-row .field--narrow { flex: 0 1 130px; }
.field--narrow { max-width: 180px; }

.fieldset {
  border: 1px solid var(--line);
  border-radius: var(--radius-md, 10px);
  padding: 14px 16px;
  margin: 0 0 16px;
}
.fieldset > legend {
  padding: 0 6px;
  font-size: var(--fs-xs);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--ink-muted);
}

/* The wrapping label: a checkbox and its wording as one clickable block. */
label.check { display: flex; align-items: flex-start; gap: 9px; cursor: pointer; }
label.check--inline { display: inline-flex; align-items: center; }
label.check > input { margin-top: 3px; flex: 0 0 auto; }
label.check--inline > input { margin-top: 0; }
.check__help {
  display: block;
  margin-top: 4px;
  font-size: var(--fs-sm);
  color: var(--ink-muted);
  font-weight: 400;
}

.actions { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 16px; }

.rule { border: 0; border-top: 1px solid var(--line); margin: 20px 0; }

.list--plain { list-style: none; margin: 0; padding: 0; }
.list--plain li + li { margin-top: 5px; }

/* --- Derived figures in the editor -------------------------------------- */

.derived-row {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}
.derived {
  display: flex;
  align-items: baseline;
  gap: 8px;
  padding: 7px 12px;
  border: 1px solid var(--line);
  border-radius: var(--radius-md, 10px);
  background: var(--surface-sunken);
}
.derived__label {
  font-size: var(--fs-xs);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--ink-muted);
}
.derived__value {
  font-size: var(--fs-md);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

/* Extension sections 37 and 38: a load factor over 100 % is valid, so it is
   marked as notable rather than as an error - and never clamped. */
.derived__value.is-over-capacity { color: var(--accent-strong); }
.derived__value.is-over-capacity::after {
  content: 'jump seats';
  margin-left: 7px;
  padding: 1px 7px;
  border-radius: 999px;
  background: var(--accent-soft);
  color: var(--accent-strong);
  font-size: var(--fs-2xs);
  font-weight: 600;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  vertical-align: middle;
}

.field__help.is-warning { color: var(--warning-ink, #8a5a00); font-weight: 500; }

/* --- Cards and tables --------------------------------------------------- */

.card--flush { padding-left: 0; padding-right: 0; }
.card--flush > .table-wrap,
.card--flush > .map { border-radius: 0; }
.card--flush > *:not(.table-wrap):not(.map) { padding-left: 18px; padding-right: 18px; }

.card__subtitle {
  font-size: var(--fs-sm);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--ink-muted);
  margin: 0 0 8px;
}
.card__note {
  margin: 12px 0 0;
  font-size: var(--fs-sm);
  color: var(--ink-muted);
}

.row-actions { white-space: nowrap; }
.row-actions > * + * { margin-left: 6px; }

.table--compare th.is-sorted { background: var(--surface-accent); }
.table--compare th a { text-decoration: none; color: inherit; }
.table--compare th a:hover { text-decoration: underline; }

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

.map-toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 0 18px 12px;
}
.map-toolbar__actions { display: flex; flex-wrap: wrap; gap: 8px; }

.legend--inline { display: flex; flex-wrap: wrap; gap: 12px; }
.legend__item { display: inline-flex; align-items: center; gap: 6px; font-size: var(--fs-sm); }
.legend__swatch {
  width: 13px;
  height: 13px;
  border-radius: 3px;
  flex: 0 0 auto;
  border: 1px solid rgba(0, 0, 0, 0.12);
}

/* One colour per person on the shared map (extension section 24). Taken from
   the validated categorical palette in the order it defines, so the separation
   between any two adjacent participants is the one the validator checked. */
.legend__swatch--u0 { background: #2a78d6; }
.legend__swatch--u1 { background: #eb6834; }
.legend__swatch--u2 { background: #1a9e77; }
.legend__swatch--u3 { background: #8a5cd6; }
.legend__swatch--u4 { background: #c2185b; }
.legend__swatch--u5 { background: #0b7f9e; }
.legend__swatch--u6 { background: #b8860b; }
.legend__swatch--u7 { background: #5f6b7a; }

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* --- Wide statistics tables --------------------------------------------- *
 *
 * The load factor tables carry nine columns and scroll sideways on anything
 * narrower than a laptop. Three things make that readable rather than merely
 * possible: the first column stays put while the rest scrolls, alternate rows
 * are tinted so the eye can track across, and the figure most people came for
 * carries a bar so the column can be scanned instead of read.
 */

.data--wide { min-width: 720px; }

.data--wide tbody tr:nth-child(even) { background: color-mix(in srgb, var(--surface-sunken) 55%, transparent); }
.data--wide tbody tr:hover { background: var(--surface-accent); }

/* The group a row is about - route, airline, aircraft - stays visible while the
   figures scroll under the cursor. */
.data--wide th.sticky-col,
.data--wide td.sticky-col {
  position: sticky;
  left: 0;
  z-index: 1;
  background: var(--surface);
  border-right: 1px solid var(--line);
}

/* A visible edge, because the numbers slide underneath this column: without one, a
   scrolled "0.0-77.2 %" reads as a truncated figure rather than one passing behind.
   It is drawn as a pseudo-element: the table collapses its borders, so a border or a
   box-shadow on the cell itself does not travel with it while it sticks. */
.data--wide th.sticky-col::after,
.data--wide td.sticky-col::after {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  right: 0;
  width: 11px;
  transform: translateX(100%);
  pointer-events: none;
  background: linear-gradient(to right,
    var(--line-strong) 0 1px,
    rgb(0 0 0 / 0.30) 1px,
    rgb(0 0 0 / 0) 100%);
}
.data--wide thead th.sticky-col { z-index: 3; background: var(--surface-sunken); }
.data--wide tbody tr:nth-child(even) td.sticky-col {
  background: color-mix(in srgb, var(--surface-sunken) 55%, var(--surface));
}
.data--wide tbody tr:hover td.sticky-col { background: var(--surface-accent); }

/* The row's subject, with its longer name underneath in a quieter voice. */
.row-title { display: block; font-weight: 560; white-space: nowrap; }
.row-sub {
  display: block;
  margin-top: 1px;
  font-size: var(--fs-xs);
  color: var(--ink-muted);
  font-weight: 400;
  /* Long airport names would otherwise set the column width for the whole table. */
  max-width: 26ch;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* --- The load factor cell ------------------------------------------------ *
 *
 * A bar behind the number, on a track that runs to 125 % rather than 100. That
 * is what makes extension section 38 visible instead of merely true: a flight
 * loaded past its seats has a bar past the marker, and nothing is clipped.
 */

.lf {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 8px;
  min-width: 132px;
}
.lf__value { font-variant-numeric: tabular-nums; white-space: nowrap; }
.lf__value--over { color: var(--accent-strong); font-weight: 600; }

.lf__track {
  position: relative;
  flex: 1 1 auto;
  height: 7px;
  min-width: 54px;
  border-radius: 999px;
  background: var(--line);
  overflow: hidden;
}
.lf__fill {
  display: block;
  height: 100%;
  border-radius: 999px;
  background: var(--accent);
}
.lf__fill--over { background: var(--accent-strong); }

/* 100 % sits at four fifths of a track that runs to 125. */
.lf__track::after {
  content: "";
  position: absolute;
  left: 80%;
  top: -1px;
  bottom: -1px;
  width: 1px;
  background: var(--line-strong);
}

/* How many flights a figure rests on, as a fraction rather than a sentence -
   it has to sit in a table cell without doubling the row height. */
.basis-cell {
  font-size: var(--fs-xs);
  color: var(--ink-muted);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.basis-cell--partial { color: var(--ink-secondary); }

/* A value that was never recorded. Quiet, so a column of them does not shout. */
.nil { color: var(--ink-faint); }

/* A counted nought, as opposed to a gap: present, true, and deliberately quiet. */
.zero { color: var(--ink-muted); font-variant-numeric: tabular-nums; }

/* --- The filed ATC route (extension sections 9, 23 and 41) -------------- */

/* The plan as it was filed, set to be read token by token. */
.route-plan {
  margin: 0 0 14px;
  padding: 11px 13px;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--surface-sunken);
  font-family: var(--font-mono, ui-monospace, "SFMono-Regular", "Cascadia Mono",
               "Liberation Mono", Consolas, monospace);
  font-size: var(--fs-sm);
  line-height: 1.7;
  word-break: break-word;
}

/* An element that could not be placed. Section 41 keeps it in the list, so it has to
   read as present-but-unplaced rather than as an error or as missing. */
tr.is-unresolved td { color: var(--ink-muted); }
tr.is-unresolved code { opacity: 0.75; }

/* Column group headings, so nine columns read as three ideas. */
.data--wide thead tr.group-row th {
  position: sticky;
  top: 0;
  z-index: 2;
  padding-top: 8px;
  padding-bottom: 3px;
  font-size: var(--fs-2xs);
  letter-spacing: 0.08em;
  color: var(--ink-faint);
  border-bottom: 0;
  background: var(--surface-sunken);
}
.data--wide thead tr.group-row th.group {
  border-left: 1px solid var(--line);
  text-align: center;
}
.data--wide thead tr.label-row th { top: 26px; }

/* --- Load factor charts (extension sections 67 and 68) ------------------ */

/* The 100 % marker. Dashed and recessive on purpose: it is something to compare
   against, not a limit, and the series is drawn over it. */
.reference-line {
  stroke: var(--line-strong);
  stroke-width: 1;
  stroke-dasharray: 4 4;
}
.reference-label {
  fill: var(--ink-faint);
  font-size: 10px;
  letter-spacing: 0.04em;
  paint-order: stroke;
  stroke: var(--surface);
  stroke-width: 4px;
  stroke-linejoin: round;
}

/* --- The data basis beside every figure (extension section 47) ---------- */

.basis {
  display: block;
  margin-top: 3px;
  font-size: var(--fs-xs);
  color: var(--ink-muted);
  font-variant-numeric: tabular-nums;
}
.basis--warn { color: var(--ink-secondary); }

.stat-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(190px, 100%), 1fr));
  gap: 12px;
}
.stat {
  padding: 13px 15px;
  border: 1px solid var(--line);
  border-radius: var(--radius-md, 10px);
  background: var(--surface-sunken);
  min-width: 0;
}
.stat__label {
  display: block;
  font-size: var(--fs-xs);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--ink-muted);
}
.stat__value {
  display: block;
  margin-top: 4px;
  font-size: var(--fs-2xl);
  font-weight: 600;
  line-height: 1.15;
  font-variant-numeric: tabular-nums;
}
.stat__value--over { color: var(--accent-strong); }
.stat__sub {
  display: block;
  margin-top: 2px;
  font-size: var(--fs-sm);
  color: var(--ink-muted);
}

/* A load factor above 100 in a table cell, marked without being alarming. */
.lf-over { color: var(--accent-strong); font-weight: 600; }

.subnav {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin: 0 0 20px;
  padding: 0;
  list-style: none;
}
.subnav a {
  display: inline-block;
  padding: 6px 12px;
  border: 1px solid var(--line);
  border-radius: 999px;
  font-size: var(--fs-sm);
  text-decoration: none;
  color: var(--ink-secondary);
}
.subnav a:hover { border-color: var(--line-strong); color: var(--ink); }
.subnav a[aria-current="page"] {
  background: var(--accent-soft);
  border-color: var(--accent-ring);
  color: var(--accent-strong);
  font-weight: 600;
}

/* ==========================================================================
   The bulk track upload (bulk sections 3, 10 to 17)
   ========================================================================== */

.card__meta { color: var(--ink-muted); font-size: var(--fs-sm); }

/* The file chooser. A dashed well rather than a bare input, because the thing it has to
   communicate is "you may drop forty files here", which a button does not. */
.dropzone {
  position: relative;
  border: 2px dashed var(--line-strong);
  border-radius: var(--r-lg);
  padding: 26px 18px;
  text-align: center;
  background: var(--surface-sunken);
  transition: border-color 0.15s ease, background 0.15s ease;
}
.dropzone--over { border-color: var(--accent); background: var(--accent-soft); }

/* Covers the well so a click anywhere opens the picker, and keyboard focus still lands
   on a real input rather than on a div pretending to be one. */
.dropzone__input {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  opacity: 0;
  cursor: pointer;
}
.dropzone__input:focus-visible + .dropzone__label { outline: 2px solid var(--accent); outline-offset: 4px; }
.dropzone__label { display: block; pointer-events: none; }
.dropzone__label strong { display: block; font-size: var(--fs-md); color: var(--accent-strong); }
.dropzone__label span { color: var(--ink-muted); font-size: var(--fs-sm); }
.dropzone__help { margin: 10px 0 0; color: var(--ink-muted); font-size: var(--fs-xs); }

.chosen { margin-top: 16px; }
.chosen__summary {
  display: flex;
  align-items: center;
  gap: 10px;
  padding-bottom: 8px;
  border-bottom: 1px solid var(--line);
  font-size: var(--fs-sm);
}
.chosen__summary span { color: var(--ink-muted); }
.chosen__summary .btn { margin-left: auto; }
.chosen__list { list-style: none; margin: 0; padding: 0; max-height: 260px; overflow-y: auto; }
.chosen__item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 6px 0;
  border-bottom: 1px solid var(--line);
  font-size: var(--fs-sm);
}
.chosen__name { flex: 1 1 auto; min-width: 0; overflow-wrap: anywhere; }
.chosen__size { color: var(--ink-muted); font-variant-numeric: tabular-nums; }

/* The counts above the assignment table. */
.tally { display: flex; flex-wrap: wrap; gap: 8px 18px; font-size: var(--fs-sm); }
.tally__item { color: var(--ink-muted); }
.tally__item strong { color: var(--ink); font-variant-numeric: tabular-nums; }
.tally__item--good strong { color: var(--good); }
.tally__item--warning strong { color: var(--warning); }
.tally__item--critical strong { color: var(--critical); }

.filters__spacer { flex: 1 1 auto; }
.label-inline { display: inline-flex; align-items: center; gap: 7px; font-size: var(--fs-sm); }
.break { overflow-wrap: anywhere; }
.tight--select { max-width: 320px; }

/* A row whose file competes with another for the same flight. Marked, never resolved:
   bulk section 13 is explicit that the choice stays with the person. */
.row--conflict { background: var(--critical-soft); }
.row--conflict:hover { background: var(--critical-soft); }

/* The badge tone the rest of the site did not need: a state that is neither good nor
   bad, only settled. */
.badge--muted { background: var(--surface-sunken); color: var(--ink-muted); }

/* The preview and the flight picker share one dialog. */
.modal {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 18px;
  background: rgb(15 23 42 / 0.45);
}
.modal[hidden] { display: none; }
.modal__box {
  width: min(760px, 100%);
  max-height: 88vh;
  display: flex;
  flex-direction: column;
  background: var(--surface-raised);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-lg);
}
.modal__head {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 16px;
  border-bottom: 1px solid var(--line);
}
.modal__head h3 { margin: 0; font-size: var(--fs-md); }
.modal__head .btn { margin-left: auto; }
.modal__body { padding: 14px 16px; overflow-y: auto; }
.map--modal { height: 380px; border-radius: var(--r-md); overflow: hidden; }

.picker__search {
  width: 100%;
  margin-bottom: 10px;
}
.picker__results { margin-top: 10px; max-height: 340px; overflow-y: auto; }
.picker__row {
  display: block;
  width: 100%;
  text-align: left;
  padding: 9px 11px;
  border: 1px solid transparent;
  border-bottom-color: var(--line);
  background: none;
  font: inherit;
  color: inherit;
  cursor: pointer;
  border-radius: var(--r-sm);
}
.picker__row:hover, .picker__row:focus-visible {
  background: var(--accent-soft);
  border-color: var(--accent-ring);
}

/* The closing report. */
.report__heading {
  margin: 16px 0 6px;
  font-size: var(--fs-sm);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--ink-muted);
}
.report__heading:first-child { margin-top: 0; }
.report__heading span {
  display: inline-block;
  margin-left: 6px;
  padding: 1px 7px;
  border-radius: var(--r-pill);
  background: var(--surface-sunken);
  color: var(--ink);
}
.report__heading--good span { background: var(--good-soft); color: var(--good); }
.report__heading--warn span { background: var(--warning-soft); color: var(--warning); }
.report__heading--bad span { background: var(--critical-soft); color: var(--critical); }
.report__list { margin: 0; padding-left: 20px; font-size: var(--fs-sm); }
.report__list li { padding: 2px 0; overflow-wrap: anywhere; }

@media (max-width: 720px) {
  .tight--select { max-width: 100%; }
  .map--modal { height: 280px; }
}

/* ==========================================================================
   The pie chart page (chart sections 23 to 25, 45 to 51)
   ==========================================================================

   Its own palette, deliberately brighter and more varied than the rest of the site,
   which is what the brief asks for. Eight slots, assigned in fixed order and never
   cycled: a ninth category becomes "Other" on the server rather than getting an
   invented ninth hue.

   The exact ten colours the brief listed were measured against the colour checks and
   two pairs fail outright - #EA580C against #DC2626 at ΔE 8.7 for normal vision where
   15 is the floor, and #7C3AED against #2563EB at ΔE 0.4 under deuteranopia, which is
   no difference at all. No set of eight bright hues passes every pair; that is a
   property of colour, not of effort. So eight of the brief's own families are kept,
   the yellow is darkened for contrast, the violet is replaced by a fuchsia that
   separates from blue, and every chart carries a legend, a label, a tooltip and a
   table - which is what actually makes a slice identifiable.

   Dark mode is a second set chosen against the dark surface, not the light one
   lightened: the same hues at the weight that clears #0e1e33. */

:root {
  --pie-1: #2563eb;   /* blue    */
  --pie-2: #ea580c;   /* orange  */
  --pie-3: #059669;   /* emerald */
  --pie-4: #c026d3;   /* fuchsia */
  --pie-5: #ca8a04;   /* amber   */
  --pie-6: #0891b2;   /* cyan    */
  --pie-7: #dc2626;   /* red     */
  --pie-8: #92400e;   /* brown   */
  /* Not a ninth category: the absence of one. Grey says "several small things" without
     competing with the named slices for attention. */
  --pie-other: #94a3b8;
}

@media (prefers-color-scheme: dark) {
  :root:where(:not([data-theme="light"])) {
    --pie-1: #60a5fa;
    --pie-2: #fb923c;
    --pie-3: #34d399;
    --pie-4: #e879f9;
    --pie-5: #facc15;
    --pie-6: #22d3ee;
    --pie-7: #f87171;
    --pie-8: #d6a06a;
    --pie-other: #64748b;
  }
}
:root[data-theme="dark"] {
  --pie-1: #60a5fa;
  --pie-2: #fb923c;
  --pie-3: #34d399;
  --pie-4: #e879f9;
  --pie-5: #facc15;
  --pie-6: #22d3ee;
  --pie-7: #f87171;
  --pie-8: #d6a06a;
  --pie-other: #64748b;
}

/* Section 48: three across on a desktop, two on a tablet, one on a phone. */
.pies {
  display: grid;
  gap: 16px;
  grid-template-columns: repeat(auto-fit, minmax(330px, 1fr));
  margin-top: 16px;
}
.pie { margin: 0; }
.pie__head h2 { font-size: var(--fs-md); }
.pie__body { display: flex; gap: 16px; align-items: center; flex-wrap: wrap; }
.pie__chart { position: relative; flex: 0 0 200px; width: 200px; }
.pie__chart .chart__svg { width: 100%; height: auto; display: block; }
.pie__note { margin-top: 12px; }

.donut__slice {
  transition: opacity 0.12s ease;
  cursor: default;
  outline: none;
}
.donut__slice--on { opacity: 0.82; }
.donut__slice:focus-visible { stroke: var(--ink); stroke-width: 2.5; }

.donut__label {
  font-size: 11px;
  font-weight: 650;
  fill: #ffffff;
  /* A white percentage on a mid-weight fill needs a hair of separation on the lighter
     slices, and a shadow costs nothing on the darker ones. */
  paint-order: stroke;
  stroke: rgb(0 0 0 / 0.25);
  stroke-width: 2.5px;
  stroke-linejoin: round;
}
.donut__total { font-size: 19px; font-weight: 680; fill: var(--ink); }
.donut__unit { font-size: 10px; fill: var(--ink-muted); letter-spacing: 0.04em; }

/* The legend, which is not optional here: it is how a slice is identified. */
.legend { list-style: none; margin: 0; padding: 0; flex: 1 1 210px; min-width: 0; }
.legend__row {
  display: grid;
  grid-template-columns: 12px 1fr auto auto;
  align-items: center;
  gap: 8px;
  padding: 3px 0;
  font-size: var(--fs-sm);
}
.legend__swatch {
  width: 12px;
  height: 12px;
  border-radius: 3px;
  flex: none;
}
/* Twenty-four slots, matching pieColour() in lib/chart.js: the eight hues, then the
   same eight lightened, then the same eight darkened. Nothing folds into "Other" any
   more, so a chart with twenty categories still gives each one its own colour and the
   legend below lists every single one with its count and share. */
.legend__swatch[data-pie-slot="0"] { background: var(--pie-1); }
.legend__swatch[data-pie-slot="1"] { background: var(--pie-2); }
.legend__swatch[data-pie-slot="2"] { background: var(--pie-3); }
.legend__swatch[data-pie-slot="3"] { background: var(--pie-4); }
.legend__swatch[data-pie-slot="4"] { background: var(--pie-5); }
.legend__swatch[data-pie-slot="5"] { background: var(--pie-6); }
.legend__swatch[data-pie-slot="6"] { background: var(--pie-7); }
.legend__swatch[data-pie-slot="7"] { background: var(--pie-8); }

.legend__swatch[data-pie-slot="8"]  { background: color-mix(in oklab, var(--pie-1) 58%, white); }
.legend__swatch[data-pie-slot="9"]  { background: color-mix(in oklab, var(--pie-2) 58%, white); }
.legend__swatch[data-pie-slot="10"] { background: color-mix(in oklab, var(--pie-3) 58%, white); }
.legend__swatch[data-pie-slot="11"] { background: color-mix(in oklab, var(--pie-4) 58%, white); }
.legend__swatch[data-pie-slot="12"] { background: color-mix(in oklab, var(--pie-5) 58%, white); }
.legend__swatch[data-pie-slot="13"] { background: color-mix(in oklab, var(--pie-6) 58%, white); }
.legend__swatch[data-pie-slot="14"] { background: color-mix(in oklab, var(--pie-7) 58%, white); }
.legend__swatch[data-pie-slot="15"] { background: color-mix(in oklab, var(--pie-8) 58%, white); }

.legend__swatch[data-pie-slot="16"] { background: color-mix(in oklab, var(--pie-1) 70%, black); }
.legend__swatch[data-pie-slot="17"] { background: color-mix(in oklab, var(--pie-2) 70%, black); }
.legend__swatch[data-pie-slot="18"] { background: color-mix(in oklab, var(--pie-3) 70%, black); }
.legend__swatch[data-pie-slot="19"] { background: color-mix(in oklab, var(--pie-4) 70%, black); }
.legend__swatch[data-pie-slot="20"] { background: color-mix(in oklab, var(--pie-5) 70%, black); }
.legend__swatch[data-pie-slot="21"] { background: color-mix(in oklab, var(--pie-6) 70%, black); }
.legend__swatch[data-pie-slot="22"] { background: color-mix(in oklab, var(--pie-7) 70%, black); }
.legend__swatch[data-pie-slot="23"] { background: color-mix(in oklab, var(--pie-8) 70%, black); }

.legend__swatch[data-pie-slot="other"] { background: var(--pie-other); }
.legend__name { overflow-wrap: anywhere; }
.legend__value { font-variant-numeric: tabular-nums; color: var(--ink-secondary); }
.legend__share { font-variant-numeric: tabular-nums; color: var(--ink-muted); min-width: 48px; text-align: right; }

.pie__table { margin-top: 12px; }
.pie__table summary {
  cursor: pointer;
  font-size: var(--fs-sm);
  color: var(--ink-muted);
  padding: 4px 0;
}
.pie__table summary:hover { color: var(--accent); }
.pie__table[open] summary { margin-bottom: 6px; }
.pie__member td { color: var(--ink-muted); font-size: var(--fs-xs); }

@media (max-width: 480px) {
  .pie__body { flex-direction: column; align-items: stretch; }
  .pie__chart { flex: 0 0 auto; width: 100%; max-width: 240px; margin: 0 auto; }
  .legend__row { grid-template-columns: 12px 1fr auto; }
  .legend__share { grid-column: 2 / -1; text-align: left; min-width: 0; }
}

@media (prefers-reduced-motion: reduce) {
  .donut__slice { transition: none; }
}
