/* Flight Operations & FDTL — application styles */

/* One knob for the whole application's type scale. Almost every size is in rem
   (relative to this root) and body is expressed as a ratio of it, so bumping
   this percentage grows all text and rem-based spacing together. 112.5% = an
   18px root (from the 16px default); raise it for larger text. */
html { font-size: 112.5%; }

:root {
  --ink: #14181f;
  --ink-soft: #5b6472;
  /* Borders a step darker than before: the old #d8dee7 hairlines were hard to
     see, so the whole app read as one flat surface. */
  --line: #b4bece;
  --line-soft: #d5dce5;
  --bg: #f5f7fa;
  --surface: #ffffff;
  --brand: #0b3d6b;
  --brand-dark: #082c4e;
  --accent: #1565c0;

  /* Limit bands — a clear red → orange → yellow severity ramp, with light blue
     kept apart for "check this row" (a data-quality flag, not a severity). Each
     stays light enough to carry its dark ink label on a shaded cell/row. */
  --breach-bg: #f9cccc;
  --breach-ink: #8a1414;
  --orange-bg: #f7bf85;
  --orange-ink: #8a4b0a;
  --yellow-bg: #f2df8f;
  --yellow-ink: #6d5a06;
  --anomaly-bg: #b8d9f5;

  --radius: 6px;
  --shadow: 0 1px 3px rgba(15, 25, 40, .08), 0 4px 12px rgba(15, 25, 40, .06);
}

* { box-sizing: border-box; }

body {
  margin: 0;
  font-family: "Segoe UI", Roboto, system-ui, -apple-system, sans-serif;
  /* 0.875rem preserves the previous 14px look at the default root; it now
     scales with the root knob above rather than being pinned in px. */
  font-size: 0.875rem;
  color: var(--ink);
  background: var(--bg);
}

h1, h2, h3 { margin: 0 0 .5rem; font-weight: 600; }
h2 { font-size: 1.15rem; }
h3 { font-size: 1rem; }
code { font-family: Consolas, "SF Mono", monospace; font-size: .85em; }

/* ---------------------------------------------------------------- auth --- */
.auth-page {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1.5rem;
  padding: 2rem 1rem;
}
.auth-card {
  width: 100%;
  max-width: 380px;
  background: var(--surface);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 2rem;
}
.auth-card-wide { max-width: 520px; }
.auth-brand { text-align: center; margin-bottom: 1.5rem; }
.auth-brand h1 { font-size: 1.25rem; color: var(--brand); }
.auth-sub { color: var(--ink-soft); margin: 0; font-size: .9rem; }
.auth-note {
  margin: 1.25rem 0 0;
  font-size: .8rem;
  color: var(--ink-soft);
  text-align: center;
}
.signout-form { margin-top: 1rem; text-align: center; }

.category-title { text-align: center; }
.category-hint { text-align: center; color: var(--ink-soft); margin-top: 0; }
.category-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1rem;
  margin: 1.5rem 0;
}
.category-option {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: .5rem;
  padding: 1.5rem 1rem;
  border: 2px solid var(--line);
  border-radius: var(--radius);
  cursor: pointer;
  transition: border-color .15s, background .15s;
}
.category-option:hover { border-color: var(--accent); background: #f7fbff; }
.category-option:has(input:checked) { border-color: var(--brand); background: #eef5fc; }
.category-label { font-weight: 600; }
/* Big aircraft emoji on each category card, and a small one in the header chip. */
.category-emoji { font-size: 2.6rem; line-height: 1; }
.chip-emoji { font-size: 1em; margin-right: .1rem; }

/* --------------------------------------------------------------- shell --- */
.gov-header { background: var(--brand); color: #fff; }
.gov-header-standalone { width: 100%; text-align: center; padding: 1rem; }
.gov-header-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: .85rem 1.25rem;
  flex-wrap: wrap;
}
.gov-header h1 {
  margin: 0;
  font-size: 1.1rem;
  letter-spacing: .06em;
  text-transform: uppercase;
}
.gov-header-meta { display: flex; align-items: center; gap: .75rem; font-size: .85rem; }

/* Live UTC / IST clocks. Tabular figures so the digits do not jitter as the
   seconds tick. */
.clock {
  display: flex;
  gap: .9rem;
  margin-left: auto;
  margin-right: .5rem;
  font-size: .78rem;
  font-variant-numeric: tabular-nums;
}
.clock-zone { display: inline-flex; align-items: baseline; gap: .35rem; white-space: nowrap; }
.clock-label {
  font-size: .62rem;
  font-weight: 700;
  letter-spacing: .08em;
  padding: .1rem .3rem;
  border-radius: 3px;
  background: rgba(255, 255, 255, .18);
}
.clock time { color: #eaf2fa; }
.clock-ip { color: #eaf2fa; }
.category-chip, .user-chip {
  background: rgba(255, 255, 255, .16);
  padding: .2rem .6rem;
  border-radius: 999px;
}
.gov-header .btn-link { color: #cfe2f5; }

/* Account menu -- name + caret opens a light popover with the actions that
   used to sit loose in the bar (Change category, Sign out). */
.user-menu { position: relative; }
.user-menu > summary {
  list-style: none;
  cursor: pointer;
  user-select: none;
  display: inline-flex;
  align-items: center;
  gap: .35rem;
}
.user-menu > summary::-webkit-details-marker { display: none; }
.user-menu .chip-caret { font-size: .7em; transition: transform .15s ease; }
.user-menu[open] .chip-caret { transform: rotate(180deg); }
.user-menu-panel {
  position: absolute;
  right: 0;
  top: calc(100% + .45rem);
  min-width: 11rem;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 6px;
  box-shadow: 0 10px 26px rgba(16, 24, 40, .22);
  padding: .3rem;
  z-index: 60;
}
.user-menu-panel form { margin: 0; }
.user-menu-item {
  display: block;
  width: 100%;
  text-align: left;
  background: none;
  border: none;
  padding: .5rem .6rem;
  border-radius: 4px;
  font: inherit;
  color: var(--ink);
  cursor: pointer;
}
.user-menu-item:hover { background: var(--line-soft); }

.app-body { display: flex; min-height: calc(100vh - 52px); align-items: stretch; }
.sidebar {
  /* Sized to the widest label, not padded out -- the old 200px left a wide
     empty strip between the nav and the content. Widened slightly from
     160px for the uppercase labels below, which run wider than mixed case. */
  width: 172px;
  flex: 0 0 172px;
  background: var(--surface);
  border-right: 1px solid var(--line);
  padding: 1rem 0;
}
.sidebar ul { list-style: none; margin: 0; padding: 0; }
.sidebar a {
  display: block;
  padding: .6rem 1rem;
  color: var(--ink);
  text-decoration: none;
  border-left: 3px solid transparent;
  text-transform: uppercase;
}
.sidebar a:hover { background: var(--line-soft); }
.sidebar a.active {
  border-left-color: var(--brand);
  background: #eef5fc;
  color: var(--brand);
  font-weight: 600;
}
.content { flex: 1 1 auto; padding: 1.25rem 1.25rem 1.25rem 1rem; min-width: 0; }

/* -------------------------------------------------------------- panels --- */
.panel {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 1.1rem;
  margin-bottom: 1.1rem;
}
.panel-flush { padding: 1.1rem 0 0; }
/* Crew Monthly Duty Report: one grid per pilot, stacked -- a heading and a
   rule between sections so a long month's worth of rows doesn't blur into
   the next pilot's. */
.crew-duty-section { margin-top: 1.6rem; padding-top: 1.2rem; border-top: 2px solid var(--line); }
.crew-duty-section:first-of-type { margin-top: .6rem; border-top: none; padding-top: 0; }
.crew-duty-pilot-name { margin: 0 0 .5rem; font-size: 1rem; color: var(--brand); }
.panel-narrow { max-width: 560px; }
.panel-head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 1rem;
  margin-bottom: .75rem;
  flex-wrap: wrap;
}
/* Collapsed by default -- click the heading to reveal the table below. */
.balances-panel > summary.panel-head {
  list-style: none;
  cursor: pointer;
  user-select: none;
  margin-bottom: 0;
}
.balances-panel > summary::-webkit-details-marker { display: none; }
.balances-panel .chip-caret { font-size: .7em; transition: transform .15s ease; }
.balances-panel[open] .chip-caret { transform: rotate(180deg); }
.balances-panel[open] > summary.panel-head { margin-bottom: .75rem; }

/* Instructions: the balance-colour key and other on-screen conventions,
   collapsed by default under the operator name -- one click instead of a
   strip taking space at the bottom of every page. Same native-<details>
   pattern as the other menus/panels, no JavaScript. */
.instructions-panel { margin: .4rem 0 0; }
.instructions-panel > summary {
  list-style: none;
  cursor: pointer;
  user-select: none;
  display: inline-flex;
  align-items: center;
  gap: .3rem;
  font-size: .92rem;
  font-weight: 700;
  color: var(--breach-ink);
}
.instructions-panel > summary::-webkit-details-marker { display: none; }
.instructions-panel .chip-caret { font-size: .7em; transition: transform .15s ease; }
.instructions-panel[open] .chip-caret { transform: rotate(180deg); }
.instructions-panel .legend { padding: .5rem 0 0; }
.label-muted {
  margin: 0;
  font-size: .72rem;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--ink-soft);
}
.operator-name { margin: .15rem 0 0; font-size: 1.05rem; font-weight: 600; }
.hint { color: var(--ink-soft); font-size: .82rem; margin: .25rem 0; }
/* Positioning/deadheading reminder: an active caution, not a passive hint --
   shown only while that duty type is picked (app.js), so it needs to read as
   "notice this" rather than blend into the grey hint text above it. */
.hint-positioning {
  color: var(--yellow-ink);
  background: var(--yellow-bg);
  border-left: 3px solid var(--yellow-ink);
  padding: .4rem .6rem;
  border-radius: 2px;
}
/* CAR 7J2 reminder: same "notice this" treatment as positioning above, but
   bold throughout -- a cited CAR obligation, not just a procedural note. */
.hint-outstation {
  color: var(--yellow-ink);
  background: var(--yellow-bg);
  border-left: 3px solid var(--yellow-ink);
  padding: .4rem .6rem;
  border-radius: 2px;
  font-weight: 700;
}
.empty { color: var(--ink-soft); text-align: center; padding: 1.25rem; }
.subheading { margin-top: 1.25rem; }
.text-right { text-align: right; }

/* ------------------------------------------------------------- filters --- */
.filter-bar {
  padding-top: .75rem;
  border-top: 1px solid var(--line-soft);
}
/* One row of controls that wraps as a unit. Fields keep their labels stacked
   above the input, so everything aligns on the input baseline. */
.filter-controls {
  display: flex;
  align-items: flex-end;
  gap: .85rem 1rem;
  flex-wrap: wrap;
}
/* The [hidden] attribute alone loses to the flex rule above -- same
   specificity, and an author rule always beats the browser's own [hidden]
   default regardless of order. Needed wherever .filter-controls is also
   toggled hidden (e.g. the Training screen's "+ Add type" reveal). */
.filter-controls[hidden] { display: none; }
.filter-controls > .btn { flex: 0 0 auto; }
/* Training's "Updated as of" -- pinned to the far right of the filter row
   it now lives in, clear of the Pilot/Type-of-aircraft/Apply controls. */
.filter-controls .updated-as-of { margin-left: auto; }
.field { margin-bottom: .85rem; }
.field-inline { margin-bottom: 0; }
.field-inline select { min-width: 11rem; }
.field label {
  display: block;
  font-size: .78rem;
  font-weight: 600;
  color: var(--ink-soft);
  margin-bottom: .25rem;
}
input[type="text"], input[type="password"], input[type="date"], input[type="time"],
input[type="number"], input[type="email"], select, textarea {
  width: 100%;
  padding: .45rem .55rem;
  border: 1px solid var(--line);
  border-radius: 4px;
  font: inherit;
  background: #fff;
  color: var(--ink);
}
input:focus, select:focus, textarea:focus {
  outline: 2px solid var(--accent);
  outline-offset: -1px;
  border-color: var(--accent);
}
/* Number inputs: drop the spinner arrows -- they crowd the field and invite an
   accidental change (e.g. No. of landings). Digits are still typed normally. */
input[type="number"]::-webkit-inner-spin-button,
input[type="number"]::-webkit-outer-spin-button { -webkit-appearance: none; margin: 0; }
input[type="number"] { -moz-appearance: textfield; appearance: textfield; }
.field-error { color: var(--breach-ink); font-size: .8rem; margin: .25rem 0 0; }
.error-list { margin: .3rem 0 0; padding-left: 1.2rem; font-size: .85rem; }
.error-list li { margin-bottom: .15rem; }

/* Training header's "Aircraft" field: one small box per registration
   (app.js progressively enhances the single comma-separated input). */
.aircraft-reg-rows { display: flex; flex-direction: column; gap: .3rem; }
.aircraft-reg-row { display: flex; align-items: center; gap: .3rem; }
.aircraft-reg-input { width: 9rem; }
.aircraft-reg-remove {
  border: none; background: none; cursor: pointer; line-height: 1;
  font-size: 1.1rem; color: var(--muted-ink, #666); padding: 0 .25rem;
}
.aircraft-reg-remove:hover { color: var(--breach-ink); }
[data-aircraft-reg-add] { margin-top: .3rem; }

/* Typed date entry with the native picker alongside. The text box is what
   submits; the picker only writes into it. */
.date-field {
  display: flex;
  align-items: stretch;
  border: 1px solid var(--line);
  border-radius: 4px;
  background: #fff;
  overflow: hidden;
}
.date-field:focus-within { outline: 2px solid var(--accent); outline-offset: -1px; }
.date-field .date-text {
  border: none;
  border-radius: 0;
  width: 7.5rem;
  font-variant-numeric: tabular-nums;
}
.date-field .date-text:focus { outline: none; }
.date-field .date-text:invalid { color: var(--breach-ink); }
/* Collapse the native control down to just its calendar button. */
.date-field .date-picker {
  width: 2rem;
  border: none;
  border-left: 1px solid var(--line-soft);
  border-radius: 0;
  padding: 0;
  background: var(--line-soft);
  color: transparent;
  cursor: pointer;
}
.date-field .date-picker:focus { outline: none; }
.date-field .date-picker::-webkit-datetime-edit,
.date-field .date-picker::-webkit-inner-spin-button,
.date-field .date-picker::-webkit-clear-button { display: none; }
.date-field .date-picker::-webkit-calendar-picker-indicator {
  opacity: .7;
  cursor: pointer;
  margin: 0;
  padding: 0 .35rem;
}
.date-field .date-picker:hover::-webkit-calendar-picker-indicator { opacity: 1; }

.quick-ranges {
  display: flex;
  gap: .75rem;
  align-items: center;
  margin-left: auto;   /* sits at the far end of the control row */
  padding-bottom: .1rem;
  flex-shrink: 0;
}
.quick-ranges .btn-link { white-space: nowrap; }

/* A row with quick-ranges must stay on one line -- ordinary .filter-controls
   wrapping would send the whole quick-ranges group to its own row well
   before the fields actually run out of space, since flex-wrap treats it as
   one unbreakable box. Scoped to rows that carry quick-ranges only, so every
   other filter bar keeps its normal wrap-when-needed behaviour. */
.filter-controls:has(.quick-ranges) {
  flex-wrap: nowrap;
}
.filter-controls:has(.quick-ranges) .field-inline {
  min-width: 0;
}

/* ------------------------------------------------------ column picker --- */
/* A <details> rather than a scripted dropdown, so it opens and closes with no
   JavaScript at all. Toggling a column only adds this class -- the cells stay
   in the DOM, so showing one again is instant and the grid does not reflow
   from a reload. */
.col-hidden { display: none !important; }

.column-picker { position: relative; flex: 0 0 auto; padding-bottom: .1rem; }
.column-picker > summary {
  display: inline-flex;
  gap: .4rem;
  align-items: center;
  list-style: none;
  cursor: pointer;
  user-select: none;
  white-space: nowrap;
}
.column-picker > summary::-webkit-details-marker { display: none; }
.column-picker > summary::after {
  content: "▾";
  font-size: .7em;
  transition: transform .15s ease;
}
.column-picker[open] > summary::after { transform: rotate(180deg); }
.picker-icon { font-size: .85em; opacity: .75; }

.picker-count {
  padding: .05rem .4rem;
  border-radius: 999px;
  background: var(--line, #D8DEE7);
  color: var(--ink, #1B2430);
  font-size: .68rem;
  font-variant-numeric: tabular-nums;
}
/* A subset is a state worth noticing -- a column hidden and forgotten is a
   misread waiting to happen on a compliance screen. */
.column-picker.has-subset .picker-count {
  background: var(--accent, #0B3D6B);
  color: #fff;
}

.picker-panel {
  position: absolute;
  z-index: 40;
  right: 0;
  margin-top: .45rem;
  padding: .75rem;
  width: min(36rem, 86vw);
  background: var(--surface, #fff);
  border: 1px solid var(--line);
  border-radius: 8px;
  box-shadow: 0 12px 28px rgba(16, 24, 40, .18);
}
.column-picker[open] > .picker-panel { animation: picker-in .14s ease-out; }
@keyframes picker-in {
  from { opacity: 0; transform: translateY(-.35rem); }
  to   { opacity: 1; transform: translateY(0); }
}
@media (prefers-reduced-motion: reduce) {
  .column-picker[open] > .picker-panel { animation: none; }
  .column-picker > summary::after { transition: none; }
}

.picker-search { margin-bottom: .5rem; }
.picker-filter {
  width: 100%;
  padding: .35rem .5rem;
  border: 1px solid var(--line);
  border-radius: 5px;
  font: inherit;
  font-size: .8rem;
}
.picker-actions {
  display: flex;
  gap: .75rem;
  align-items: center;
  margin-bottom: .5rem;
  padding-bottom: .5rem;
  border-bottom: 1px solid var(--line);
}
.picker-hint { margin-left: auto; font-size: .72rem; color: var(--ink-muted, #5B6472); }
.picker-list {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(11rem, 1fr));
  gap: .1rem .75rem;
  margin: 0;
  padding: 0;
  max-height: 16rem;
  overflow-y: auto;
  list-style: none;
}
.picker-item {
  display: flex;
  gap: .45rem;
  align-items: center;
  padding: .2rem .3rem;
  border-radius: 4px;
  font-size: .76rem;
  font-weight: 400;
  cursor: pointer;
  transition: background-color .1s ease;
}
.picker-item:hover { background: var(--surface-2, #F1F4F8); }
.picker-item.is-locked { color: var(--ink-muted, #5B6472); cursor: default; }
.picker-item.is-locked:hover { background: none; }
.picker-item input { flex: 0 0 auto; }
.picker-empty {
  margin: .5rem 0 0;
  font-size: .76rem;
  color: var(--ink-muted, #5B6472);
}

/* ------------------------------------------------------------- buttons --- */
.btn {
  display: inline-block;
  padding: .45rem .9rem;
  border: 1px solid transparent;
  border-radius: 4px;
  font: inherit;
  font-weight: 600;
  cursor: pointer;
  text-decoration: none;
  line-height: 1.4;
}
.btn-primary { background: var(--brand); color: #fff; }
.btn-primary:hover { background: var(--brand-dark); }
.btn-secondary { background: #fff; color: var(--ink); border-color: var(--line); }
.btn-danger { background: #fff; color: var(--breach-ink); border-color: var(--breach-ink); }
.btn-danger:hover { background: var(--breach-bg); }
.btn-secondary:hover { background: var(--line-soft); }
.btn-link {
  background: none;
  border: none;
  color: var(--accent);
  padding: .2rem .3rem;
  text-decoration: underline;
}
.btn-block { width: 100%; }
.btn-small { padding: .3rem .6rem; font-size: .82rem; }
.inline-form { display: inline; }
.download-group { display: flex; gap: .5rem; flex-wrap: wrap; align-items: flex-start; }

/* PDF download menu -- a native <details> popover offering the plain sheet or
   the sheet plus a document manifest. */
.pdf-menu { position: relative; }
.pdf-menu > summary {
  list-style: none;
  cursor: pointer;
  user-select: none;
  display: inline-flex;
  align-items: center;
  gap: .35rem;
}
.pdf-menu > summary::-webkit-details-marker { display: none; }
.pdf-menu .chip-caret { font-size: .7em; transition: transform .15s ease; }
.pdf-menu[open] .chip-caret { transform: rotate(180deg); }
.pdf-menu-panel {
  position: absolute;
  right: 0;
  top: calc(100% + .4rem);
  min-width: 14rem;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 6px;
  box-shadow: 0 10px 26px rgba(16, 24, 40, .22);
  padding: .3rem;
  z-index: 40;
}
.pdf-menu-item {
  display: block;
  padding: .5rem .6rem;
  border-radius: 4px;
  color: var(--ink);
  text-decoration: none;
  font-size: .85rem;
}
.pdf-menu-item:hover { background: var(--line-soft); }
.row-inactive td { opacity: .55; }
.form-actions { display: flex; gap: .6rem; justify-content: flex-end; margin-top: 1rem; }
.actions { white-space: nowrap; }

/* -------------------------------------------------------------- alerts --- */
.alert { padding: .65rem .85rem; border-radius: 4px; margin-bottom: .75rem; }
.alert-error { background: var(--breach-bg); color: var(--breach-ink); }
.alert-warning { background: var(--orange-bg); color: var(--orange-ink); }
.alert-info, .alert-success { background: #e3f0fb; color: #0d3c61; }
.messages { margin-bottom: .75rem; }

/* ---------------------------------------------------------------- grid --- */
/* Bounded so the header row (and the sticky DATE column) actually freeze:
   position:sticky needs a scroll container to stick within, and a table with
   no height simply scrolls with the page instead. max-height is a ceiling, so
   short tables (e.g. current balances) still render at their natural height. */
.grid-scroll {
  overflow: auto;
  max-height: calc(100vh - 13rem);
  -webkit-overflow-scrolling: touch;
}
/* tabindex="0" in the markup makes the scroll container itself focusable, so
   Up/Down/Page Up/Page Down/Home/End scroll it -- without focus, a browser
   only routes arrow keys to whichever element (if any) already has it, so the
   grid silently ignored them. */
.grid-scroll:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
}
.grid { border-collapse: separate; border-spacing: 0; width: 100%; font-size: .8rem; white-space: nowrap; }
.grid th, .grid td {
  border-right: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
  padding: .35rem .5rem;
  text-align: center;
}
.grid th:first-child, .grid td:first-child { border-left: 1px solid var(--line); }
.grid thead th { border-top: 1px solid var(--line); }
.grid thead th {
  background: #eef2f7;
  font-size: .78rem;
  letter-spacing: .03em;
  text-transform: uppercase;
  color: var(--ink-soft);
  position: sticky;
  top: 0;
  z-index: 2;
  /* Centre every label vertically so the single-line headers sit level with the
     two-line "NO. OF LANDINGS" -- one consistent row, one font size. */
  vertical-align: middle;
}
/* Landings shares BA TIME's width: its header stacks ("NO. OF" / "LANDINGS") via
   a <br>, so the column no longer stretches to fit one long line. Both columns
   are pinned to the same width so they match. */
.grid th[data-col="NO. OF LANDINGS"], .grid td[data-col="NO. OF LANDINGS"],
.grid th[data-col="BA TIME"], .grid td[data-col="BA TIME"] {
  width: 4.8rem;
  min-width: 4.8rem;
}
.grid thead th[data-col="NO. OF LANDINGS"] { line-height: 1.15; }

/* FDTL grid: let multi-word headers stack ("TOTAL" / "DP") so each column is as
   narrow as its data, and more columns fit on screen. Data cells keep one line.
   The DATE column stays wide enough for the full date. */
#fdtl-grid thead th {
  white-space: normal;
  min-width: 3.3rem;
  line-height: 1.15;
  vertical-align: middle;
}
#fdtl-grid th, #fdtl-grid td { padding-left: .35rem; padding-right: .35rem; }
/* Collapse the empty row-actions column to its content (the × on planned rows),
   so with a trimmed column set the real columns expand to fill the width rather
   than leaving a wide empty block on the right. */
#fdtl-grid .row-actions-col { width: 1%; white-space: nowrap; }
/* A clearly visible row highlight, not a barely-there tint. Set on the row so
   band-coloured balance cells keep their own colour (they have their own td
   background); the sticky DATE cell gets its matching hover below. */
.grid tbody tr:hover { background: #cfe0f2; }
/* DATE stays visible while the other 31 columns scroll. */
.sticky-col {
  position: sticky;
  left: 0;
  background: var(--surface);
  z-index: 1;
  font-weight: 600;
}
/* The DATE header's own weight (600, set above for the body's date links)
   is reset back to the same bold every other header gets by default. */
.grid thead .sticky-col { background: #eef2f7; z-index: 3; font-weight: bold; }
.grid tbody tr:hover .sticky-col { background: #cfe0f2; }
/* A right-edge shadow tells the reader the pinned column has content scrolled
   off to its right. app.js adds is-scrolled-x once the grid is scrolled, so the
   shadow is absent when nothing is hidden -- no false affordance at rest. The
   negative spread keeps the shadow on the right edge only. */
.grid-scroll.is-scrolled-x .sticky-col {
  box-shadow: 6px 0 6px -4px rgba(15, 25, 40, .18);
  clip-path: inset(0 -10px 0 0);
}
.date-link {
  background: none;
  border: none;
  font: inherit;
  font-weight: 600;
  color: var(--accent);
  cursor: pointer;
  text-decoration: underline;
  padding: 0;
}

/* DETAILS is a wide column holding the whole leg-by-leg breakdown in the day's
   own row, like the reference vendor's Detail column. It wraps (the rest of the
   grid does not) and is wide enough to hold the labelled fields without the
   row scrolling sideways to read them. */
.col-details {
  white-space: normal !important;
  text-align: left !important;
  vertical-align: top;
  /* width:1% makes an auto-layout table shrink this column to its content,
     rather than stretching it wide and leaving empty space to the right of the
     cards. The card content sets the real width; the slack goes to the trailing
     columns instead of opening a gap inside this cell. */
  width: 1%;
  /* Capped so the card can't claim more than it needs -- the reclaimed width
     goes to REMARK instead, which is capped wider to take it. Wide enough for
     the crew-grid's own columns (name+start+switched+total) plus padding, and
     for the six-column Total FTL/DP/FDP table (2026-09-01, client: widen this
     column) so its cells don't cramp against each other. */
  max-width: 40rem;
}
/* The DP ON/DP OFF-only case (no sector card to top-align against) --
   vertically centred like every other cell in the row, not pinned to the
   top (2026-09-01, client: "keep it in middle"). */
.col-details.col-details-compact { vertical-align: middle; }
/* Every cell in a detail row sits at the top, so DATE and the totals line up
   with the first line of the breakdown rather than floating in the middle of a
   tall cell. */
.grid tr.row-with-detail > td { vertical-align: top; }
/* A DGCA violation (over a limit / days-off, CAR para 7): outline the row with a
   dark-red box instead of flooding it red -- a solid red row was too heavy. A
   run of consecutive flagged rows reads as ONE box: side rails on every row, and
   a single top and bottom edge for the run (no divider inside the group). */
.grid tr.row-violation > td { background: transparent; }
.grid tr.row-violation > td:first-child {
  border-left: 3px solid var(--breach-ink);
  font-weight: 700;
}
.grid tr.row-violation > td:last-child { border-right: 3px solid var(--breach-ink); }
/* Top edge: recolour the divider above the run's first row (or a real top border
   if the run starts at the very first data row). */
.grid tbody tr:not(.row-violation):has(+ tr.row-violation) > td { border-bottom: 3px solid var(--breach-ink); }
.grid tbody tr.row-violation:first-child > td { border-top: 3px solid var(--breach-ink); }
/* Bottom edge: the last row of the run. */
.grid tbody tr.row-violation:not(:has(+ tr.row-violation)) > td { border-bottom: 3px solid var(--breach-ink); }
/* On the blue "check this row" fill the accent-blue date link reads poorly, so
   darken it to ink; over-limit rows are unfilled now, so their link stays blue. */
.grid tr.row-anomaly .date-link { color: var(--ink); }
.detail-panel { padding: .1rem 0; }
.detail-loading, .detail-error { margin: 0; font-size: .8rem; color: var(--ink-soft); }
.detail-error { color: var(--breach-ink, #8A1F1F); }

/* Read-only day breakdown. */
.detail-header {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: .2rem .9rem;
  margin: 0 0 1rem;
  font-size: .84rem;
}
.detail-header dt { color: var(--ink-soft); font-size: .72rem; text-transform: uppercase; }
.detail-header dd { margin: 0 0 .5rem; font-weight: 600; }
.detail-table { white-space: nowrap; }
.detail-table th[scope="row"] { font-weight: 600; }
.detail-table td, .detail-table th { text-align: center; }
.detail-table th[scope="row"] { text-align: left; }
.detail-callsign {
  display: flex;
  gap: .4rem;
  align-items: baseline;
  margin: 0 0 .55rem;
  font-size: .85rem;
}
.detail-callsign-label {
  font-size: .64rem;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--ink-muted, #5B6472);
}
.detail-callsign strong { font-variant-numeric: tabular-nums; letter-spacing: .02em; }
.detail-frb-label { margin-left: 1.1rem; }

/* DUTY TYPE: let a combined name ("GRD RUN + MEETING") wrap to a second line
   instead of forcing the column wide -- that reclaimed room goes to REMARK. */
.grid th[data-col="DUTY TYPE"], .grid td[data-col="DUTY TYPE"] {
  white-space: normal;
  max-width: 7rem;
}
/* On the FDTL grid and the Flight Data grid, DUTY TYPE matches the DATE
   column's own width (6rem) rather than its own narrower cap -- otherwise a
   combined name ("GRD RUN + MEETING") wraps to three lines while DATE next
   to it sits on one. */
#fdtl-grid thead th.sticky-col,
#flight-grid thead th.sticky-col {
  min-width: 6rem;
}
#fdtl-grid th[data-col="DUTY TYPE"], #fdtl-grid td[data-col="DUTY TYPE"],
#flight-grid th[data-col="DUTY TYPE"], #flight-grid td[data-col="DUTY TYPE"] {
  /* Wide enough for the longest single-word label ("ADMINISTRATIVE") to sit
     on one line without wrapping or breaking mid-word (2026-09-02) -- a
     combined name ("GRD RUN + MEETING") still wraps at its own spaces past
     this width, exactly as before, since it has somewhere to break. */
  width: 8rem;
  min-width: 8rem;
  max-width: 8rem;
  overflow-wrap: break-word;
  word-break: break-word;
}
/* REMARK is free text and operators write full notes here, so give it a solid
   minimum width; still capped so one long note cannot stretch the row. Given
   back some of that width -- DETAILS needed more of it, to stop its own TOTAL
   column clipping. */
.grid td[data-col="REMARK"], .grid th[data-col="REMARK"] {
  white-space: normal;
  text-align: left;
  min-width: 13rem;
  max-width: 20rem;
  color: var(--ink-soft);
  font-size: .78rem;
}
/* The column's notes stay left-aligned (readable free text); only the header
   word itself is centred, matching every other column header. */
.grid th[data-col="REMARK"] { text-align: center; }
.detail-empty { text-align: left; padding: .3rem 0 .4rem; font-size: .8rem; }
/* DP ON/DP OFF for a non-flying duty with no sectors (ADMIN, STANDBY,
   TRAINING, MEETING, AUDIT, POSITIONING and the like) -- shown in place of
   the empty state whenever the day actually has those times. No padding of
   its own (2026-09-01, client: it read as floating below the DUTY TYPE cell,
   not on the same line) -- the cell's own .grid td padding already applies,
   so adding more here just pushed this one line down against a plain
   ADMINISTRATIVE label that has no such extra offset. */
.dp-only-detail {
  text-align: left;
  margin: 0;
  font-size: .82rem;
  font-weight: 600;
}
.dp-only-label { color: var(--ink-soft); font-weight: 400; margin-right: .2rem; }
.dp-only-sep { margin: 0 .5rem; color: var(--ink-soft); }

/* DOCUMENT column: attachments per day, with a compact upload control. Narrow
   and top-aligned so it hugs its buttons instead of ballooning to fill a tall
   flight row. */
.grid td.col-document {
  white-space: normal;
  text-align: left;
  vertical-align: top;
  width: 6.5rem;
}
.doc-cell { display: flex; flex-direction: column; gap: .25rem; align-items: flex-start; }
/* Extra-small button for the dense DOCUMENT cell. */
.btn-xs { padding: .18rem .5rem; font-size: .72rem; font-weight: 600; line-height: 1.3; }
.doc-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: .2rem; }
.doc-item { display: flex; align-items: center; gap: .35rem; }
.doc-badge {
  flex: 0 0 auto;
  font-size: .58rem;
  font-weight: 700;
  letter-spacing: .04em;
  padding: .08rem .3rem;
  border-radius: 3px;
  color: #fff;
}
/* PDF and video get distinct badges; neither reuses a reserved status colour. */
.doc-badge-pdf { background: #5a3fb8; }
.doc-badge-video { background: #0f6ca8; }
.doc-badge-file { background: var(--ink-soft); }
.doc-name {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--accent);
  font-size: .76rem;
}
.doc-delete-form { margin: 0; flex: 0 0 auto; }
.doc-delete-form .btn-remove { width: 1.25rem; height: 1.25rem; font-size: .9rem; }

/* The native file input reads "Choose File — No file chosen" and is ~200px
   wide, which is what made the cell look like a big empty box. Replace it with
   a small styled label wrapping a transparent input: clicking the label still
   opens the picker with no JavaScript, and app.js fills in the chosen name. */
.doc-upload-form { display: flex; align-items: center; gap: .3rem; flex-wrap: wrap; }
.doc-file-label { position: relative; overflow: hidden; }
.doc-file-input {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  opacity: 0;
  cursor: pointer;
  font-size: 0;
}
.doc-file-name {
  font-size: .68rem;
  color: var(--ink-soft);
  max-width: 9.5rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.doc-empty { display: inline-block; font-size: .74rem; }

/* Attachment viewer dialog. Opened with showModal(), so it lives in the top
   layer and the grid's scroll box never clips it. Full filenames live here,
   which is what keeps the grid cell compact. */
.doc-dialog {
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 0;
  width: min(34rem, 92vw);
  box-shadow: 0 24px 60px rgba(16, 24, 40, .32);
  color: var(--ink);
}
.doc-dialog::backdrop { background: rgba(12, 20, 32, .5); }
.doc-dialog-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: .7rem 1rem;
  border-bottom: 1px solid var(--line);
  font-size: .95rem;
}
.doc-dialog .doc-list { padding: .6rem 1rem .85rem; gap: .35rem; }
.doc-dialog .doc-item {
  gap: .5rem;
  padding: .35rem 0;
  border-bottom: 1px solid var(--line-soft);
}
.doc-dialog .doc-item:last-child { border-bottom: none; }
/* Full names in the dialog -- the ellipsis clamp was only for the grid cell. */
.doc-dialog .doc-name {
  white-space: normal;
  overflow: visible;
  text-overflow: clip;
  font-size: .82rem;
}
.doc-size { margin-left: auto; color: var(--ink-soft); font-size: .72rem; white-space: nowrap; }
.is-hidden { display: none !important; }

/* Toasts: transient confirmations for in-place actions (upload/delete), so the
   page never has to reload to tell the operator what happened. */
.toast-stack {
  position: fixed;
  right: 1rem;
  bottom: 1rem;
  z-index: 200;
  display: flex;
  flex-direction: column;
  gap: .5rem;
}
.toast {
  min-width: 9rem;
  max-width: 22rem;
  padding: .55rem .9rem;
  border-radius: 6px;
  color: #fff;
  background: var(--ink);
  box-shadow: 0 10px 28px rgba(16, 24, 40, .28);
  font-size: .85rem;
  opacity: 0;
  transform: translateY(.5rem);
  transition: opacity .18s ease, transform .18s ease;
}
.toast.show { opacity: 1; transform: translateY(0); }
.toast-success { background: #1c5c31; }
.toast-error { background: var(--breach-ink); }
@media (prefers-reduced-motion: reduce) { .toast { transition: none; } }

/* Sector breakdown: one card per sector. A tinted header strip carries the
   number badge and route; the crew sit inside with their own start-up,
   switch-off and total. Times are against each name so a seat that ever
   differs is shown correctly, not assumed equal. */
.sector-list {
  display: flex;
  flex-direction: column;
  gap: .35rem;
  /* The DETAILS column now shrinks to fit (see .col-details), so the list can
     fill it -- no cap needed. */
  max-width: 100%;
}
.sector-card {
  border: 1px solid var(--line);
  border-radius: 6px;
  overflow: hidden;
  background: var(--surface);
}
.sector-card-head {
  display: flex;
  gap: .55rem;
  align-items: flex-start;
  padding: .35rem .5rem;
  background: #eef4fb;
  border-bottom: 1px solid var(--line);
}
/* Type badge: a plane for a flight leg, a wheel for a ground run, with the
   sector number as a small corner marker. */
.sector-icon {
  position: relative;
  flex: 0 0 auto;
  width: 1.6rem;
  height: 1.6rem;
  border-radius: 50%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  margin-top: .05rem;
}
.sector-icon svg { width: .95rem; height: .95rem; display: block; }
/* The flight badge now holds a coloured aircraft emoji (helicopter / aeroplane),
   so it needs no blue fill; the ground-run badge keeps its wheel SVG on grey. */
.sector-emoji { font-size: 1.1rem; line-height: 1; }
.sector-icon.is-flight { background: transparent; }
.sector-icon.is-flight::after { box-shadow: 0 0 0 1.5px #fff; }
.sector-icon.is-ground { background: #e7e7e3; color: var(--ink-soft); }
.sector-icon::after {
  content: attr(data-seq);
  position: absolute;
  top: -.3rem;
  right: -.3rem;
  min-width: .85rem;
  height: .85rem;
  padding: 0 .12rem;
  border-radius: 999px;
  background: var(--accent);
  color: #fff;
  font-size: .58rem;
  font-weight: 700;
  line-height: .85rem;
  text-align: center;
  font-variant-numeric: tabular-nums;
  box-shadow: 0 0 0 1.5px #eef4fb;
}
.sector-icon.is-ground::after { background: var(--ink-soft); }
.sector-headline {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  align-items: baseline;
  gap: .06rem;
  min-width: 0;
  padding-top: .05rem;
}
.sector-route {
  font-weight: 700;
  font-size: .76rem;
  letter-spacing: .02em;
  color: var(--ink);
}
.route-arrow { color: var(--accent); font-weight: 700; margin: 0 .1rem; }
/* align-items: center, NOT baseline -- baseline alignment in a grid inflates
   each row track to reserve space above and below the baseline, which is what
   made the rows look far apart. A tight line-height keeps them compact. */
.crew-grid {
  display: grid;
  /* Name column sizes to its content, not a stretchy fraction -- that stretch
     is what opened the gap between the crew names and the time columns. Tracks
     pack to the start; any slack trails harmlessly on the right. Capped at
     8rem now that .col-details itself is capped, so a long name wraps instead
     of pushing the card past the column's own max-width. */
  grid-template-columns: minmax(7rem, 8rem) 4rem 5rem 3.5rem;
  justify-content: start;
  gap: 0 1.1rem;
  align-items: center;
  padding: .2rem .5rem .25rem;
  font-size: .7rem;
  line-height: 1.25;
  font-variant-numeric: tabular-nums;
}
.cg-h {
  font-size: .58rem;
  letter-spacing: .03em;
  text-transform: uppercase;
  color: var(--ink-soft);
  border-bottom: 1px solid var(--line-soft);
  padding-bottom: .08rem;
  margin-bottom: .06rem;
}
.cg-name { text-transform: uppercase; }
.crew-role { color: var(--ink-soft); font-weight: 600; text-transform: none; }
.cg-total { font-weight: 700; }

/* Total FTL, and each crew pilot's own DP ON/FDP ON/FDP OFF/DP OFF, one row
   per pilot (2026-09-01, client instruction: a single decent-looking table,
   not a summary box plus a separate figure list). Deliberately the same
   neutral accent tint as the rest of this card, not the workbook's yellow:
   yellow is a reserved status colour here (the Caution band), and this is
   not a limit warning. */
.ftl-table {
  margin-top: .3rem;
  border-collapse: collapse;
  font-size: .78rem;
}
.ftl-table th,
.ftl-table td {
  border: 1px solid var(--line-soft);
  padding: .22rem .6rem;
  text-align: left;
  white-space: nowrap;
}
.ftl-table thead th {
  background: #eef4fb;
  color: var(--accent);
  font-weight: 700;
  font-size: .68rem;
  letter-spacing: .03em;
  text-transform: uppercase;
}
.ftl-table tbody th {
  background: #f7f9fc;
  font-weight: 700;
  text-transform: uppercase;
}
.ftl-table td { font-variant-numeric: tabular-nums; font-weight: 600; }
.ftl-name { text-transform: uppercase; }


/* Limit bands. Colour is paired with a title attribute in the markup so it is
   never the sole carrier of a warning. */
.band-breach { background: var(--breach-bg); color: var(--breach-ink); font-weight: 700; }
.band-orange { background: var(--orange-bg); color: var(--orange-ink); font-weight: 600; }
.band-yellow { background: var(--yellow-bg); color: var(--yellow-ink); }
.row-anomaly .sticky-col { background: var(--anomaly-bg); }
.row-anomaly td { background: var(--anomaly-bg); }

/* Report header: the Download-PDF button sits beside the "All reports" link. */
.report-head-actions { display: flex; gap: .5rem; align-items: center; flex-wrap: wrap; }

/* Crew-insights filter: time period, measure and the pilots multi-select laid
   out as one tidy row, labels stacked above each control. */
.viz-filter { display: flex; align-items: flex-end; gap: .85rem 1rem; flex-wrap: wrap; }
.viz-filter .column-picker > summary { min-width: 8rem; justify-content: space-between; }

/* The pilots dropdown is a short list, not the 35-column grid the picker panel
   was sized for -- so make it a compact, single-column, scrollable list that
   opens under its button. */
[data-pilot-picker] .picker-panel {
  width: 15rem;
  left: 0;
  right: auto;
  padding: .5rem;
}
[data-pilot-picker] .picker-list {
  display: block;
  max-height: 15rem;
  overflow-y: auto;
  gap: 0;
}
[data-pilot-picker] .picker-item {
  width: 100%;
  font-size: .82rem;
  padding: .3rem .35rem;
}
[data-pilot-picker] .picker-actions { justify-content: flex-start; gap: 1rem; }
[data-pilot-picker] [data-picker-apply],
[data-pilot-picker] .picker-panel > button[type="submit"] { margin-top: .5rem; width: 100%; }

/* Licence numbers for the pilot in view, copied from their Training. */
.pilot-licences { margin: .2rem 0 0; font-size: .82rem; color: var(--ink); }
.pilot-licence { white-space: nowrap; }
/* The Flight Data variant sits under the filter row as its own strip. */
.pilot-licences-bar { margin: .75rem 0 0; padding-top: .6rem; border-top: 1px solid var(--line-soft); }

/* Show-times-in UTC / IST toggle under the filter. */
.tz-toggle { display: flex; align-items: center; gap: .5rem; margin: .6rem 0 0; flex-wrap: wrap; }
.tz-hint { font-size: .76rem; color: var(--ink-soft); }

/* VVIP-flying currency note under the operator name. Green when current, red
   when not -- these are status colours, always paired with the words. */
.vvip-note {
  margin: .5rem 0 0; padding: .45rem .7rem; border-radius: 6px;
  font-size: .82rem; max-width: 68ch;
}
.vvip-note strong { display: block; margin-bottom: .15rem; }
.vvip-ok { background: #e2f0e7; color: #1c4d2e; border: 1px solid #b6d8c2; }
.vvip-warn { background: var(--breach-bg); color: var(--breach-ink); border: 1px solid var(--breach-ink); }
.vvip-reasons { margin: .2rem 0 0; padding-left: 1.15rem; }

.legend {
  display: flex;
  gap: .5rem 1.25rem;
  flex-wrap: wrap;
  align-items: center;
  padding: 0 1.1rem .7rem;
  font-size: .78rem;
  color: var(--ink-soft);
}
/* When the legend sits directly beneath the filter row (the FDTL grid), it
   otherwise crowds the controls above it. Lift it off with a little space and a
   hairline divider so it reads as its own strip rather than a run-on line. */
.legend-spaced {
  margin-top: 1rem;
  padding-top: .85rem;
  border-top: 1px solid var(--line-soft);
}
.legend-title { flex-basis: 100%; font-weight: 600; color: var(--ink); }
.legend-item { display: inline-flex; align-items: center; gap: .35rem; }
.legend-item strong { color: var(--ink); font-weight: 600; }
.swatch {
  width: 14px;
  height: 14px;
  border-radius: 3px;
  border: 1px solid var(--line);
  display: inline-block;
}
.swatch-anomaly { background: var(--anomaly-bg); }

/* --------------------------------------------------------------- modal --- */
.modal-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(12, 20, 32, .55);
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding: 2rem 1rem;
  overflow-y: auto;
  z-index: 50;
}
.modal-backdrop[hidden] { display: none; }
.modal {
  position: relative;   /* containing block for the over-limit popup overlay */
  background: var(--surface);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  width: 100%;
  /* Wide enough for the Sectors table to fit without a horizontal scrollbar,
     but capped to the viewport so it never overflows a smaller screen. */
  max-width: min(1480px, 97vw);
}
.modal-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 1rem 1.25rem;
  border-bottom: 1px solid var(--line);
}
.modal-head h2 { margin: 0; }
.modal-close {
  background: none;
  border: none;
  font-size: 1.6rem;
  line-height: 1;
  cursor: pointer;
  color: var(--ink-soft);
}
.modal-body { padding: 1.25rem; max-height: 70vh; overflow-y: auto; }
.modal-narrow { max-width: 680px; }

/* Sign-in expiry alert. Uses the alertdialog role and is centred, so it is
   dealt with rather than scrolled past. */
.expiry-backdrop { align-items: center; }
.expiry-summary { margin: 0 0 .85rem; font-size: .95rem; }
.expiry-count-expired { color: var(--breach-ink); }
.expiry-table { font-size: .82rem; }
.expiry-table .text-left { text-align: left; }

/* Per-pilot expiry grid (2026-08-20): cards flow to fit the modal's width
   instead of one long scrolling table -- the point of this layout is to
   keep the whole sign-in alert on screen without scrolling. */
.expiry-pilot-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
  gap: .85rem;
  margin-bottom: 1rem;
}
.expiry-pilot-card {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: .6rem;
}
.expiry-pilot-name {
  margin: 0 0 .5rem;
  font-size: .88rem;
  text-transform: uppercase;
  text-align: center;
}
.expiry-bucket-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: .5rem;
}
.expiry-bucket {
  border: 1px solid var(--line);
  border-radius: 4px;
  padding: .35rem .5rem;
  font-size: .78rem;
}
.expiry-bucket > details > summary,
.expiry-bucket > span:first-child {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .4rem;
}
.expiry-bucket > details > summary {
  cursor: pointer;
  list-style: none;
  user-select: none;
}
.expiry-bucket > details > summary::-webkit-details-marker { display: none; }
.expiry-bucket-label { font-weight: 700; letter-spacing: .02em; }
.expiry-bucket-count { font-weight: 700; font-variant-numeric: tabular-nums; }
.expiry-bucket-count-zero { color: var(--ink-soft); font-weight: 400; }
.expiry-bucket-items {
  list-style: none;
  margin: .4rem 0 0;
  padding: .4rem 0 0;
  border-top: 1px solid rgba(0, 0, 0, .08);
  display: flex;
  flex-direction: column;
  gap: .25rem;
  font-size: .74rem;
}
.expiry-bucket-items li { display: flex; justify-content: space-between; gap: .5rem; }
.expiry-bucket-expired { background: var(--breach-bg); }
.expiry-bucket-expired .expiry-bucket-label,
.expiry-bucket-expired .expiry-bucket-count { color: var(--breach-ink); }
.expiry-bucket-critical { background: var(--orange-bg); }
.expiry-bucket-critical .expiry-bucket-label,
.expiry-bucket-critical .expiry-bucket-count { color: var(--orange-ink); }
.expiry-bucket-caution { background: var(--yellow-bg); }
.expiry-bucket-caution .expiry-bucket-label,
.expiry-bucket-caution .expiry-bucket-count { color: var(--yellow-ink); }
.expiry-bucket-plan { background: var(--surface-muted, #f3f5f8); }
.modal-foot form { margin: 0; }
.modal-foot {
  display: flex;
  justify-content: flex-end;
  gap: .6rem;
  padding: 1rem 1.25rem;
  border-top: 1px solid var(--line);
}
/* Over-limit alert shown as a centred popup over the entry modal (see
   _entry_modal.html), so it is seen the instant a save comes back over-limit
   instead of scrolled past. Positioned within .modal (which is relative). */
.limit-popup-backdrop {
  position: absolute; inset: 0; z-index: 20;
  background: rgba(15, 22, 30, .5);
  display: grid; place-items: center; padding: 1.25rem;
  border-radius: var(--radius);
}
.limit-popup {
  background: var(--surface); border-radius: var(--radius);
  box-shadow: var(--shadow); width: 100%; max-width: 440px;
  max-height: 90%; overflow-y: auto; padding: 1.5rem;
}
.limit-popup h3 { margin: 0 0 .7rem; color: var(--orange-ink); font-size: 1.12rem; }
.limit-popup .error-list { margin: 0 0 .85rem; padding-left: 1.15rem; }
.limit-popup .hint { margin: 0; }
.limit-popup-foot { display: flex; justify-content: flex-end; margin-top: 1.25rem; }
/* The delete-choice popup can carry several buttons (all + one per pilot +
   cancel), so it wraps and stacks them left-aligned rather than one row. */
.delete-choice-foot { flex-wrap: wrap; gap: .5rem; justify-content: flex-start; }

fieldset {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: .85rem 1rem;
  margin: 0 0 1rem;
}
fieldset[hidden] { display: none; }
legend { font-weight: 600; font-size: .85rem; padding: 0 .4rem; }
.case-picker { display: block; }
.case-option, .radio-inline {
  display: flex;
  align-items: flex-start;
  gap: .5rem;
  padding: .3rem 0;
  cursor: pointer;
}
.radio-row { display: flex; gap: 1.25rem; }
.radio-inline { align-items: center; }
.checkbox-row { display: flex; align-items: center; gap: .5rem; margin: .5rem 0; cursor: pointer; }
.checkbox-row input { width: auto; }
.grid-2 { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: .75rem; }

/* Read-only summary of what is already stored for the date. */
.recorded-panel { background: #f7f9fc; }
.recorded-row { padding: .5rem 0; border-bottom: 1px solid var(--line-soft); }
.recorded-row:last-of-type { border-bottom: none; }
.recorded-row-anomaly { background: var(--anomaly-bg); border-radius: 4px; padding: .5rem; }
.recorded-head { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; }
.recorded-duty { color: var(--ink-soft); font-size: .85rem; }
.tag {
  font-size: .68rem;
  text-transform: uppercase;
  letter-spacing: .04em;
  background: var(--line);
  color: var(--ink-soft);
  padding: .1rem .4rem;
  border-radius: 999px;
}
/* A planned row is a projection, marked so it reads distinctly from flown
   history. A tinted background plus the label -- never colour alone. */
.tag-planned {
  background: #e5edff;
  color: #234a9e;
  font-weight: 700;
  margin-right: .35rem;
}
.grid tr.row-planned > td { background: #f5f8ff; font-style: italic; }
.grid tr.row-planned:hover > td { background: #eef3ff; }
.grid tr.row-planned > .sticky-col { background: #f5f8ff; font-style: normal; }

/* Daily Task: a blank future date added via "+ Add future date" (Aeroplane
   only, 2026-08-23, client: "highlight with light grey colour"). Separate
   from row-planned above, which is the FDTL tab's own colour for an actual
   saved is_planned=True day. */
.grid tr.row-future-date > td { background: #f0f0f0; }
.grid tr.row-future-date:hover > td { background: #e6e6e6; }
.grid tr.row-future-date > .sticky-col { background: #f0f0f0; }
.grid tr.row-future-date:hover > .sticky-col { background: #e6e6e6; }

/* Inline scenario planning in the FDTL grid. */
.row-actions-col { width: 2rem; }
.grid td.row-actions { text-align: center; padding: .2rem .3rem; }
.row-planned-new > td { background: #eef3ff; font-style: normal; }
.row-planned-new input {
  width: 100%;
  min-width: 4.5rem;
  padding: .3rem .35rem;
  font-size: .8rem;
  font-variant-numeric: tabular-nums;
}
.row-planned-new .field-error { white-space: normal; font-size: .68rem; }
.plan-actions { display: flex; gap: .6rem; align-items: center; margin-top: .75rem; }
.plan-standard-note {
  font-size: .72rem;
  color: var(--ink-soft);
  text-transform: uppercase;
  letter-spacing: .03em;
}

/* Floating time calculator -- a corner button on every app page that opens a
   scratch calculator. Chain add/subtract of HH:MM durations, = for the total. */
/* Sits under the sidebar nav (160px wide) rather than floating over the
   bottom-right of the page content, where it could sit on top of a save/
   download button. */
.time-calc-fab { position: fixed; left: 1.25rem; bottom: 1.25rem; z-index: 120; }
.time-calc-toggle {
  width: 3rem;
  height: 3rem;
  border-radius: 50%;
  border: none;
  background: var(--brand);
  color: #fff;
  font-size: 1.3rem;
  cursor: pointer;
  box-shadow: 0 6px 18px rgba(16, 24, 40, .28);
  display: flex;
  align-items: center;
  justify-content: center;
}
.time-calc-toggle:hover { background: var(--brand-dark); }
.time-calc-box {
  position: absolute;
  left: 0;
  bottom: calc(100% + .6rem);
  width: 17rem;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 10px;
  box-shadow: 0 16px 40px rgba(16, 24, 40, .28);
  padding: .75rem;
}
.time-calc-box[hidden] { display: none; }
.time-calc-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: .5rem;
  font-size: .9rem;
}
/* Two tools in one box: Calculator and IST<->UTC converter. */
.tool-tabs { display: flex; gap: .3rem; margin-bottom: .6rem; }
.tool-tab {
  flex: 1 1 0;
  border: 1px solid var(--line);
  background: #fff;
  border-radius: 5px;
  padding: .3rem .4rem;
  font: inherit;
  font-size: .78rem;
  cursor: pointer;
  color: var(--ink-soft);
}
.tool-tab.is-active { background: var(--brand); border-color: var(--brand); color: #fff; font-weight: 600; }
.conv-dirs { display: flex; gap: .9rem; margin-top: .5rem; font-size: .82rem; }
.conv-dirs .pick-option { display: inline-flex; align-items: center; gap: .35rem; }
.time-calc-display {
  background: #0f1720;
  color: #fff;
  border-radius: 8px;
  padding: .45rem .7rem .55rem;
  margin-bottom: .5rem;
  text-align: right;
  font-variant-numeric: tabular-nums;
}
.time-calc-expr {
  min-height: 1rem;
  font-size: .74rem;
  color: #9fb0c3;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.time-calc-total { font-size: 1.6rem; font-weight: 700; line-height: 1.2; }
.time-calc-input {
  width: 100%;
  text-align: right;
  font-size: 1rem;
  font-variant-numeric: tabular-nums;
  margin-bottom: .5rem;
}
.time-calc-input.is-bad { border-color: var(--breach-ink); background: var(--breach-bg); }
.time-calc-keys { display: grid; grid-template-columns: repeat(4, 1fr); gap: .4rem; }
.time-calc-keys .btn { padding: .5rem 0; font-size: 1.05rem; }
.time-calc-fab .hint { margin: .5rem 0 0; font-size: .72rem; }
/* Each group is its own fixed three-column row. auto-fit was the bug here:
   it flowed dt/dd pairs into whatever slots were free, so DP OFF could land
   on the line below DP ON. A pair the reader compares must never wrap apart. */
.recorded-groups { margin: .4rem 0 0; font-size: .82rem; }
.recorded-group {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: .1rem 1.25rem;
  margin: 0;
  padding: .18rem 0;
}
.recorded-group + .recorded-group { border-top: 1px solid var(--line-soft); }
.recorded-pair {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: baseline;
  gap: .5rem;
  padding: .1rem 0;
}
.recorded-pair dt {
  color: var(--ink-soft);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.recorded-pair dd {
  margin: 0;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  text-align: right;
}
.recorded-pair.is-empty dd { font-weight: 400; color: var(--ink-soft); }
/* A day total that is at or over its 24-hour limit is shaded like the grid --
   rendered as a small pill so it reads as a deliberate caution, not a stray fill. */
.recorded-pair dd.band-breach,
.recorded-pair dd.band-orange,
.recorded-pair dd.band-yellow {
  padding: 0 .4rem;
  border-radius: 3px;
}
/* "Next available" (CAR 8.1) called out on its own -- bold, highlighted,
   one size step up from the rest of the panel (2026-09-01, client
   instruction) -- so it reads as the one figure worth a scheduler's second
   glance, on the same line as BA time. Blue rather than the yellow/orange
   band colours: this is not a caution state, those are reserved for a
   balance close to its limit. */
.recorded-pair.recorded-next-available {
  background: rgba(21, 101, 192, .1);
  border-radius: 4px;
  padding: .18rem .4rem;
  margin: .15rem -.4rem 0;
}
.recorded-pair.recorded-next-available dt,
.recorded-pair.recorded-next-available dd {
  font-size: 1.14em;
  font-weight: 700;
}
.recorded-pair.recorded-next-available dt { color: var(--brand); }
.recorded-pair.recorded-next-available dd { color: var(--brand-dark); }

@media (max-width: 720px) {
  .recorded-group { grid-template-columns: minmax(0, 1fr); }
}
.recorded-anomaly { margin: .4rem 0 0; font-size: .8rem; color: var(--orange-ink); }

/* Why a recorded day is red: the breached DGCA limits, named in the panel --
   including rolling-window breaches the day's own totals never reveal. */
.recorded-row-breach { background: var(--breach-bg); border-radius: 4px; padding: .5rem; }
.tag-breach { background: var(--breach-ink); color: #fff; }
.recorded-breaches {
  margin: .45rem 0 0; padding: .5rem .6rem; border-radius: 4px;
  background: #fff; border: 1px solid var(--breach-ink);
  color: var(--breach-ink); font-size: .82rem;
}
.recorded-breaches ul { margin: .3rem 0 0; padding-left: 1.15rem; }
.recorded-breaches li { margin: .15rem 0; }
.recorded-breach-car { color: var(--ink-soft); font-size: .9em; }

.entry-table { width: 100%; border-collapse: collapse; font-size: .74rem; }
.entry-table th, .entry-table td {
  border: 1px solid var(--line);
  padding: .3rem .4rem;
  text-align: left;
  vertical-align: top;
}
.entry-table thead th { background: #eef2f7; font-size: .64rem; text-transform: uppercase; }
.entry-table select, .entry-table input { min-width: 90px; }

/* Type of duty needs the room -- some labels ("FLIGHT + ADMINISTRATIVE") are
   long enough to clip. Positioning is blank on most rows, so it gives up its
   spare width instead. */
#duty-table .duty-type-cell { width: 30%; }
#duty-table .duty-type-cell select { min-width: 0; width: 100%; }
#duty-table .positioning-cell { width: 70px; min-width: 0; }

/* Sectors is a column-heavy table -- compact its controls, tighten padding and
   cap every column's width (not just its minimum) so the whole row fits the
   modal without a horizontal scrollbar. Scoped to #sector-table only: the
   Duty roster and Split duty tables above keep their normal size. */
#sector-table th, #sector-table td { padding: .15rem .2rem; }
#sector-table select, #sector-table input { min-width: 0; }
#sector-table input[name$="-sequence"] { width: 1.6rem; min-width: 1.6rem; text-align: center; }
/* width (not just min-width) caps these -- a <select> otherwise grows to fit
   its longest option (a long pilot name or duty-type label), which is what
   was forcing the horizontal scrollbar even on a wide screen. */
#sector-table select[name$="-duty_type"] { width: 4.6rem; max-width: 4.6rem; }
#sector-table select[name$="-call_sign"] { width: 6.2rem; max-width: 6.2rem; }
#sector-table select[name$="-pic"],
#sector-table select[name$="-sic"] { width: 5rem; max-width: 5rem; }
#sector-table input[name$="-from_airport"],
#sector-table input[name$="-to_airport"] { width: 3.2rem; min-width: 3.2rem; }
#sector-table input[name$="-frb_no"] { width: 2.4rem; min-width: 2.4rem; }
#sector-table input[name$="-landings"] { width: 2.4rem; min-width: 2.4rem; }
#sector-table select[name$="-day_night"] { width: 3.4rem; max-width: 3.4rem; }
#sector-table .time-text { width: 3.6rem !important; min-width: 3.6rem !important; }
#sector-table .cal-btn { padding: .05rem .15rem; font-size: .64rem; }
#sector-table .col-copy { width: 3rem; }
#sector-table .btn-copy { padding: .1rem .2rem; font-size: .6rem; }
#sector-table .col-remove { width: 1.5rem; }
#sector-table .sector-total { display: inline-block; width: 2.2rem; }
/* Narrow columns force the two-word headers (Flight Type, Day/Night, FRB No.)
   onto two lines, which is expected and keeps the header from re-widening
   the column it labels. */
#sector-table thead th { white-space: normal; line-height: 1.1; }
/* If it still overflows on a narrow screen, only the table scrolls, not the
   whole dialog -- the scrollbar sits right under the table. */
.entry-scroll { overflow-x: auto; }
.entry-table .row-number { text-align: center; }

/* Auto-crew duty types (e.g. SIMULATOR TRAINING): each pilot's own Start Up/
   Switched Off, no shared flight. Sized to its content rather than 100% width
   -- with only five columns, stretching across the full modal would spread
   them apart the same way the Sectors table did before it got the same fix. */
#auto-crew-table { width: auto; }
#auto-crew-table th, #auto-crew-table td { padding: .3rem .5rem; white-space: nowrap; }
#auto-crew-table input[name$="-start_up"],
#auto-crew-table input[name$="-switched_off"] { width: 3.6rem !important; min-width: 3.6rem !important; }
.autocrew-pilot-name { font-weight: 600; }
.autocrew-total { font-variant-numeric: tabular-nums; }

/* 24-hour time entry. Plain text, because a native time input renders as
   AM/PM on an en-IN browser regardless of the document language. */
.time-text {
  min-width: 4.6rem !important;
  width: 4.6rem;
  text-align: center;
  font-variant-numeric: tabular-nums;
  letter-spacing: .02em;
}
.time-text:disabled { background: var(--line-soft); color: var(--ink-soft); }

/* Time + an inline calendar icon that opens a date picker. The date is optional
   (blank = the entry date); set it to move the time to another day. The native
   date input itself is hidden -- the icon triggers it -- so no second line. */
.timefield { display: inline-flex; align-items: center; gap: .2rem; position: relative; }
.cal-btn {
  border: 1px solid var(--line);
  background: #fff;
  border-radius: 4px;
  padding: .05rem .25rem;
  font-size: .8rem;
  line-height: 1.2;
  cursor: pointer;
}
.cal-btn:hover { border-color: var(--accent); background: #eef5fc; }
.cal-btn.has-date { border-color: var(--accent); background: #eef5fc; }
.cal-value {
  font-size: .66rem;
  color: var(--accent);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
/* Hidden but present so the icon can call showPicker() on it. */
.time-date {
  position: absolute;
  left: 0; bottom: 0;
  width: 1px; height: 1px;
  opacity: 0;
  padding: 0; margin: 0; border: 0;
  min-width: 0 !important;
  pointer-events: none;
}

.col-copy { width: 5.2rem; text-align: center; }
.btn-copy {
  border: 1px solid var(--line);
  background: #fff;
  color: var(--accent);
  border-radius: 4px;
  padding: .2rem .45rem;
  font-size: .72rem;
  font-weight: 600;
  cursor: pointer;
  white-space: nowrap;
}
.btn-copy:hover { background: #eef5fc; border-color: var(--accent); }
.btn-copy:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

.pilot-cell select { min-width: 11rem; }
.crew-cell select { min-width: 8rem; }
.crew-other-wrap { margin-top: .25rem; }
.crew-other-wrap input { min-width: 8rem; }

.ftl-total-row td { background: #f7f9fc; }
.ftl-name { color: var(--brand); }

.checkbox-row.is-disabled { opacity: .5; cursor: not-allowed; }
.overwrite-note {
  margin-right: auto;
  font-size: .82rem;
  color: var(--orange-ink);
  align-self: center;
}
#adhoc-registration-wrap { margin-top: .4rem; }

/* Per-row remove control. */
.col-remove { width: 2.2rem; text-align: center; }
.btn-remove {
  border: 1px solid var(--line);
  background: #fff;
  color: var(--ink-soft);
  border-radius: 4px;
  width: 1.6rem;
  height: 1.6rem;
  line-height: 1;
  font-size: 1.05rem;
  cursor: pointer;
  padding: 0;
}
.btn-remove:hover { background: var(--breach-bg); color: var(--breach-ink); border-color: #e2a5a5; }
.btn-remove:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

/* Visually hidden but read by screen readers. */
.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;
}
/* Centre the time boxes together -- the cells default to vertical-align:top,
   which left the padded input boxes sitting unevenly against their labels. */
.dp-cell, .ba-cell { width: 110px; vertical-align: middle; }
.sector-total, .split-total, #total-ftl, #total-split {
  font-variant-numeric: tabular-nums;
  font-weight: 600;
}
/* An out-of-window split-duty break (under 3:00 or 10:00+): flag it as the
   operator types, before the server rejects it. */
.split-total.split-invalid { color: var(--breach-ink); font-weight: 700; }
.split-row-invalid input[name$="break_start"],
.split-row-invalid input[name$="break_end"] { border-color: var(--breach-ink); }

/* ------------------------------------------------------------ training --- */
.training-header {
  display: flex;
  flex-wrap: nowrap;
  gap: 1rem;
}
.training-header .field { flex: 1 1 0; min-width: 0; margin-bottom: 0; }
.training-header .header-actions { flex: 0 0 auto; }
.training-header .name-pair { display: flex; gap: .6rem; }
.training-header .name-sub { display: flex; flex-direction: column; gap: .2rem; min-width: 0; flex: 1; }
.training-header .sub-label {
  font-size: .72rem; font-weight: 600; letter-spacing: .02em;
  color: var(--ink-soft); text-transform: none;
}
.training-header .name-sub input { width: 100%; }
.designation-table { border-collapse: collapse; }
.designation-table td {
  border: 1px solid var(--line);
  padding: .25rem .5rem;
  font-size: .8rem;
}
.designation-table td:last-child { width: 1%; text-align: center; }
.static-value {
  margin: 0;
  padding: .45rem 0;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}
.section-title {
  margin-bottom: .6rem;
  padding-bottom: .35rem;
  border-bottom: 2px solid var(--line);
  font-size: .95rem;
  letter-spacing: .02em;
  text-transform: uppercase;
  color: var(--brand);
}
.training-table { width: 100%; border-collapse: collapse; font-size: .85rem; }
.training-table th, .training-table td {
  border: 1px solid var(--line);
  padding: .35rem .5rem;
  text-align: left;
  vertical-align: middle;
}
.training-table thead th {
  background: #eef2f7;
  font-size: .7rem;
  text-transform: uppercase;
  letter-spacing: .03em;
  color: var(--ink-soft);
  white-space: nowrap;
}
.training-table .item-name { font-weight: 600; width: 30%; }
.training-table input[type="text"] { min-width: 8rem; }
.training-table .col-status { width: 9rem; text-align: center; }
/* Shrink-to-content: width:1% makes an auto-layout table pull this column in to
   just its button, so the slack goes to the name/date columns instead of
   opening a gap beside Attach. */
.training-table .col-document,
.training-table .col-document-h { width: 1%; white-space: nowrap; }
.training-table .col-document { vertical-align: top; }
.training-table .col-actions { width: 7rem; text-align: right; white-space: nowrap; }

/* Edit-on-demand, shared by the pilot header and every qualification row (both
   carry data-edit-scope): plain values until Edit swaps in the inputs. New
   Recurrent Training rows have inputs directly, so they need no toggle. */
[data-edit-scope] .cell-edit,
[data-edit-scope] .qual-edit-actions { display: none; }
[data-edit-scope].is-editing .cell-view,
[data-edit-scope].is-editing [data-qual-edit] { display: none; }
[data-edit-scope].is-editing .cell-edit { display: block; }
[data-edit-scope].is-editing .qual-edit-actions { display: inline-flex; gap: .4rem; }
.qual-row-new .qual-name-input { min-width: 10rem; }
.training-header .cell-view { display: block; padding: .35rem 0; font-weight: 500; }
.training-header .header-actions { align-self: end; }

/* Row tinting mirrors the grid's limit banding, so "needs attention" looks
   the same everywhere in the application. */
.training-table tr.state-expired > .item-name { background: var(--breach-bg); }
.training-table tr.state-due > .item-name { background: var(--orange-bg); }

.status-pill {
  display: inline-block;
  padding: .1rem .5rem;
  border-radius: 999px;
  font-size: .72rem;
  font-weight: 600;
  white-space: nowrap;
}
.status-expired { background: var(--breach-bg); color: var(--breach-ink); }
.status-due { background: var(--orange-bg); color: var(--orange-ink); }
.status-valid { background: #dcf0e2; color: #1c5c31; }
.status-lifetime { background: #e3f0fb; color: #0d3c61; }
.status-unknown { background: var(--line-soft); color: var(--ink-soft); }

.training-actions {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  padding: .5rem 0 1.5rem;
}

/* --------------------------------------------------------------- misc --- */
.change-list { margin: .3rem 0; padding-left: 1.1rem; font-size: .8rem; }
.pagination { display: flex; gap: 1rem; align-items: center; justify-content: center; padding: .75rem; }
/* -------------------------------------------------------- visualization --- */
/* Palette validated against this surface (#fff) -- see apps/operations/charts.py */
.kpi-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: .75rem;
  margin-bottom: 1.1rem;
}
.kpi-tile {
  background: var(--surface);
  border: 1px solid var(--line);
  border-left: 3px solid var(--line);
  border-radius: var(--radius);
  padding: .8rem .9rem;
}
.kpi-label {
  margin: 0;
  font-size: .7rem;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--ink-soft);
}
/* Proportional figures for standalone values; tabular only where columns align. */
.kpi-value { margin: .25rem 0 .15rem; font-size: 1.6rem; font-weight: 600; line-height: 1.1; }
.kpi-note { margin: 0; font-size: .74rem; color: var(--ink-soft); }
/* Status is carried by a border and a label, never by the value's colour. */
.kpi-good { border-left-color: #0ca30c; }
.kpi-warning { border-left-color: #fab219; }
.kpi-critical { border-left-color: #d03b3b; }

.chart-title { margin: 0 0 .15rem; font-size: 1rem; }
.chart-sub { margin: 0 0 .75rem; font-size: .8rem; color: var(--ink-soft); max-width: 62ch; }
.chart-frame { width: 100%; overflow-x: auto; }
.chart-frame .viz { width: 100%; min-width: 520px; height: auto; display: block; }

.chart-legend-row {
  display: flex;
  gap: 1.25rem;
  flex-wrap: wrap;
  margin-bottom: .5rem;
  font-size: .78rem;
  color: var(--ink-soft);
}
.legend-key { display: inline-flex; align-items: center; gap: .4rem; }
.legend-swatch { width: 11px; height: 11px; border-radius: 3px; display: inline-block; }
.legend-line {
  width: 16px;
  height: 0;
  border-top: 2px dashed #d03b3b;
  display: inline-block;
}

.chart-split {
  display: grid;
  grid-template-columns: minmax(320px, 1.4fr) minmax(240px, 1fr);
  gap: 1.25rem;
  align-items: start;
}
.mix-table { font-size: .8rem; }
.mix-table .text-left { text-align: left; }

/* Compare pilots. Even split -- the two charts answer different questions
   (how much flown vs how close to a limit) and deserve equal room. */
.chart-split-even { grid-template-columns: 1fr 1fr; }
.chart-subtitle {
  margin: 0 0 .25rem;
  font-size: .82rem;
  font-weight: 600;
  color: var(--ink);
}
.compare-table { font-size: .82rem; margin-top: 1rem; }
.compare-table .muted { color: var(--ink-soft); font-size: .92em; }
/* Status pills, paired with the status word so colour is never alone. */
.status-good { background: #dcf0e2; color: #1c5c31; }
.status-warning { background: var(--orange-bg); color: var(--orange-ink); }
.status-critical { background: var(--breach-bg); color: var(--breach-ink); }

.viz-caveat {
  margin: 0 0 1.5rem;
  padding: .7rem .85rem;
  background: var(--yellow-bg);
  color: var(--yellow-ink);
  border-radius: var(--radius);
  font-size: .82rem;
}

/* Crew-insights blocks: a titled section per chart. */
.viz-block { margin-bottom: 1.75rem; }
.viz-title { font-size: .95rem; font-weight: 700; margin: 0 0 .1rem; }

@media (max-width: 900px) {
  .chart-split { grid-template-columns: 1fr; }
}

@media (max-width: 720px) {
  .app-body { flex-direction: column; }
  .sidebar { width: 100%; flex: 1 1 auto; border-right: none; border-bottom: 1px solid var(--line); }
  .sidebar ul { display: flex; flex-wrap: wrap; }
}

/* ---------------------------------------------------------------- print --- */
/* Operators print duty sheets. Strip the chrome, let the grid spill to its full
   width instead of scrolling inside a capped box, and force the band colours to
   render -- a limit warning that prints white is the one thing that must not be
   lost on paper. */
@media print {
  .sidebar,
  .gov-header-meta,
  .filter-bar,
  .download-group,
  .column-picker,
  .quick-ranges,
  .plan-actions,
  .row-actions-col,
  .row-actions,
  .modal-backdrop,
  .btn { display: none !important; }

  body { background: #fff; }
  .content { padding: 0; }
  .panel { border: none; box-shadow: none; margin: 0 0 1rem; padding: 0; }

  /* Uncap the scroll containers so nothing is clipped off the page. */
  .grid-scroll { overflow: visible; max-height: none; }
  .grid { white-space: normal; font-size: 8pt; }

  /* Keep the shading on paper; the sticky column need not float when printed. */
  .band-breach, .band-orange, .band-yellow,
  .row-anomaly td, .row-anomaly .sticky-col,
  .status-pill, .swatch, .kpi-tile {
    -webkit-print-color-adjust: exact;
    print-color-adjust: exact;
  }
  .sticky-col { position: static; box-shadow: none; }

  /* Avoid orphaned rows split across a page break where it can be helped. */
  tr, .sector-card { break-inside: avoid; }
}

/* -- Visualization: duty-type-by-pilot table ------------------------------- */
.range-picker, .pilot-picker {
  display: flex;
  flex-wrap: wrap;
  gap: .4rem .6rem;
  margin-bottom: .6rem;
}
.pick-option {
  display: inline-flex;
  align-items: center;
  gap: .4rem;
  padding: .3rem .7rem;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
  cursor: pointer;
  font-size: .9rem;
}
.pick-option:has(input:checked) { border-color: var(--brand); background: #eef5fc; }
.pick-option input { margin: 0; }
.duty-count-table { width: auto; min-width: 22rem; }
.duty-count-table td.muted { color: var(--ink-soft); }

/* Per-leg aircraft / FRB / landings, shown inline after the route. */
.sector-aircraft-tag {
  font-size: .7rem;
  font-weight: 600;
  color: var(--ink-soft);
  white-space: nowrap;
}
.sector-aircraft-tag::before { content: "\00b7"; margin-right: .35rem; }
/* A no-route leg (ground run) names its kind in place of a From -> To route. */
.sector-kind {
  font-weight: 700;
  color: var(--brand);
  text-transform: uppercase;
  letter-spacing: .03em;
}
/* Keep the per-leg aircraft dropdown compact in the entry table. */
.sector-aircraft select { min-width: 7rem; }
/* When a leg has no destination, the To box gives way to a muted dash so the
   column still lines up. */
.sector-no-to { color: var(--ink-soft); }

/* -- Day View: one date, the whole operation ------------------------------- */
.day-title { font-size: 1.15rem; font-weight: 700; margin: 0 0 .75rem; }
.day-summary {
  display: flex;
  flex-wrap: wrap;
  gap: .75rem 1.75rem;
  /* Spread the stats across the full width so the bar reads as one strip,
     not a block of figures with a void on the right. */
  justify-content: space-between;
  padding: .85rem 1.5rem;
  margin-bottom: 1.25rem;
  background: #f4f8fc;
  border: 1px solid var(--border, #dbe4ee);
  border-radius: 8px;
}
.day-summary .stat { display: flex; flex-direction: column; line-height: 1.2; }
.stat-num { font-size: 1.35rem; font-weight: 700; color: var(--brand); font-variant-numeric: tabular-nums; }
.stat-num.stat-text { font-size: 1rem; }
.stat-lbl { font-size: .72rem; text-transform: uppercase; letter-spacing: .03em; color: var(--ink-soft); }
.day-section-title {
  font-size: .8rem; text-transform: uppercase; letter-spacing: .04em;
  color: var(--ink-soft); margin: 1.25rem 0 .5rem;
}
.day-flight {
  border: 1px solid var(--border, #dbe4ee);
  border-radius: 8px;
  padding: .5rem .75rem .75rem;
  margin-bottom: .85rem;
}
.day-flight-head { display: flex; align-items: center; gap: .6rem; margin-bottom: .45rem; }
.day-flight-crew { font-weight: 700; font-size: .95rem; color: var(--ink); }
/* Day View is full-width, so let each crew row span the card: the name takes
   the slack and the time columns sit at the right edge, instead of a void
   there. Scoped to .day-flight, so the narrow Flight Data column is unchanged. */
.day-flight .crew-grid { grid-template-columns: minmax(11rem, 1fr) 4rem 5rem 3.5rem; }
.day-roster { border-collapse: collapse; }
.day-roster th {
  text-align: left; font-weight: 700; padding: .3rem 1.25rem .3rem 0;
  vertical-align: top; white-space: nowrap; color: var(--ink);
}
.day-roster td { padding: .3rem 0; color: var(--ink); }

/* -- Owner Console --------------------------------------------------------- */
/* Standalone page: no operator sidebar, so the content is centred. */
.owner-content { max-width: 1120px; margin: 0 auto; width: 100%; padding: 1.5rem; }
.owner-global-actions { display: flex; gap: .5rem; flex-wrap: wrap; align-items: center; }

.owner-summary {
  display: flex;
  gap: 1rem;
  margin: 1rem 0 1.25rem;
  flex-wrap: wrap;
}
.owner-stat {
  display: flex;
  flex-direction: column;
  align-items: center;
  min-width: 8rem;
  padding: .85rem 1.25rem;
  background: var(--line-soft);
  border-radius: var(--radius);
}
.owner-stat-num { font-size: 1.9rem; font-weight: 700; color: var(--brand); line-height: 1; }
.owner-stat-label { font-size: .78rem; color: var(--ink-soft); margin-top: .35rem; }

.owner-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(15.5rem, 1fr));
  gap: 1rem;
}
.op-card {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 1rem 1.1rem 1.1rem;
  background: var(--surface);
  box-shadow: var(--shadow);
  border-top: 3px solid var(--brand);
}
.op-card-inactive { border-top-color: var(--line); opacity: .8; }
.op-card-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .5rem;
}
.op-card-head h3 { margin: 0; font-size: 1.02rem; color: var(--ink); }
.op-code { color: var(--ink-soft); font-size: .78rem; margin: .1rem 0 .8rem; }
.op-card-count { display: flex; align-items: baseline; gap: .5rem; margin-bottom: .6rem; }
.op-card-health { display: flex; gap: .4rem; flex-wrap: wrap; margin-bottom: .9rem; }
.op-count-num { font-size: 2.1rem; font-weight: 700; color: var(--brand); line-height: 1; }
.op-count-label { color: var(--ink-soft); font-size: .82rem; }
.op-card-actions { display: flex; align-items: center; gap: .35rem; flex-wrap: wrap; }

/* ==========================================================================
   Interaction polish -- hover / press / focus micro-interactions.
   Purely cosmetic and additive: delete this whole block to revert exactly.
   Only cheap properties (transform / shadow / colour) are animated, so it
   stays smooth on the large grids.
   ========================================================================== */

.btn,
.sidebar a,
.pick-option, .chip,
input[type="text"], input[type="date"], input[type="number"],
select, textarea, .date-text, .time-text,
[data-date] {
  transition: background-color .14s ease, border-color .14s ease,
              color .14s ease, box-shadow .16s ease, transform .1s ease;
}

/* Buttons: a subtle lift on hover, a satisfying press on click. */
.btn:not(.btn-link):hover {
  transform: translateY(-1px);
  box-shadow: 0 3px 8px rgba(11, 61, 107, .18);
}
.btn:not(.btn-link):active {
  transform: translateY(0);
  box-shadow: 0 1px 2px rgba(11, 61, 107, .28) inset;
}
.btn-primary:hover { box-shadow: 0 3px 10px rgba(8, 44, 78, .30); }
.btn-link:hover { color: var(--brand-dark); }

/* A clear, on-brand focus ring for keyboard users -- hidden on mouse click. */
.btn:focus-visible,
.sidebar a:focus-visible,
[data-date]:focus-visible,
.pick-option:focus-within {
  outline: none;
  box-shadow: 0 0 0 3px rgba(21, 101, 192, .35);
}

/* Form fields: a soft accent glow when focused. */
input[type="text"]:focus, input[type="date"]:focus, input[type="number"]:focus,
select:focus, textarea:focus, .date-text:focus, .time-text:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px rgba(21, 101, 192, .15);
}

/* Sidebar: smoother hover, with a hint of the active accent bar. */
.sidebar a:hover:not(.active) { border-left-color: rgba(21, 101, 192, .4); }

/* Clickable date in the grid: reads more clearly as a link. */
[data-date] { border-radius: 4px; }
[data-date]:hover { background: rgba(21, 101, 192, .10); }

/* Choice pills (columns, pilots, ranges): react to the pointer. */
.pick-option:hover { border-color: var(--accent); }

/* The entry modal glides in rather than snapping open. */
@keyframes viz-fade-in { from { opacity: 0; } to { opacity: 1; } }
@keyframes viz-modal-pop {
  from { opacity: 0; transform: translateY(10px) scale(.985); }
  to   { opacity: 1; transform: none; }
}
.modal-backdrop:not([hidden]) { animation: viz-fade-in .16s ease both; }
.modal-backdrop:not([hidden]) .modal {
  animation: viz-modal-pop .22s cubic-bezier(.2, .8, .2, 1) both;
}

/* Respect users who prefer reduced motion -- no transforms or animations. */
@media (prefers-reduced-motion: reduce) {
  .btn, .sidebar a, [data-date], .modal, .modal-backdrop, .pick-option {
    transition: none !important;
    animation: none !important;
    transform: none !important;
  }
}

/* -- Violation Report ------------------------------------------------------ */
.violation-table th.text-left, .violation-table td.text-left { text-align: left; }
.violation-table tbody tr.viol > td:first-child {
  box-shadow: inset 3px 0 0 var(--breach-ink);
  font-weight: 600;
}
.viol-badge {
  display: inline-block;
  font-size: .68rem;
  font-weight: 700;
  padding: .1rem .55rem;
  border-radius: 999px;
  white-space: nowrap;
}
.viol-badge-rest, .viol-badge-limit { background: var(--breach-bg); color: var(--breach-ink); }
.viol-badge-days-off { background: var(--orange-bg); color: var(--orange-ink); }
.viol-badge-landings { background: var(--breach-bg); color: var(--breach-ink); }
.viol-badge-fdp { background: var(--breach-bg); color: var(--breach-ink); }
.viol-badge-night { background: var(--breach-bg); color: var(--breach-ink); }
.viol-badge-outstation { background: var(--orange-bg); color: var(--orange-ink); }
.viol-car { font-size: .72rem; color: var(--ink-soft); font-variant-numeric: tabular-nums; }

/* -- Reports hub + monthly reports ---------------------------------------- */
.report-cards {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: 1rem;
}
.report-card {
  display: flex;
  flex-direction: column;
  gap: .3rem;
  padding: .85rem 1rem;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--surface);
  text-decoration: none;
  color: inherit;
  transition: border-color .14s ease, box-shadow .16s ease, transform .1s ease;
}
.report-card:hover {
  border-color: var(--accent);
  box-shadow: 0 3px 10px rgba(11, 61, 107, .12);
  transform: translateY(-1px);
}
.report-card-title { font-weight: 700; color: var(--brand); }
.report-card-desc { font-size: .82rem; color: var(--ink-soft); line-height: 1.35; }
.monthly-grid td, .monthly-grid th { white-space: nowrap; text-align: center; font-variant-numeric: tabular-nums; }
.monthly-grid td.text-left, .monthly-grid th.text-left { text-align: left; }
.monthly-grid tfoot .monthly-foot th, .monthly-grid tfoot .monthly-foot td {
  border-top: 2px solid var(--line);
  font-weight: 700;
  background: #f4f8fc;
}
