/* Buraq staff dashboard.

   Built on static/buraq/css/tokens.css, the same palette, type scale and
   spacing rhythm the public storefront uses. This file defines only what a
   back-office needs and never redefines a token — so a tenant's colours,
   dark mode and the type system stay in one place for both surfaces.

   ── How this differs from the storefront ───────────────────────────────
   Same design language, different DENSITY. The storefront is read once by a
   passenger deciding between six departures; this is read all day by a
   counter clerk who has the layout memorised and wants more rows on screen.
   So: tighter vertical rhythm, smaller default type, tables instead of
   ruled cards, and no page ever spends space on persuasion.

   What stays identical, deliberately: the type system, the ruled-not-boxed
   hierarchy, monospaced tabular figures for every number, and the rule that
   a status is never colour alone. A dispatcher reading a fare off this
   screen and a passenger reading it off the storefront should see the same
   number rendered the same way.

   ── Layout ─────────────────────────────────────────────────────────────
   Persistent sidebar + main column above 860px; off-canvas drawer below.
   One class, `.nav-open`, drives both — see the note on the toggle script
   in base.html. */

/* ---- shell ----------------------------------------------------------- */
.shell {
  display: grid;
  grid-template-columns: 250px minmax(0, 1fr);
  min-height: 100vh;
  min-height: 100dvh;
}

.sidebar {
  grid-column: 1;
  display: flex;
  flex-direction: column;
  background: var(--brand);
  color: #fff;
  position: sticky;
  top: 0;
  height: 100vh;
  height: 100dvh;
  /* Only .nav scrolls — the brand and the signed-in footer stay pinned, so
     opening a menu group never scrolls the logo out of place. */
  overflow: hidden;
}
/* The sidebar is the one surface painted in the tenant's own --brand, so it
   carries their identity without any other component having to. It sets its
   own ink tokens rather than inheriting the page's, since --brand is dark
   in both themes and a tenant can set it to anything. */
.sidebar {
  --side-ink: rgba(255, 255, 255, 0.92);
  --side-ink-dim: rgba(255, 255, 255, 0.56);
  --side-rule: rgba(255, 255, 255, 0.12);
  --side-hover: rgba(255, 255, 255, 0.08);
}

.brand {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  padding: var(--s-5) var(--s-4);
  border-bottom: 1px solid var(--side-rule);
  min-height: 66px;
}
/* An uploaded logo, uncropped at its own aspect ratio and as wide as the
   sidebar allows. Logos are drawn for light paper and the sidebar is the
   tenant's (dark) --brand, so it sits on a white plate to stay legible
   without recolouring anyone's brand. */
.brand-logo {
  display: block;
  width: 100%;
  height: auto;
  max-height: 96px;
  object-fit: contain;
  object-position: center;
  background: #fff;
  border-radius: var(--r-md);
  padding: 8px 12px;
  box-sizing: border-box;
  min-width: 0;
}
/* A logo drawn for dark grounds needs no plate on the sidebar. */
.brand-logo--dark { background: none; padding: 0 4px; object-position: left center; }
.brand--logo { padding-block: var(--s-4); }
.brand--logo .brand-name { display: none; }
/* A square, icon-only logo: keep it small and put the name back beside it. */
.brand--mark-only .brand-logo { width: 40px; height: 40px; padding: 4px; flex-shrink: 0; }
.brand--mark-only .brand-name { display: inline; }
.brand-mark {
  width: 30px; height: 30px;
  border-radius: var(--r-sm);
  background: rgba(255, 255, 255, 0.14);
  display: grid; place-items: center;
  flex-shrink: 0;
}
.brand-name {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: var(--t-md);
  font-variation-settings: "opsz" 22;
  letter-spacing: -0.015em;
  color: #fff;
  line-height: 1.15;
  min-width: 0;
  overflow-wrap: anywhere;
}

.nav {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: var(--s-3) var(--s-2);
}
.brand { flex-shrink: 0; }
/* The nav's scrollbar in the sidebar's own ink — a translucent white thumb
   on no track, so it reads on whatever --brand the tenant picked instead of
   the browser's default light-grey bar. */
.nav {
  scrollbar-width: thin;
  scrollbar-color: rgba(255, 255, 255, 0.22) transparent;
}
.nav::-webkit-scrollbar { width: 8px; }
.nav::-webkit-scrollbar-track { background: transparent; }
.nav::-webkit-scrollbar-thumb {
  background: rgba(255, 255, 255, 0.22);
  border-radius: 8px;
  border: 2px solid transparent;
  background-clip: padding-box;
}
.nav::-webkit-scrollbar-thumb:hover { background-color: rgba(255, 255, 255, 0.4); }

.nav-link {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  padding: 9px var(--s-3);
  border-radius: var(--r-sm);
  color: var(--side-ink-dim);
  font-size: var(--t-sm);
  text-decoration: none;
  min-height: 38px;
  transition: background-color var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}
.nav-link:hover { background: var(--side-hover); color: var(--side-ink); text-decoration: none; }
.nav-link.active { background: rgba(255, 255, 255, 0.13); color: #fff; font-weight: 600; }
.nav-link .ico { width: 16px; height: 16px; }

.nav-link-top { margin-bottom: var(--s-2); }

/* Accordion groups. `--gc` is the group's accent, set once on <details> and
   inherited by the header chip and the open group's left rail. */
.nav-group { margin-bottom: 2px; }
.nav-group-summary {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  padding: 9px var(--s-3);
  border-radius: var(--r-sm);
  cursor: pointer;
  list-style: none;
  color: var(--side-ink-dim);
  font-size: var(--t-sm);
  min-height: 38px;
  transition: background-color var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}
.nav-group-summary::-webkit-details-marker { display: none; }
.nav-group-summary:hover { background: var(--side-hover); color: var(--side-ink); }
.nav-group[open] > .nav-group-summary { color: var(--side-ink); }

.nav-group-icon {
  width: 24px; height: 24px;
  border-radius: 7px;
  background: color-mix(in srgb, var(--gc, #fff) 26%, transparent);
  color: var(--gc, #fff);
  display: grid; place-items: center;
  flex-shrink: 0;
}
.nav-group-icon .ico { width: 14px; height: 14px; }
.nav-group-label { flex: 1; min-width: 0; }
.nav-chevron {
  width: 13px; height: 13px;
  flex-shrink: 0;
  opacity: 0.5;
  transition: transform var(--dur) var(--ease);
}
.nav-group[open] > .nav-group-summary .nav-chevron { transform: rotate(90deg); }

/* The open group gets a coloured rail down its children — which section
   you're in stays legible after scrolling past its header. */
.nav-group-body {
  margin: 2px 0 var(--s-2) calc(var(--s-3) + 11px);
  padding-left: var(--s-4);
  border-left: 2px solid var(--side-rule);
}
.nav-group-focused > .nav-group-body { border-left-color: var(--gc, currentColor); }
.nav-group-body .nav-link { padding-left: var(--s-2); font-size: var(--t-xs); min-height: 34px; }

.link-btn {
  background: none;
  border: 0;
  padding: var(--s-2);
  color: var(--side-ink-dim);
  cursor: pointer;
  display: grid;
  place-items: center;
  border-radius: var(--r-sm);
}
.link-btn:hover { color: #fff; background: var(--side-hover); }

/* ---- main column ----------------------------------------------------- */
.main { grid-column: 2; min-width: 0; display: flex; flex-direction: column; }

.topbar {
  position: sticky;
  top: 0;
  z-index: var(--z-sticky);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-4);
  padding: var(--s-3) var(--s-6);
  min-height: 66px;
  background: color-mix(in srgb, var(--paper) 18%, transparent);
  backdrop-filter: saturate(1.4) blur(12px);
  border-bottom: 1px solid var(--rule);
}
.topbar h1 {
  flex: 1 1 0;
  min-width: min(10ch, 100%);
  font-size: var(--t-xl);
  font-variation-settings: "opsz" 40;
  overflow-wrap: anywhere;
}
/* The bar wraps (see .topbar) rather than overflowing: a page with extra
   actions (trips) pushed the account menu off the right edge at laptop
   widths. The heading keeps ~10ch; when the actions don't fit beside it
   they move to a line of their own, right-aligned, and wrap within it. */
.topbar-actions { display: flex; flex-wrap: wrap; justify-content: flex-end; align-items: center; gap: var(--s-2); margin-left: auto; min-width: 0; max-width: 100%; }

/* ---- account menu (topbar, far right) -------------------------------- */
.user-menu { position: relative; }
.user-menu__button {
  list-style: none;
  display: flex;
  align-items: center;
  gap: var(--s-2);
  height: 40px;
  padding: 0 var(--s-2) 0 3px;
  border: 1px solid var(--rule-strong);
  border-radius: var(--r-pill);
  background: var(--surface);
  color: var(--ink);
  cursor: pointer;
  font-size: var(--t-sm);
  font-weight: 600;
  user-select: none;
}
.user-menu__button::-webkit-details-marker { display: none; }
.user-menu__button:hover { border-color: var(--ink-3); }
.user-menu__button .avatar { width: 32px; height: 32px; }
.user-menu__name { max-width: 120px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.user-menu__chev { color: var(--ink-3); transition: transform var(--dur-fast) var(--ease); }
.user-menu[open] .user-menu__chev { transform: rotate(180deg); }
.user-menu[open] .user-menu__button { border-color: var(--brand-2); }

.user-menu__panel {
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  z-index: var(--z-modal);
  width: 280px;
  max-width: calc(100vw - 24px);
  padding: 6px;
  background: var(--surface);
  border: 1px solid var(--rule-strong);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-3);
  animation: user-menu-in var(--dur-fast) var(--ease);
}
@keyframes user-menu-in { from { opacity: 0; transform: translateY(-4px); } }
.user-menu__head {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  padding: var(--s-3) var(--s-3) var(--s-4);
  margin-bottom: 6px;
  border-bottom: 1px solid var(--rule);
}
.user-menu__who { display: flex; flex-direction: column; min-width: 0; line-height: 1.35; }
.user-menu__who strong { font-size: var(--t-sm); color: var(--ink); }
.user-menu__who span { font-size: var(--t-xs); color: var(--ink-3); overflow-wrap: anywhere; }
.user-menu__who .user-menu__role {
  margin-top: 3px;
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
.user-menu__item {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  width: 100%;
  padding: 8px var(--s-3);
  border: 0;
  border-radius: var(--r-sm);
  background: none;
  color: var(--ink-2);
  font: inherit;
  font-size: var(--t-sm);
  text-align: left;
  text-decoration: none;
  cursor: pointer;
}
.user-menu__item .ico { color: var(--ink-3); }
.user-menu__item:hover, .user-menu__item:focus-visible { background: var(--surface-hover); color: var(--ink); text-decoration: none; }
.user-menu__item:hover .ico { color: var(--ink); }
.user-menu__item--danger:hover, .user-menu__item--danger:hover .ico { color: var(--critical); }
.user-menu__sep { height: 1px; margin: 6px 4px; background: var(--rule); }
/* Phone: just the avatar, no name/chevron. */
@media (max-width: 720px) {
  .user-menu__name, .user-menu__chev { display: none; }
  .user-menu__button { padding: 0 3px; }
}

.menu-toggle {
  display: none;
  width: 38px; height: 38px;
  border-radius: var(--r-sm);
  border: 1px solid var(--rule-strong);
  background: var(--surface);
  color: var(--ink-2);
  cursor: pointer;
  place-items: center;
  flex-shrink: 0;
}
.menu-toggle:hover { color: var(--brand-2); border-color: var(--brand-2); }

.content { padding: var(--s-6); flex: 1; min-width: 0; }
.content--bare { max-width: 460px; margin: 0 auto; padding: var(--s-10) var(--s-5); }

/* Mobile drawer scrim — only ever shown by the max-width:860px block below. */
.nav-backdrop { display: none; }

/* ---- responsive ------------------------------------------------------ */
/* One class across both breakpoints — see base.html's toggle script.
   Above 860px .nav-open COLLAPSES a sidebar that is shown by default;
   below it, .nav-open REVEALS a drawer that is hidden by default. */
@media (min-width: 861px) {
  .menu-toggle { display: grid; }
  .shell.nav-open { grid-template-columns: 0 minmax(0, 1fr); }
  .shell.nav-open .sidebar { display: none; }
}
@media (max-width: 860px) {
  .menu-toggle { display: grid; }
  .shell { grid-template-columns: minmax(0, 1fr); }
  .main { grid-column: 1; }
  .sidebar {
    position: fixed;
    inset: 0 auto 0 0;
    width: 264px;
    z-index: var(--z-modal);
    transform: translateX(-100%);
    transition: transform var(--dur) var(--ease);
    box-shadow: var(--shadow-3);
  }
  .shell.nav-open .sidebar { transform: none; }
  /* The drawer is 264px wide and overlays the topbar's own toggle button,
     so without a scrim to tap there is no way to close it again once open.
     Sits one layer under the sidebar itself. */
  .shell.nav-open .nav-backdrop {
    display: block;
    position: fixed;
    inset: 0;
    z-index: calc(var(--z-modal) - 1);
    background: color-mix(in srgb, #000 45%, transparent);
    -webkit-tap-highlight-color: transparent;
  }
  .content { padding: var(--s-4); }
  .topbar { padding: var(--s-3) var(--s-4); }
  .topbar h1 { font-size: var(--t-lg); }
  .topbar-actions .btn-label { display: none; }
}

/* ---- icons ----------------------------------------------------------- */
.ico { width: 17px; height: 17px; flex-shrink: 0; color: inherit; }
.ico--sm { width: 14px; height: 14px; }
.ico--lg { width: 21px; height: 21px; }

/* ---- messages -------------------------------------------------------- */
.messages { list-style: none; margin: var(--s-4) var(--s-6) 0; padding: 0; }
.message {
  display: flex;
  align-items: flex-start;
  gap: var(--s-3);
  padding: var(--s-3) var(--s-4);
  border-radius: var(--r-md);
  border: 1px solid;
  font-size: var(--t-sm);
  margin-bottom: var(--s-2);
}
.message.success { background: var(--positive-wash); border-color: color-mix(in srgb, var(--positive) 35%, transparent); color: var(--positive); }
.message.error,
.message.warning { background: var(--critical-wash); border-color: color-mix(in srgb, var(--critical) 35%, transparent); color: var(--critical); }
.message.info { background: var(--info-wash); border-color: color-mix(in srgb, var(--info) 35%, transparent); color: var(--info); }
@media (max-width: 860px) { .messages { margin: var(--s-3) var(--s-4) 0; } }

/* ---- buttons --------------------------------------------------------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-2);
  font-family: inherit;
  font-size: var(--t-sm);
  font-weight: 600;
  line-height: 1;
  min-height: 38px;
  padding: 0 var(--s-4);
  border-radius: var(--r-md);
  border: 1px solid var(--rule-strong);
  background: var(--surface);
  color: var(--ink);
  cursor: pointer;
  text-decoration: none;
  white-space: nowrap;
  transition: background-color var(--dur-fast) var(--ease),
              border-color var(--dur-fast) var(--ease),
              color var(--dur-fast) var(--ease);
}
.btn:hover { background: var(--surface-hover); border-color: var(--ink-3); text-decoration: none; }
.btn-primary { background: var(--brand-2); border-color: var(--brand-2); color: var(--brand-2-ink); }
.btn-primary:hover { background: color-mix(in srgb, var(--brand-2) 86%, #000); border-color: color-mix(in srgb, var(--brand-2) 86%, #000); }
.btn-danger { border-color: color-mix(in srgb, var(--critical) 45%, transparent); color: var(--critical); background: transparent; }
.btn-danger:hover { background: var(--critical-wash); border-color: var(--critical); }
.btn-quiet { border-color: transparent; background: transparent; color: var(--ink-2); }
.btn-quiet:hover { background: var(--surface-hover); color: var(--ink); border-color: transparent; }
.btn-sm { min-height: 30px; font-size: var(--t-xs); padding: 0 var(--s-3); }
.btn-lg { min-height: 46px; font-size: var(--t-base); padding: 0 var(--s-5); }
.btn-block { width: 100%; }
.btn[disabled], .btn[aria-disabled="true"] { opacity: 0.45; cursor: not-allowed; pointer-events: none; }

.btn-row { display: flex; flex-wrap: wrap; gap: var(--s-2); }

/* ---- cards / panels -------------------------------------------------- */
.card {
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: var(--r-lg);
  overflow: hidden;
}
.card + .card { margin-top: var(--s-5); }
.card-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-4);
  flex-wrap: wrap;
  padding: var(--s-4) var(--s-5);
  border-bottom: 1px solid var(--rule);
}
.card-head h2, .card-head h3 { font-size: var(--t-md); font-variation-settings: "opsz" 20; }
.card-body { padding: var(--s-5); }
.card-body--flush { padding: 0; }

.section-title {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s-4);
  flex-wrap: wrap;
  margin-bottom: var(--s-4);
}

/* ---- stat tiles ------------------------------------------------------ */
/* Ruled, not boxed: a row of tiles divided by hairlines inside one card
   rather than a grid of floating panels. Same reasoning as the storefront's
   departure rows — it reads as one figure set, which is what it is. */
.stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: var(--r-lg);
  overflow: hidden;
}
.stat {
  padding: var(--s-4) var(--s-5);
  border-right: 1px solid var(--rule);
  border-bottom: 1px solid var(--rule);
  min-width: 0;
}
/* Two lines are reserved whether or not this label needs them. Status
   labels are the model's own choice text and are not written to a length
   budget: "Pending payment/confirmation" wraps to two lines where
   "Confirmed" and "Expired" take one, and every figure in that row then
   sat 17px below its neighbours. Holding the height here keeps the numbers
   on one line across the row.

   Done on the label rather than by pushing the value to the bottom of the
   tile: some rows (the overview) mix tiles that carry a .stat-sub under
   the value with tiles that do not, and bottom-pinning aligns the last
   element rather than the number. The px value is the fallback for
   browsers without the lh unit. */
.stat-label {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-bottom: var(--s-2);
  min-height: 34px;
  min-height: 2lh;
}
/* Both of these are <span>s in the templates, so they need an explicit
   block: left inline they share a line ("R-DHK-RNGP RT-Dhaka - Rangpur",
   "13:30 Fri 28 Aug 2026") instead of stacking, and .stat-sub's margin-top
   is dropped entirely, since vertical margins don't apply to inline boxes.
   Same inline-span trap as .bar-fill needing display:block to get a width. */
.stat-value {
  display: block;
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: var(--t-xl);
  font-weight: 600;
  letter-spacing: -0.03em;
  line-height: 1.1;
  color: var(--ink);
  overflow-wrap: anywhere;
}
.stat-sub {
  display: block;
  font-size: var(--t-xs);
  color: var(--ink-3);
  line-height: var(--lh-snug);
  margin-top: 4px;
  /* A route name ("RT-Dhaka - Rangpur") outruns a 170px tile, so let it
     wrap onto a second line rather than widen the column or clip. */
  overflow-wrap: anywhere;
}
.stat-value--sm { font-size: var(--t-lg); }
/* The value is a block now, so an inline marker inside it (the route's
   line-dot, a badge) needs its own vertical centring against the text. */
.stat-value .line-dot { vertical-align: middle; }
.stat-value .badge { vertical-align: middle; }

/* ---- tables ---------------------------------------------------------- */
.table-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }
table.data {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--t-sm);
}
table.data th {
  text-align: left;
  font-family: var(--font-mono);
  font-size: var(--t-2xs);
  font-weight: 500;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--ink-3);
  padding: var(--s-3) var(--s-4);
  background: var(--surface-sunken);
  border-bottom: 1px solid var(--rule);
  white-space: nowrap;
  position: sticky;
  top: 0;
  z-index: 1;
}
table.data td {
  padding: var(--s-3) var(--s-4);
  border-bottom: 1px solid var(--rule);
  vertical-align: middle;
  line-height: var(--lh-snug);
}
table.data tbody tr:last-child td { border-bottom: 0; }
table.data tbody tr { transition: background-color var(--dur-fast) var(--ease); }
table.data tbody tr:hover { background: var(--surface-hover); }
table.data td.num,
table.data th.num { text-align: right; font-family: var(--font-mono); font-variant-numeric: tabular-nums; }
table.data td.actions { text-align: right; white-space: nowrap; }
/* Plain text links in a cell — the record name that opens its detail page.
   `:not(.btn)` is load-bearing, not tidiness: `table.data a` is an
   element-plus-class selector and so outranks a lone `.btn-primary`, which
   made every button sitting in a table cell inherit these colours instead of
   its own. The "Select" button in the trip picker rendered dark ink on a
   filled brand fill (3.25:1, under AA), and on hover this rule set the text
   to --brand-2 on a --brand-2 background — 1:1, the label disappeared. */
table.data a:not(.btn) {
  color: var(--ink);
  font-weight: 500;
  /* A quiet dotted underline, so a linked cell is recognisably a link
     without turning every table into a column of blue. */
  text-decoration: underline dotted color-mix(in srgb, var(--ink) 40%, transparent);
  text-underline-offset: 3px;
}
table.data a:not(.btn):hover { color: var(--brand-2); text-decoration: underline solid; }

.empty {
  padding: var(--s-9) var(--s-5);
  text-align: center;
  color: var(--ink-2);
}
.empty .ico { width: 30px; height: 30px; color: var(--ink-3); margin: 0 auto var(--s-3); }
.empty h3 { margin-bottom: var(--s-2); }

/* ---- badges ---------------------------------------------------------- */
.badge {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-family: var(--font-mono);
  font-size: var(--t-2xs);
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  padding: 3px 8px;
  border-radius: var(--r-sm);
  border: 1px solid transparent;
  white-space: nowrap;
}
/* The suffix is the TONE name that dashboard_extras.status_tone returns —
   ok / warn / bad / info / none — so `badge-{{ x.status|status_tone }}`
   always resolves. The longer aliases are kept for the handful of places
   that name a tone directly in markup. */
.badge-ok, .badge-positive, .badge-success { background: var(--positive-wash); color: var(--positive); border-color: color-mix(in srgb, var(--positive) 28%, transparent); }
.badge-warn, .badge-caution, .badge-warning { background: var(--caution-wash); color: var(--caution); border-color: color-mix(in srgb, var(--caution) 28%, transparent); }
.badge-bad, .badge-critical, .badge-danger { background: var(--critical-wash); color: var(--critical); border-color: color-mix(in srgb, var(--critical) 28%, transparent); }
.badge-info { background: var(--info-wash); color: var(--info); border-color: color-mix(in srgb, var(--info) 28%, transparent); }
.badge-none, .badge-quiet, .badge-muted { background: var(--surface-sunken); color: var(--ink-3); border-color: var(--rule); }

/* ---- forms ----------------------------------------------------------- */
.form-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: var(--s-4) var(--s-5);
}
.form-grid--single { grid-template-columns: minmax(0, 1fr); }
.field { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.field--wide { grid-column: 1 / -1; }
/* `:first-child` matters: a widget can emit <label>s of its own into the
   field (ClearableFileInput does, for its "Clear" checkbox), and those are
   not field names — without this they render as uppercase mono headings. */
.field > label:first-child,
.auth-label-row > label,
.form-label {
  font-family: var(--font-mono);
  font-size: var(--t-2xs);
  font-weight: 500;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--ink-3);
}
.field .helptext, .form-hint { font-size: var(--t-xs); color: var(--ink-3); }
.field .errorlist, .form-error {
  list-style: none;
  margin: 0;
  padding: 0;
  font-size: var(--t-xs);
  color: var(--critical);
}

input[type="text"], input[type="email"], input[type="password"], input[type="number"],
input[type="tel"], input[type="url"], input[type="date"], input[type="time"],
input[type="datetime-local"], input[type="search"], select, textarea {
  width: 100%;
  font-family: inherit;
  font-size: var(--t-sm);
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--rule-strong);
  border-radius: var(--r-md);
  padding: 9px var(--s-3);
  min-height: 38px;
  transition: border-color var(--dur-fast) var(--ease), box-shadow var(--dur-fast) var(--ease);
}
textarea { min-height: 88px; resize: vertical; line-height: var(--lh-body); }
input:focus, select:focus, textarea:focus {
  outline: none;
  border-color: var(--brand-2);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--brand-2) 18%, transparent);
}
input::placeholder, textarea::placeholder { color: var(--ink-3); }
input[disabled], select[disabled], textarea[disabled],
input[readonly], textarea[readonly] { background: var(--surface-sunken); color: var(--ink-3); }
select {
  appearance: none;
  padding-right: var(--s-7);
  background-image: linear-gradient(45deg, transparent 50%, currentColor 50%),
                    linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-position: calc(100% - 17px) calc(50% + 1px), calc(100% - 11px) calc(50% + 1px);
  background-size: 6px 6px, 6px 6px;
  background-repeat: no-repeat;
  cursor: pointer;
}
select[multiple] { background-image: none; padding-right: var(--s-3); min-height: 120px; }
input[type="checkbox"], input[type="radio"] { width: auto; min-height: 0; accent-color: var(--brand-2); }

.check-inline { display: flex; align-items: center; gap: var(--s-2); font-size: var(--t-sm); }

.form-actions {
  display: flex;
  gap: var(--s-2);
  flex-wrap: wrap;
  align-items: center;
  padding-top: var(--s-5);
  margin-top: var(--s-5);
  border-top: 1px solid var(--rule);
}

/* ---- filter bar ------------------------------------------------------ */
.filters {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-2);
  align-items: flex-end;
  padding: var(--s-4) var(--s-5);
  border-bottom: 1px solid var(--rule);
  background: var(--surface-sunken);
}
.filters .field { flex: 0 1 190px; }
.filters .btn { flex-shrink: 0; }

/* ---- definition lists ------------------------------------------------ */
.dl {
  display: grid;
  grid-template-columns: minmax(120px, auto) minmax(0, 1fr);
  gap: var(--s-3) var(--s-5);
  align-items: baseline;
}
.dl dt {
  font-family: var(--font-mono);
  font-size: var(--t-2xs);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--ink-3);
}
.dl dd { margin: 0; min-width: 0; overflow-wrap: anywhere; }
/* JetBrains Mono renders visibly larger than Familjen Grotesk at the same
   nominal size — measured on this pair, its x-height is 0.5625em against
   0.5em and its cap-height 0.7344em against 0.6563em, so a 15.5px mono value
   sits 12-20% taller than the 15.5px sans value directly above it. That only
   shows where the two meet in one column, which is exactly what a .dl is: a
   ticket number or a timestamp reads as bigger than the passenger name.

   font-size-adjust matches x-heights instead of nominal sizes, which is what
   the eye is comparing. 0.5 is not arbitrary: it is the body face own ex
   ratio, so the mono is asked to meet the sans rather than both meeting some
   third value. Matching cap-height instead would land within 0.1px of the
   same result. Unsupported browsers ignore it and get todays rendering.
   Scoped to .dl on purpose — a table column that is entirely mono has
   nothing to disagree with and should keep its full size. */
.dl dd.num, .dl dd .num { font-size-adjust: 0.5; }

/* ---- utilities ------------------------------------------------------- */
.u-label {
  font-family: var(--font-mono);
  font-size: var(--t-2xs);
  font-weight: 500;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--ink-3);
}
.num, .u-num {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.01em;
}
/* ---- theme toggle ---------------------------------------------------- */
/* Hidden until the pre-paint script in base.html marks the document as
   scripted. A control that silently does nothing is worse than an absent
   one, and with scripting off tokens.css's `color-scheme: light dark`
   already follows the OS, so nothing is actually lost. */
.theme-toggle { display: none; }
.js .theme-toggle { display: inline-flex; padding-left: var(--s-3); padding-right: var(--s-3); }
/* The script stamps the RESOLVED theme on the button, so the icon shown is
   the scheme you are in. Driving the swap off that one attribute keeps it in
   step with the OS-preference case, which no CSS selector can see without
   repeating the media query. */
.theme-toggle[data-theme="dark"] .theme-toggle__moon,
.theme-toggle[data-theme="light"] .theme-toggle__sun { display: none; }
/* Before the script paints, show neither rather than both stacked. */
.theme-toggle:not([data-theme]) .theme-toggle__sun { display: none; }

.muted { color: var(--ink-3); }
.grid-2 { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: var(--s-5); align-items: start; }
.stack { display: flex; flex-direction: column; gap: var(--s-4); }
.row { display: flex; align-items: center; gap: var(--s-3); flex-wrap: wrap; }
.spread { display: flex; align-items: center; justify-content: space-between; gap: var(--s-4); flex-wrap: wrap; }

.note {
  display: flex;
  align-items: flex-start;
  gap: var(--s-3);
  padding: var(--s-3) var(--s-4);
  border-radius: var(--r-md);
  font-size: var(--t-sm);
  background: var(--surface-sunken);
  border: 1px solid var(--rule);
  color: var(--ink-2);
}
.note-info { background: var(--info-wash); border-color: color-mix(in srgb, var(--info) 25%, transparent); color: var(--info); }
.note-caution { background: var(--caution-wash); border-color: color-mix(in srgb, var(--caution) 25%, transparent); color: var(--caution); }
.note-critical { background: var(--critical-wash); border-color: color-mix(in srgb, var(--critical) 25%, transparent); color: var(--critical); }
.note-positive { background: var(--positive-wash); border-color: color-mix(in srgb, var(--positive) 25%, transparent); color: var(--positive); }
.note .ico { margin-top: 1px; }

/* Route wayfinding dot, same convention and colours as the storefront. */
.line-dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; display: inline-block; background: var(--line); }
.line-1 { --line: var(--line-1); }
.line-2 { --line: var(--line-2); }
.line-3 { --line: var(--line-3); }
.line-4 { --line: var(--line-4); }
.line-5 { --line: var(--line-5); }
.line-6 { --line: var(--line-6); }

/* ---- pagination ------------------------------------------------------ */
.pager {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-3);
  padding: var(--s-3) var(--s-5);
  border-top: 1px solid var(--rule);
  flex-wrap: wrap;
}
.pager-links { display: flex; gap: 4px; align-items: center; flex-wrap: wrap; }
/* Numbered pager: 1 2 … 7 8 9 … 20 21, with ‹ › steppers either side. */
.pager-num, .pager-step {
  display: inline-grid;
  place-items: center;
  min-width: 32px;
  height: 32px;
  padding: 0 8px;
  border-radius: var(--r-sm);
  border: 1px solid var(--rule-strong);
  background: var(--surface);
  color: var(--ink-2);
  font-family: var(--font-mono);
  font-size: var(--t-xs);
  font-variant-numeric: tabular-nums;
  text-decoration: none;
  transition: border-color var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}
a.pager-num:hover, a.pager-step:hover { border-color: var(--ink-3); color: var(--ink); text-decoration: none; }
.pager-num.is-on {
  border-color: var(--brand-2);
  background: var(--brand-2);
  color: var(--brand-2-ink, #fff);
  font-weight: 600;
}
.pager-step.is-off { opacity: 0.35; }
.pager-gap { min-width: 20px; text-align: center; color: var(--ink-3); }

/* ---- breadcrumb ------------------------------------------------------ */
.crumbs {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  flex-wrap: wrap;
  margin-bottom: var(--s-4);
  font-family: var(--font-mono);
  font-size: var(--t-2xs);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--ink-3);
}
.crumbs a { color: inherit; text-decoration: none; }
.crumbs a:hover { color: var(--brand-2); }
.crumbs .ico { width: 12px; height: 12px; opacity: 0.6; }

/* ---- avatar ---------------------------------------------------------- */
.avatar {
  width: 34px; height: 34px;
  border-radius: var(--r-pill);
  object-fit: cover;
  flex-shrink: 0;
  background: var(--surface-sunken);
  display: grid;
  place-items: center;
  font-family: var(--font-mono);
  font-size: var(--t-xs);
  font-weight: 600;
  color: var(--ink-2);
  border: 1px solid var(--rule);
}
.avatar-lg { width: 52px; height: 52px; font-size: var(--t-md); }
/* An avatar with the name it belongs to, in one table cell. */
.person { display: inline-flex; align-items: center; gap: var(--s-2); white-space: nowrap; }
.person .avatar { width: 28px; height: 28px; font-size: var(--t-2xs); }

/* ---- seat map (staff booking wizard) --------------------------------- */
/* Same floor-plan model as the storefront, tightened for a counter screen
   a clerk is working through quickly. */
.deck {
  border: 1px solid var(--rule-strong);
  border-radius: var(--r-lg) var(--r-lg) var(--r-md) var(--r-md);
  padding: var(--s-5) var(--s-4) var(--s-4);
  background: var(--surface-sunken);
  position: relative;
  max-width: 300px;
}
.deck-driver {
  position: absolute;
  top: calc(-1 * var(--s-3));
  right: var(--s-4);
  background: var(--surface);
  border: 1px solid var(--rule-strong);
  border-radius: var(--r-pill);
  padding: 3px var(--s-3);
  display: inline-flex;
  align-items: center;
  gap: 5px;
}
.deck-rows { display: flex; flex-direction: column; gap: 7px; }
.deck-row { display: grid; grid-template-columns: repeat(2, 36px) var(--s-5) repeat(2, 36px); gap: 7px; justify-content: center; }
.seat {
  width: 36px; height: 36px;
  border-radius: 8px 8px 5px 5px;
  border: 1.5px solid var(--rule-strong);
  background: var(--surface);
  color: var(--ink-2);
  font-family: var(--font-mono);
  font-size: 10.5px;
  font-weight: 500;
  cursor: pointer;
  display: grid;
  place-items: center;
  padding: 0;
  transition: background-color var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}
.seat:hover:not(:disabled) { border-color: var(--brand-2); color: var(--brand-2); }
.seat-taken {
  background: repeating-linear-gradient(-45deg, var(--surface-sunken), var(--surface-sunken) 3px, var(--rule) 3px, var(--rule) 5px);
  border-color: var(--rule);
  color: var(--ink-3);
  cursor: not-allowed;
}
.seat-sel { background: var(--brand-2); border-color: var(--brand-2); color: var(--brand-2-ink); font-weight: 600; }
.seat-gap { visibility: hidden; }
.seat-legend { display: flex; flex-wrap: wrap; gap: var(--s-4); margin-bottom: var(--s-4); }
.seat-legend-item { display: inline-flex; align-items: center; gap: 7px; font-size: var(--t-xs); color: var(--ink-2); }
.seat-legend-swatch { width: 14px; height: 14px; border-radius: 4px; border: 1.5px solid var(--rule-strong); background: var(--surface); }
.seat-legend-swatch.taken { background: repeating-linear-gradient(-45deg, var(--surface-sunken), var(--surface-sunken) 3px, var(--rule) 3px, var(--rule) 5px); border-color: var(--rule); }
.seat-legend-swatch.sel { background: var(--brand-2); border-color: var(--brand-2); }

/* ---- money breakdown ------------------------------------------------- */
.money { display: flex; flex-direction: column; gap: var(--s-3); }
.money-row { display: flex; align-items: baseline; justify-content: space-between; gap: var(--s-4); font-size: var(--t-sm); }
.money-label { color: var(--ink-2); display: inline-flex; align-items: center; gap: 6px; min-width: 0; }
.money-value { font-family: var(--font-mono); font-variant-numeric: tabular-nums; color: var(--ink); white-space: nowrap; }
.money-row--credit .money-label, .money-row--credit .money-value { color: var(--positive); }
.money-row--total { border-top: 1px solid var(--rule-strong); padding-top: var(--s-3); margin-top: var(--s-1); }
.money-row--total .money-label { font-weight: 600; color: var(--ink); font-size: var(--t-base); }
.money-row--total .money-value { font-size: var(--t-lg); font-weight: 600; letter-spacing: -0.03em; }

/* ---- stepper (booking wizard) ---------------------------------------- */
.steps { display: flex; align-items: center; gap: var(--s-2); margin-bottom: var(--s-5); flex-wrap: wrap; }
.step { display: inline-flex; align-items: center; gap: var(--s-2); font-size: var(--t-xs); color: var(--ink-3); white-space: nowrap; }
.step-dot {
  width: 21px; height: 21px;
  border-radius: 50%;
  border: 1.5px solid var(--rule-strong);
  display: grid; place-items: center;
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 600;
}
.step-done { color: var(--positive); }
.step-done .step-dot { background: var(--positive); border-color: var(--positive); color: #fff; }
.step-on { color: var(--ink); font-weight: 600; }
.step-on .step-dot { background: var(--brand-2); border-color: var(--brand-2); color: var(--brand-2-ink); }
.steps-link { flex: 1 1 12px; min-width: 8px; border-top: 1px solid var(--rule-strong); }

/* ---- auth (staff sign-in, tenant picker) ------------------------------
   Two shapes live here. The tenant picker is the plain centred column
   (.auth / .auth-card). Every other signed-out screen is the split shell
   (.auth-split, below): an ink rail carrying the platform beside a paper
   column carrying the operator and the form on one card.

   The rail is not decoration. Every operator's dashboard sits on a
   look-alike subdomain, so the first screen has to answer "whose console
   is this?" before it asks for a password — the rail answers it with the
   tenant's own logo, name and the host itself set in mono, which catches a
   wrong-subdomain login before the credentials are typed rather than
   after. It is also the one surface here painted in the tenant's --brand,
   exactly as the signed-in sidebar is, so the sign-in screen looks like the
   product it opens into.

   Below 920px the rail collapses to a single identity band above the form:
   the tenant and the host still answer the same question, and the
   supporting copy — level-2 information — gets out of the way of the one
   task on the screen. */
.auth {
  min-height: 100vh;
  min-height: 100dvh;
  display: grid;
  place-items: center;
  padding: var(--s-6);
}
.auth-card { width: 100%; max-width: 400px; }
.auth-brand {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  justify-content: center;
  margin-bottom: var(--s-6);
}
.auth-brand-mark {
  width: 40px; height: 40px;
  border-radius: var(--r-md);
  background: var(--brand);
  color: #fff;
  display: grid; place-items: center;
}
.auth-brand-name {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: var(--t-lg);
  font-variation-settings: "opsz" 28;
  letter-spacing: -0.015em;
}

/* -- split shell: "operations console" --------------------------------
   Two plain surfaces, no photograph, no glass. A solid deep-ink rail
   carries the platform — the Buraq wordmark, what the console is for, a map
   of the product, and the host as a verification chip — and a quiet paper
   column carries the operator and the form, on one card. Flat colour is
   what lets every piece of small type here pass contrast by a wide margin
   instead of by measurement against whatever a photograph did behind it.

   The rail is ink in both schemes (a brand surface, like the signed-in
   sidebar); the form column follows the page's own light/dark tokens.
   Below 920px the rail collapses to a band above the form holding just the
   wordmark and the host — level-2 copy gets out of the way of the one task
   on the screen. */
.auth-split {
  min-height: 100vh;
  min-height: 100dvh;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: auto minmax(0, 1fr);
  background: var(--paper);
}
@media (min-width: 920px) {
  .auth-split {
    grid-template-columns: minmax(340px, 44fr) 56fr;
    grid-template-rows: minmax(0, 1fr);
    /* Exactly one window tall: the rail's illustration and spacing flex to
       fit (below), so the page itself never scrolls. */
    height: 100vh;
    height: 100dvh;
    min-height: 0;
    overflow: hidden;
  }
  .auth-form { min-height: 0; overflow: hidden; }
}
/* Only a window shorter than even the compact card lets the card column
   scroll on its own — with the bar hidden, so it never shows. */
@media (min-width: 920px) and (max-height: 520px) {
  .auth-form { overflow-y: auto; scrollbar-width: none; }
  .auth-form::-webkit-scrollbar { display: none; }
}

/* -- the rail -- */
.auth-board {
  --board-ink: #F4F7FB;
  --board-dim: #B9C6D8;   /* ≥7:1 across the whole gradient */
  --board-rule: rgb(255 255 255 / 0.12);
  --coral: #F2645A;
  color-scheme: dark;     /* --line-* accents resolve to their dark-ground values */
  position: relative;
  isolation: isolate;
  overflow: hidden;
  background:
    radial-gradient(70% 45% at 85% 12%, rgb(242 100 90 / 0.22), transparent 70%),
    radial-gradient(60% 50% at 10% 95%, rgb(20 184 166 / 0.28), transparent 70%),
    linear-gradient(160deg, #0D1A2E 0%, #12305A 52%, #0C5D6B 100%);
  color: var(--board-ink);
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--s-3) var(--s-5);
  padding: var(--s-4) var(--s-5);
}
@media (min-width: 920px) {
  .auth-board {
    flex-direction: column;
    align-items: stretch;
    flex-wrap: nowrap;
    gap: var(--s-6);
    padding: var(--s-8) clamp(var(--s-7), 5vw, 88px) var(--s-7);
  }
}

/* Supporting copy: desktop only. */
.auth-board-body { display: none; }
@media (min-width: 920px) {
  .auth-board-body { display: block; max-width: 480px; flex-shrink: 0; }
}
.auth-board-eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 6px 12px;
  border-radius: 999px;
  background: rgb(242 100 90 / 0.16);
  color: #FFB4AD;
  font-family: var(--font-mono);
  font-size: var(--t-2xs);
  font-weight: 600;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  margin-bottom: var(--s-4);
}
.auth-board-lede {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(2.2rem, 1.3rem + 1.9vw, 3.1rem);
  font-variation-settings: "opsz" 90;
  line-height: 1.04;
  letter-spacing: -0.022em;
  color: var(--board-ink);
  margin: 0;
  max-width: 13ch;
}
.auth-board-sub {
  margin: var(--s-4) 0 0;
  max-width: 40ch;
  font-size: var(--t-base);
  line-height: 1.55;
  color: var(--board-dim);
}
/* The five product areas as pills, each icon in its sidebar accent. */
.auth-board-chips {
  list-style: none;
  margin: var(--s-5) 0 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
.auth-board-chips li {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 7px 14px 7px 11px;
  border: 1px solid var(--board-rule);
  border-radius: 999px;
  background: rgb(255 255 255 / 0.06);
  font-size: var(--t-sm);
  font-weight: 500;
  color: var(--board-ink);
}
.auth-board-chips .ico { color: var(--ic, var(--board-dim)); }

/* The illustration: fills the room between the copy and the host, anchored
   to the bottom so the coach sits on the rail's floor. Desktop only. */
.auth-board-scene { display: none; }
@media (min-width: 920px) {
  .auth-board-scene {
    display: block;
    flex: 1 1 0;
    min-height: 0;
    width: 100%;
    max-width: 620px;
    object-fit: contain;
    object-position: left bottom;
  }
}

/* The full-colour Buraq wordmark, straight on the rail — no tile. Not
   smaller than ~130px: below that the horse and coach turn to noise. */
.auth-board-mark {
  display: inline-flex;
  align-self: flex-start;
}
.auth-board-wordmark { display: block; width: 173px; height: auto; }
@media (min-width: 920px) {
  .auth-board-wordmark { width: 225px; }
  /* Breathing room between the wordmark and the "Staff console" block —
     scales with the window height so short screens still fit. */
  .auth-board-mark { margin-bottom: clamp(12px, 4vh, 40px); }
}

/* The host, as a verification chip. Every operator is on a look-alike
   subdomain; this is what catches a wrong-subdomain sign-in before the
   password is typed. */
.auth-board-host {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  flex-wrap: wrap;
  min-width: 0;
  margin: 0;
  color: var(--board-dim);
}
.auth-board-host-chip {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
  padding: 7px 12px;
  border: 1px solid var(--board-rule);
  border-radius: 999px;
  background: rgb(0 0 0 / 0.18);
}
.auth-board-host-chip .ico { width: 14px; height: 14px; color: #7FD1A6; flex-shrink: 0; }
.auth-board-host code {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: var(--t-xs);
  color: var(--board-ink);
  overflow-wrap: anywhere;
}
.auth-board-host-note { display: none; font-size: var(--t-xs); }
@media (min-width: 920px) { .auth-board-host-note { display: inline; } }


/* -- the form column -- */
.auth-form {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  display: grid;
  place-items: center;
  padding: var(--s-7) var(--s-4) var(--s-8);
  background: var(--paper);
}
/* Two soft shapes behind the card — coral top right, teal bottom left —
   the colour the references carry, kept off the card itself. */
.auth-form::before,
.auth-form::after {
  content: "";
  position: absolute;
  z-index: -1;
  border-radius: 50%;
  pointer-events: none;
}
.auth-form::before {
  width: 420px; height: 420px;
  right: -140px; top: -160px;
  background: radial-gradient(circle at 40% 60%, rgb(242 100 90 / 0.22), transparent 68%);
}
.auth-form::after {
  width: 480px; height: 480px;
  left: -180px; bottom: -200px;
  background: radial-gradient(circle at 60% 40%, rgb(20 184 166 / 0.20), transparent 68%);
}
@media (min-width: 920px) { .auth-form { padding: var(--s-8); } }

/* The card: one surface for the operator, the heading, the form and the
   small print, so the page reads as a single task. */
.auth-form-inner {
  width: 100%;
  max-width: 440px;
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: var(--r-lg);
  box-shadow: 0 1px 2px rgb(15 27 45 / 0.05), 0 24px 48px -20px rgb(15 27 45 / 0.28);
  padding: var(--s-7) var(--s-5) var(--s-6);
}
@media (min-width: 520px) { .auth-form-inner { padding: var(--s-8) var(--s-7) var(--s-7); } }

/* The operator's own lockup, directly above the form that signs in to it —
   the name a person checks before typing a password. */
.auth-op {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  margin: 0 0 var(--s-6);
  padding-bottom: var(--s-5);
  border-bottom: 1px solid var(--rule);
  min-width: 0;
}
/* The whole logo at its own proportions. On the light card it needs no
   plate; the dark scheme gives it one, since logos are drawn for paper. */
.auth-op-logo {
  display: block;
  height: 64px;
  width: auto;
  max-width: 100%;
  object-fit: contain;
  object-position: left center;
  border-radius: var(--r-md);
  box-sizing: border-box;
  flex-shrink: 0;
}
.auth-op--logo .auth-op-name { display: none; }
/* An uploaded logo sits straight on the card, no plate or strip. A tenant
   with a dark-ground variant (Tenant.logo_dark) gets that one in dark mode;
   without one, the regular logo shows in both. */
.auth-op-logo--dark { display: none; }
:root[data-theme="dark"] .auth-op-logo--light { display: none; }
:root[data-theme="dark"] .auth-op-logo--dark { display: block; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .auth-op-logo--light { display: none; }
  :root:not([data-theme="light"]) .auth-op-logo--dark { display: block; }
}
/* A square, icon-only logo: smaller, with the name back beside it. */
.auth-op--mark-only .auth-op-logo { height: 48px; }
.auth-op--mark-only .auth-op-name { display: inline; }
.auth-op-mark {
  width: 40px; height: 40px;
  border-radius: var(--r-md);
  background: var(--brand);
  color: #fff;
  display: grid; place-items: center;
  flex-shrink: 0;
}
.auth-op-name {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: var(--t-lg);
  font-variation-settings: "opsz" 28;
  letter-spacing: -0.015em;
  overflow-wrap: anywhere;
}
.auth-title {
  font-size: var(--t-2xl);
  font-variation-settings: "opsz" 90;
  letter-spacing: -0.015em;
  margin: 0 0 var(--s-5);
}
/* Supporting copy under an auth heading. */
.auth-lead {
  font-size: var(--t-sm);
  color: var(--ink-2);
  margin: calc(-1 * var(--s-3)) 0 var(--s-5);
  max-width: 46ch;
}
/* Inputs a touch taller than the dashboard's and rounder, for a screen
   whose only job is two fields and a button. */
.auth-form-inner input[type="text"],
.auth-form-inner input[type="email"],
.auth-form-inner input[type="password"] {
  min-height: 48px;
  border-radius: 12px;
}
/* An icon inside the field, at its start. */
.auth-input { position: relative; }
.auth-input > .ico {
  position: absolute;
  left: 14px;
  top: 50%;
  transform: translateY(-50%);
  width: 18px; height: 18px;
  color: var(--ink-3);
  pointer-events: none;
}
.auth-input input { padding-left: 44px !important; }
.auth-input:focus-within > .ico { color: var(--brand-2); }
/* "Forgot password?" on the password label's own row. */
.auth-label-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s-3);
}
.auth-label-row a { font-size: var(--t-xs); color: var(--brand-2); }
/* The one call to action: a gradient in the tenant's own blue, leaning teal. */
.auth-form-inner .btn-primary {
  min-height: 50px;
  border: 0;
  border-radius: 12px;
  background: linear-gradient(135deg, var(--brand-2), color-mix(in srgb, var(--brand-2) 45%, #14B8A6));
  box-shadow: 0 10px 22px -10px color-mix(in srgb, var(--brand-2) 70%, transparent);
}
.auth-form-inner .btn-primary:hover {
  background: linear-gradient(135deg, color-mix(in srgb, var(--brand-2) 88%, #000), color-mix(in srgb, var(--brand-2) 40%, #0F9488));
}

/* -- fitting the window -------------------------------------------------
   Desktop windows vary more in height than in width (a 1366×650 laptop
   with its browser chrome, a 1440×900 monitor), so the rail and the card
   step down by HEIGHT: first the spacing tightens, then the supporting
   line goes, then the pills and the illustration. The form itself never
   loses anything. */
@media (min-width: 920px) {
  .auth-board { padding-block: clamp(20px, 4.5vh, 40px) clamp(16px, 3.5vh, 32px); gap: clamp(12px, 2.6vh, 24px); }
  .auth-form { padding-block: clamp(16px, 3vh, 40px); }
  .auth-form-inner { padding-block: clamp(20px, 4vh, 40px) clamp(18px, 3.2vh, 32px); }
}
@media (min-width: 920px) and (max-height: 820px) {
  .auth-board-sub { display: none; }
  .auth-board-lede { font-size: clamp(1.9rem, 1.2rem + 1.4vw, 2.5rem); }
  .auth-board-eyebrow { margin-bottom: var(--s-3); }
  .auth-op { margin-bottom: var(--s-4); padding-bottom: var(--s-4); }
  .auth-op-logo { height: 60px; }
  .auth-title { font-size: var(--t-xl); margin-bottom: var(--s-4); }
  .auth-foot { margin-top: var(--s-4); padding-top: var(--s-4); }
}
@media (min-width: 920px) and (max-height: 680px) {
  .auth-board-chips { display: none; }
  .auth-op-logo { height: 50px; }
  .auth-form-inner .stack { gap: var(--s-3); }
  .auth-form-inner input[type="text"],
  .auth-form-inner input[type="email"],
  .auth-form-inner input[type="password"] { min-height: 44px; }
  .auth-form-inner .btn-primary { min-height: 46px; }
}
@media (min-width: 920px) and (max-height: 560px) {
  .auth-board-scene { display: none; }
  .auth-board-body { margin-block: auto; }
}
/* Narrow desktop / landscape tablet: a slimmer rail and a smaller headline
   so the two columns keep their proportions. */
@media (min-width: 920px) and (max-width: 1180px) {
  .auth-board { padding-inline: var(--s-6); }
  .auth-board-lede { font-size: clamp(1.8rem, 1rem + 1.6vw, 2.3rem); }
  .auth-board-host-note { display: none; }
}
/* Phones: the card fills the width, with less chrome around it. */
@media (max-width: 519px) {
  .auth-form { padding: var(--s-5) var(--s-3) var(--s-6); }
  .auth-form-inner { padding: var(--s-6) var(--s-4) var(--s-5); border-radius: var(--r-md); }
  .auth-op-logo { height: 60px; }
  .auth-title { font-size: var(--t-xl); }
  .auth-board-wordmark { width: 147px; }
}
.auth-foot a { color: var(--brand-2); }

/* Tenant picker rows. A button that is really a destination: the name
   leads, the role sits under it as meta, and the arrow says it leaves. */
.auth-pick {
  justify-content: space-between;
  min-height: 56px;
  padding-block: var(--s-3);
  text-align: left;
  line-height: var(--lh-snug);
}
.auth-pick .u-label { color: var(--ink-3); }

/* A submit that has to read as a link — the sign-out on the tenant picker
   is a POST (it changes state, so it cannot be an <a>), but it belongs in
   the small print, not as a third button competing with the list above. */
.btn-linkish {
  background: none;
  border: 0;
  padding: 0;
  font: inherit;
  color: var(--brand-2);
  cursor: pointer;
  text-decoration: underline;
  text-decoration-color: color-mix(in srgb, currentColor 35%, transparent);
  text-underline-offset: 0.18em;
}
.btn-linkish:hover { text-decoration-color: currentColor; }

/* The small print, under a hairline inside the card. */
.auth-foot {
  margin-top: var(--s-6);
  padding-top: var(--s-5);
  border-top: 1px solid var(--rule);
  font-size: var(--t-xs);
  color: var(--ink-3);
}
.auth-foot p { margin: 0 0 var(--s-2); }
.auth-foot p:last-child { margin: 0; }

/* Show/hide sits inside the input's own box so the field keeps its full
   width and the control cannot be mistaken for a submit button. It used to
   borrow .link-btn, which is written against the sidebar's --side-* ink and
   hovers to #fff — invisible on a white input out here. */
.pw-wrap { position: relative; }
.pw-wrap input { padding-right: 42px; }
.pw-toggle {
  position: absolute;
  right: 5px;
  top: 50%;
  transform: translateY(-50%);
  width: 28px; height: 28px;
  display: grid; place-items: center;
  padding: 0;
  border: 0;
  border-radius: var(--r-sm);
  background: none;
  color: var(--ink-3);
  cursor: pointer;
  transition: color var(--dur-fast) var(--ease),
              background-color var(--dur-fast) var(--ease);
}
.pw-toggle:hover { color: var(--ink); background: var(--surface-hover); }

/* Caps Lock is the most common cause of a "wrong password" that is not
   one. Warned before submission rather than after, and it carries an icon
   and a word — never colour alone. */
.auth-hint {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: var(--t-xs);
  color: var(--caution);
}

/* Button loading state: the form posts a real round trip, and without
   feedback the usual response to the wait is a second click. */
.btn[aria-busy="true"] { pointer-events: none; opacity: 0.8; }
.btn-spinner {
  width: 14px; height: 14px;
  border: 2px solid currentColor;
  border-top-color: transparent;
  border-radius: 50%;
  animation: btn-spin 620ms linear infinite;
}
@keyframes btn-spin { to { transform: rotate(360deg); } }

/* ---- print ----------------------------------------------------------- */
@media print {
  .sidebar, .topbar, .messages, .no-print, .btn { display: none !important; }
  .shell { grid-template-columns: minmax(0, 1fr); }
  .main { grid-column: 1; }
  .content { padding: 0; }
  body { background: #fff; }
  .card { border-color: #999; break-inside: avoid; }
}

/* ---- charts ----------------------------------------------------------
   Plain CSS bars sized by a server-computed percentage, not a charting
   library. Two shapes cover every chart in this dashboard: a column series
   for a time trend, and proportional rows for a categorical breakdown.

   Bars are the tenant's --brand-2 at reduced opacity rather than a rainbow:
   these charts each show ONE measure, so a second hue would be decoration
   carrying no information. Every bar also states its value in text beside
   or under it — a chart nobody can read the numbers off is a picture. */
.chart {
  display: flex;
  align-items: flex-end;
  gap: 3px;
  height: 132px;
  padding-top: var(--s-2);
}
.chart-col {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  height: 100%;
}
.chart-bar {
  width: 100%;
  max-width: 46px;
  min-height: 3px;
  border-radius: 3px 3px 0 0;
  background: color-mix(in srgb, var(--brand-2) 55%, transparent);
  transition: background-color var(--dur-fast) var(--ease);
}
.chart-col:hover .chart-bar { background: var(--brand-2); }
.chart-axis {
  display: flex;
  justify-content: space-between;
  gap: var(--s-3);
  padding-top: var(--s-2);
  border-top: 1px solid var(--rule);
  margin-top: var(--s-2);
}

.bars { display: flex; flex-direction: column; gap: var(--s-3); }
.bar-row {
  display: grid;
  grid-template-columns: minmax(90px, 150px) minmax(0, 1fr) auto;
  gap: var(--s-3);
  align-items: center;
  font-size: var(--t-sm);
}
.bar-label { color: var(--ink-2); overflow-wrap: anywhere; }
.bar-track {
  /* display:block matters — these are <span>s so they stay inline
     otherwise, and an inline box ignores both height and width, which
     renders every bar as an empty track. */
  display: block;
  height: 8px;
  border-radius: var(--r-pill);
  background: var(--surface-sunken);
  overflow: hidden;
}
.bar-fill {
  display: block;
  height: 100%;
  border-radius: var(--r-pill);
  background: color-mix(in srgb, var(--brand-2) 65%, transparent);
  min-width: 2px;
}
.bar-value { font-family: var(--font-mono); font-variant-numeric: tabular-nums; font-size: var(--t-xs); color: var(--ink); }
@media (max-width: 560px) {
  .bar-row { grid-template-columns: minmax(0, 1fr) auto; }
  .bar-track { grid-column: 1 / -1; }
}

/* ---- stat tiles as filters -------------------------------------------
   On the list pages the tiles double as the status filter: each is a link
   that narrows the table below it, and the active one is marked. They keep
   the same ruled-row treatment as the read-only tiles on the overview —
   a filter shouldn't look like a different kind of object just because you
   can click it.

   --sc is the tile's status accent, set inline per tile. It tints only the
   top edge and the value, never the whole surface: seven saturated panels
   in a row is a swatch palette, not a summary. */
a.stat {
  text-decoration: none;
  color: inherit;
  position: relative;
  transition: background-color var(--dur-fast) var(--ease);
}
a.stat::before {
  content: "";
  position: absolute;
  inset: 0 auto auto 0;
  width: 100%;
  height: 2px;
  background: var(--sc, transparent);
  opacity: 0;
  transition: opacity var(--dur-fast) var(--ease);
}
a.stat:hover { background: var(--surface-hover); text-decoration: none; }
a.stat:hover::before { opacity: 0.5; }
.stat-on { background: var(--surface-hover); }
.stat-on::before { opacity: 1 !important; }
.stat-on .stat-value { color: var(--sc, var(--ink)); }
.stat-on .stat-label { color: var(--ink); }

/* Status accents, shared by the tiles and by any inline status dot. Named
   for the STATE, not the colour, so the mapping lives in one place and a
   template never picks a hex. */
.sc-ok { --sc: var(--positive); }
.sc-warn { --sc: var(--caution); }
.sc-bad { --sc: var(--critical); }
.sc-info { --sc: var(--info); }
.sc-none { --sc: var(--ink-3); }

/* ---- filter chips ----------------------------------------------------
   The tile row above a list stops working once a dimension has seven or
   eight values — boarding results, for one. At that width each tile is too
   narrow to read and the row wraps into a block of colour. Chips carry the
   same information (label, count, active state, tone) in a fraction of the
   space, so the table stays on the first screen. */
.chips { display: flex; flex-wrap: wrap; gap: var(--s-2); margin-bottom: var(--s-5); }
.chip {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  padding: 7px var(--s-3);
  border-radius: var(--r-pill);
  border: 1px solid var(--rule-strong);
  background: var(--surface);
  color: var(--ink-2);
  font-size: var(--t-xs);
  text-decoration: none;
  white-space: nowrap;
  transition: border-color var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease), background-color var(--dur-fast) var(--ease);
}
.chip:hover { border-color: var(--ink-3); color: var(--ink); text-decoration: none; }

/* A 1–5 score as five stars (trip ratings list). */
.rating-stars { white-space: nowrap; letter-spacing: 1px; color: var(--rule-strong); }
.rating-stars .on { color: var(--caution); }

/* Read-only summary figures above a list (fare rules). Flat and borderless,
   label over value, split by hairlines — deliberately unlike the bordered,
   clickable .chip filters, so it never reads as a row of buttons. */
.figures {
  display: inline-flex;
  flex-wrap: wrap;
  margin: 0 0 var(--s-5);
  padding: 0;
}
.figure {
  padding: 2px var(--s-5) 2px 0;
  margin-right: var(--s-5);
  border-right: 1px solid var(--rule-strong);
}
.figure:last-child { border-right: 0; margin-right: 0; padding-right: 0; }
.figure dt {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-3);
}
.figure dd {
  margin: 2px 0 0;
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: var(--t-md);
  font-weight: 600;
  color: var(--ink);
}
.chip-count {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: var(--t-2xs);
  font-weight: 600;
  padding: 1px 6px;
  border-radius: var(--r-pill);
  background: var(--surface-sunken);
  color: var(--ink-2);
}
.chip-on {
  border-color: var(--sc, var(--brand-2));
  background: color-mix(in srgb, var(--sc, var(--brand-2)) 12%, transparent);
  color: var(--sc, var(--brand-2));
  font-weight: 600;
}
.chip-on .chip-count { background: color-mix(in srgb, var(--sc, var(--brand-2)) 20%, transparent); color: inherit; }

/* ---- list toolbar ----------------------------------------------------
   One row inside the list card instead of a chip row above it plus a filter
   bar below it: a segmented status filter on the left, a search + filter
   form on the right. Opt-in per list via {% block filter_bar %} (buses). */
.list-toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-2) var(--s-4);
  padding: var(--s-2) var(--s-3);
  border-bottom: 1px solid var(--rule);
  background: var(--surface-sunken);
}
.seg {
  display: inline-flex;
  flex-wrap: wrap;
  gap: 2px;
  padding: 3px;
  border-radius: var(--r-md);
  background: color-mix(in srgb, var(--ink) 5%, transparent);
}
.seg-item {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 28px;
  padding: 0 var(--s-3);
  border-radius: calc(var(--r-md) - 3px);
  font-size: var(--t-xs);
  font-weight: 500;
  color: var(--ink-2);
  text-decoration: none;
  white-space: nowrap;
  transition: background-color var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}
.seg-item:hover { color: var(--ink); background: color-mix(in srgb, var(--surface) 60%, transparent); text-decoration: none; }
.seg-item.is-on {
  background: var(--surface);
  color: var(--ink);
  font-weight: 600;
  box-shadow: 0 1px 2px color-mix(in srgb, var(--ink) 14%, transparent), 0 0 0 1px var(--rule);
}
.seg-item.is-empty:not(.is-on) { color: var(--ink-3); }
.seg-item .dot { width: 7px; height: 7px; border-radius: 50%; background: var(--sc, var(--ink-3)); }
.seg-item.is-empty:not(.is-on) .dot { opacity: 0.45; }
.seg-count {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: var(--t-2xs);
  color: var(--ink-3);
}
.seg-item.is-on .seg-count { color: var(--ink-2); }

/* Wraps at every width: right-aligned and unwrapped, a list with more
   filters than fit (boarding events: result, method, route, device, date)
   pushed the first ones off the left edge, out of reach. */
.list-toolbar__search {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-2);
  flex: 1 1 340px;
  justify-content: flex-end;
  max-width: 560px;
}
.list-toolbar__search select,
.list-toolbar__search input[type="search"] { min-height: 32px; padding-block: 5px; font-size: var(--t-xs); }
.list-toolbar__search select { flex: 0 1 180px; width: auto; min-width: 130px; }
/* Capped so a toolbar with more filters (drivers: crew app + licence) gets
   more room for them, not a wider search box — same size on every list. */
.search-input { position: relative; flex: 1 1 200px; min-width: 0; max-width: 280px; }
.search-input > .ico {
  position: absolute;
  left: 10px;
  top: 50%;
  transform: translateY(-50%);
  color: var(--ink-3);
  pointer-events: none;
}
.list-toolbar__search .search-input input { padding-left: 30px; }
@media (max-width: 640px) {
  .list-toolbar__search { flex-wrap: wrap; max-width: none; }
  .search-input { max-width: none; flex-basis: 100%; }
  .list-toolbar__search select { flex: 1 1 140px; }
}

/* ---- whole-row links -------------------------------------------------
   `tr.row-link` makes the row's primary link (.row-link__main) cover the
   whole row, so a fleet list opens a bus from anywhere on its line rather
   than from a "View" button at the far edge. Other links in the row stay
   clickable on top of it. */
table.data tr.row-link { position: relative; cursor: pointer; }
table.data tr.row-link td { padding-block: 9px; }
table.data .row-link__main::after { content: ""; position: absolute; inset: 0; }
table.data tr.row-link a:not(.row-link__main),
table.data tr.row-link button { position: relative; z-index: 1; }
table.data .row-link__main:focus-visible { outline: none; }
table.data tr.row-link:has(.row-link__main:focus-visible) { box-shadow: inset 3px 0 0 var(--brand-2); background: var(--surface-hover); }
.row-link__chev { color: var(--ink-3); transition: transform var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease); }
table.data tr.row-link:hover .row-link__chev { color: var(--brand-2); transform: translateX(2px); }
table.data a.reg:not(.btn) {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-weight: 600;
  letter-spacing: 0.01em;
  text-decoration: none;
}
table.data tr.row-link:hover a.reg { color: var(--brand-2); }
table.data td.nowrap, .nowrap { white-space: nowrap; }
.code-tag {
  display: inline-block;
  margin-left: 6px;
  padding: 1px 5px;
  border-radius: var(--r-sm);
  background: var(--surface-sunken);
  border: 1px solid var(--rule);
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.04em;
  color: var(--ink-3);
  vertical-align: 1px;
}

.list-toolbar__search--wide { flex: 1 1 560px; max-width: none; }
.list-toolbar__search--wide select { flex-basis: 160px; }
.list-toolbar__search input[type="date"] { min-height: 32px; padding-block: 4px; font-size: var(--t-xs); width: auto; flex: 0 0 140px; }

/* Crew app access card (_crew_access.html). */
.crew-state { display: flex; flex-wrap: wrap; align-items: center; gap: 4px var(--s-3); }
.crew-password { margin-top: var(--s-4); padding-top: var(--s-4); border-top: 1px solid var(--rule); }

/* ---- self-explaining tables ------------------------------------------
   .th-hint: one plain line under a column heading saying what it holds or
   where it comes from. .status-key: what each status colour means, under
   the table ({% status_key %}). */
table.data th .th-hint {
  display: block;
  margin-top: 2px;
  font-family: var(--font-body);
  font-size: 10.5px;
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
  color: var(--ink-3);
  opacity: 0.85;
}
.status-key {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px var(--s-4);
  padding: var(--s-3) var(--s-4);
  border-top: 1px solid var(--rule);
  background: var(--surface-sunken);
  font-size: var(--t-xs);
  color: var(--ink-3);
}
.status-key__item { display: inline-flex; align-items: center; gap: 6px; }
.status-key__item strong { color: var(--ink-2); font-weight: 600; }
.status-key .lamp { width: 7px; height: 7px; }

/* ---- confirm dialog (base.html, data-confirm) ------------------------- */
.btn-danger-solid { background: var(--critical); border-color: var(--critical); color: #fff; }
.btn-danger-solid:hover { background: color-mix(in srgb, var(--critical) 86%, #000); border-color: color-mix(in srgb, var(--critical) 86%, #000); }
dialog.confirm {
  width: min(440px, calc(100vw - 32px));
  padding: 0;
  border: 1px solid var(--rule);
  border-radius: var(--r-lg);
  background: var(--surface);
  color: var(--ink);
  box-shadow: 0 24px 60px rgba(0, 0, 0, 0.28), 0 2px 8px rgba(0, 0, 0, 0.12);
}
dialog.confirm::backdrop { background: rgba(10, 12, 16, 0.45); backdrop-filter: blur(2px); }
dialog.confirm[open] { animation: confirm-in var(--dur-fast, 160ms) var(--ease, ease-out); }
@keyframes confirm-in { from { opacity: 0; transform: translateY(6px) scale(0.98); } }
@media (prefers-reduced-motion: reduce) { dialog.confirm[open] { animation: none; } }
.confirm__inner {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--s-3) var(--s-4);
  padding: var(--s-5);
}
.confirm__icon {
  width: 40px; height: 40px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: var(--critical-wash);
  color: var(--critical);
}
.confirm--neutral .confirm__icon { background: var(--info-wash); color: var(--info); }
.confirm__title { margin: 2px 0 6px; font-size: var(--t-md); font-weight: 600; }
.confirm__body { margin: 0; font-size: var(--t-sm); color: var(--ink-2); line-height: var(--lh-body, 1.5); }
.confirm__actions { grid-column: 1 / -1; display: flex; justify-content: flex-end; gap: var(--s-2); margin-top: var(--s-2); }

/* ---- summary cards (_summary_cards.html) -----------------------------
   Four numbers above a list, each saying what it counts and linking to the
   rows behind it. A soft wash in the card's status tone, not the tenant
   brand, so they read the same for every operator. */
.summary-cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: var(--s-3);
  margin-bottom: var(--s-5);
}
.summary-card {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: var(--s-3) var(--s-4) var(--s-3);
  border: 1px solid color-mix(in srgb, var(--sc, var(--ink-3)) 22%, var(--rule));
  border-radius: var(--r-lg);
  background:
    radial-gradient(120% 140% at 100% 0%, color-mix(in srgb, var(--sc, var(--ink-3)) 16%, transparent), transparent 60%),
    var(--surface);
  color: var(--ink);
  text-decoration: none;
  box-shadow: 0 1px 2px color-mix(in srgb, var(--ink) 6%, transparent);
  transition: transform var(--dur-fast) var(--ease), box-shadow var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease);
}
a.summary-card:hover {
  text-decoration: none;
  transform: translateY(-1px);
  border-color: color-mix(in srgb, var(--sc, var(--ink-3)) 45%, var(--rule));
  box-shadow: 0 6px 18px color-mix(in srgb, var(--sc, var(--ink)) 14%, transparent);
}
.summary-card__label {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-family: var(--font-mono);
  font-size: var(--t-2xs);
  font-weight: 600;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--ink-2);
}
.summary-card__value {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: var(--t-2xl);
  font-weight: 600;
  line-height: 1.1;
  color: var(--sc, var(--ink));
}
.summary-card.sc-none .summary-card__value, .summary-card.sc-info .summary-card__value { color: var(--ink); }
.summary-card__hint { font-size: var(--t-xs); color: var(--ink-3); }
.summary-card__go {
  position: absolute; top: var(--s-3); right: var(--s-3);
  color: var(--ink-3); opacity: 0;
  transition: opacity var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease);
}
a.summary-card:hover .summary-card__go { opacity: 1; transform: translateX(2px); color: var(--sc, var(--brand-2)); }
@media (prefers-reduced-motion: reduce) { a.summary-card:hover { transform: none; } }

/* Amenity icons in a bus-type row: small tiles, label on hover/for AT. */
.amenities { display: inline-flex; flex-wrap: wrap; gap: 4px; }
.amenity {
  display: inline-grid; place-items: center;
  width: 26px; height: 26px;
  border-radius: var(--r-sm);
  background: var(--surface-sunken);
  border: 1px solid var(--rule);
  color: var(--ink-2);
}

/* Second line inside a one-row cell: a date's time-left, a trip's route. */
.cell-sub { display: block; margin-top: 2px; font-size: var(--t-2xs); color: var(--ink-3); white-space: nowrap; }
.expiry.is-flagged { color: var(--sc); font-weight: 600; }
table.data a.next-trip:not(.btn) { font-family: var(--font-mono); font-variant-numeric: tabular-nums; font-size: var(--t-xs); }
table.data a.next-trip.is-live:not(.btn) {
  display: inline-flex; align-items: center; gap: 6px;
  font-family: var(--font-body); font-weight: 600; color: var(--sc); text-decoration: none;
}
.next-trip.is-live .lamp { box-shadow: 0 0 0 3px color-mix(in srgb, var(--sc) 22%, transparent); }
table.data a.person__name:not(.btn) {
  display: inline-block;
  max-width: 220px;
  overflow: hidden;
  text-overflow: ellipsis;
  vertical-align: middle;
  text-decoration: none;
  font-weight: 600;
}
table.data tr.row-link:hover a.person__name { color: var(--brand-2); }

/* ---- route stops: grouped line diagram --------------------------------
   One band per route in its line colour (--line, from .line-N), then its
   stops on a vertical rail with a node each — first and last stops get a
   ring like a terminus on a transit map; pass-through stops a hollow dot. */
table.data tr.route-band th {
  position: static;
  padding: 9px var(--s-4);
  background: color-mix(in srgb, var(--line, var(--ink-3)) 8%, var(--surface));
  border-top: 1px solid var(--rule-strong);
  border-bottom: 1px solid var(--rule);
  box-shadow: inset 4px 0 0 var(--line, var(--ink-3));
  font-family: inherit;
  font-size: var(--t-xs);
  font-weight: 500;
  letter-spacing: 0;
  text-transform: none;
  color: var(--ink-2);
}
table.data tbody tr.route-band:first-child th { border-top: 0; }
.route-band__inner { display: flex; flex-wrap: wrap; align-items: center; gap: 4px var(--s-3); }
.route-band__swatch { width: 10px; height: 10px; border-radius: 50%; background: var(--line, var(--ink-3)); box-shadow: 0 0 0 3px color-mix(in srgb, var(--line, var(--ink-3)) 22%, transparent); }
table.data a.route-band__name:not(.btn) { font-size: var(--t-sm); font-weight: 700; color: var(--ink); text-decoration: none; }
table.data a.route-band__name:not(.btn):hover { color: var(--brand-2); }
.route-band__ends { color: var(--ink-3); }
.route-band__meta { display: inline-flex; align-items: center; gap: var(--s-3); margin-left: auto; font-family: var(--font-mono); font-size: var(--t-2xs); color: var(--ink-3); }
.route-band__meta .btn { font-family: var(--font-body); min-height: 26px; }

table.data tr.stop-row td { padding-block: 0; height: 42px; }
.stop { display: flex; align-items: center; gap: var(--s-3); height: 42px; }
.stop__rail { position: relative; align-self: stretch; width: 14px; flex-shrink: 0; }
.stop__rail::before {
  content: ""; position: absolute; left: 50%; top: 0; bottom: 0; width: 3px; transform: translateX(-50%);
  background: var(--line, var(--ink-3)); border-radius: 2px;
}
tr.stop-row.is-first .stop__rail::before { top: 50%; }
tr.stop-row.is-last .stop__rail::before { bottom: 50%; }
tr.stop-row.is-first.is-last .stop__rail::before { display: none; }
.stop__node {
  position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
  width: 10px; height: 10px; border-radius: 50%;
  background: var(--surface); border: 3px solid var(--line, var(--ink-3));
  box-sizing: border-box;
}
tr.stop-row.is-first .stop__node, tr.stop-row.is-last .stop__node {
  width: 14px; height: 14px; background: var(--line, var(--ink-3)); border-color: var(--surface);
  box-shadow: 0 0 0 2px var(--line, var(--ink-3));
}
tr.stop-row.is-through .stop__node { width: 8px; height: 8px; border-width: 2px; border-style: dashed; }
.stop__seq { width: 18px; text-align: right; font-size: var(--t-2xs); color: var(--ink-3); }
table.data a.stop__name:not(.btn) { font-weight: 600; text-decoration: none; }
tr.stop-row.is-through a.stop__name:not(.btn) { font-weight: 500; color: var(--ink-2); }
table.data tr.stop-row:hover a.stop__name { color: var(--brand-2); }

/* ---- fare rules: legs and fare bars ------------------------------------ */
.list-toolbar__count { padding-left: var(--s-2); }
.leg { display: inline-flex; align-items: center; gap: var(--s-2); }
.leg__stop { display: inline-flex; align-items: center; gap: 6px; }
.leg__node { width: 9px; height: 9px; border-radius: 50%; border: 2px solid var(--line, var(--ink-3)); background: var(--surface); flex-shrink: 0; }
.leg__node.is-end { background: var(--line, var(--ink-3)); }
.leg__line { width: 28px; height: 3px; border-radius: 2px; background: var(--line, var(--ink-3)); opacity: 0.7; }
table.data a.leg__stop:not(.btn) { text-decoration: none; font-weight: 600; }
table.data tr.fare-row:hover a.leg__stop { color: var(--brand-2); }
.fare { display: flex; align-items: center; gap: var(--s-3); min-width: 220px; }
.fare__amount { min-width: 70px; text-align: right; font-size: var(--t-base); font-weight: 600; color: var(--ink); }
.fare__bar { flex: 1; max-width: 180px; height: 6px; border-radius: 3px; background: var(--surface-sunken); overflow: hidden; }
.fare__bar > span { display: block; height: 100%; border-radius: 3px; background: var(--line, var(--brand-2)); opacity: 0.75; }

/* ---- crew profile (drivers/conductors detail) -------------------------- */
.profile-hero {
  background:
    radial-gradient(90% 160% at 100% 0%, color-mix(in srgb, var(--sc, var(--brand-2)) 12%, transparent), transparent 60%),
    var(--surface);
}
.profile-hero__inner { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-4) var(--s-5); padding: var(--s-5); }
.profile-hero .avatar-lg { width: 72px; height: 72px; font-size: var(--t-xl); box-shadow: 0 0 0 4px var(--surface), 0 0 0 5px var(--rule); }
.profile-hero__who { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.profile-hero__name { margin: 0; font-size: var(--t-xl); line-height: 1.15; }
.profile-hero__facts { display: flex; flex-wrap: wrap; align-items: center; gap: 6px var(--s-3); margin-top: 2px; }
.profile-hero__phone { display: inline-flex; align-items: center; gap: 5px; color: var(--ink-2); text-decoration: none; font-size: var(--t-sm); }
.profile-hero__phone:hover { color: var(--brand-2); }
.profile-hero__live {
  margin-left: auto;
  display: inline-flex; align-items: center; gap: var(--s-3);
  padding: var(--s-3) var(--s-4);
  border-radius: var(--r-md);
  border: 1px solid color-mix(in srgb, var(--sc) 35%, transparent);
  background: color-mix(in srgb, var(--sc) 9%, transparent);
  color: var(--sc); text-decoration: none; font-size: var(--t-sm);
}
.profile-hero__live:hover { text-decoration: none; background: color-mix(in srgb, var(--sc) 15%, transparent); }
.profile-hero__live .lamp { width: 10px; height: 10px; box-shadow: 0 0 0 4px color-mix(in srgb, var(--sc) 22%, transparent); animation: lamp-breathe 1.8s var(--ease) infinite; }
@media (prefers-reduced-motion: reduce) { .profile-hero__live .lamp { animation: none; } }

.profile-grid { display: grid; grid-template-columns: minmax(0, 1.7fr) minmax(280px, 1fr); gap: var(--s-5); align-items: start; margin-top: var(--s-5); }
.profile-grid__main, .profile-grid__side { display: flex; flex-direction: column; gap: var(--s-5); min-width: 0; }
.profile-grid .card + .card { margin-top: 0; }
.profile-grid .card-head h2 { display: inline-flex; align-items: center; gap: var(--s-2); }
@media (max-width: 1100px) { .profile-grid { grid-template-columns: minmax(0, 1fr); } }

/* ---- routes: mini line diagram ----------------------------------------- */
.mini-line { display: inline-flex; align-items: center; vertical-align: middle; }
.mini-line__node { width: 8px; height: 8px; border-radius: 50%; background: var(--surface); border: 2px solid var(--line, var(--ink-3)); box-sizing: border-box; flex-shrink: 0; }
.mini-line__node.is-end { background: var(--line, var(--ink-3)); }
.mini-line__seg { width: 14px; height: 2px; background: var(--line, var(--ink-3)); }
a.unassigned.is-warn, .unassigned.is-warn { color: var(--caution); border-color: color-mix(in srgb, var(--caution) 45%, transparent); text-decoration: none; }

/* ---- seat layouts --------------------------------------------------------- */
.seat-mini--md .seat-mini-dot { width: 6px; height: 6px; }
.seat-mini--md { gap: 2px; padding: 4px; border-radius: var(--r-sm); background: var(--surface-sunken); border: 1px solid var(--rule); }
.seat-pattern {
  display: inline-block; padding: 2px 8px; border-radius: var(--r-pill);
  background: var(--info-wash); color: var(--info); font-weight: 600; font-size: var(--t-xs);
}
.chip-links { display: inline-flex; flex-wrap: wrap; gap: 4px; }
table.data a.chip-link:not(.btn) {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 2px 8px; border-radius: var(--r-pill);
  border: 1px solid var(--rule-strong); background: var(--surface);
  font-size: var(--t-xs); font-weight: 500; text-decoration: none; white-space: nowrap;
}
table.data a.chip-link:not(.btn) .u-num { font-size: 10px; color: var(--ink-3); }
table.data a.chip-link:not(.btn):hover { border-color: var(--brand-2); color: var(--brand-2); }
table.data a.chip-link.is-off:not(.btn) { color: var(--ink-3); border-style: dashed; }

/* ---- route page (module_detail__routes.html) --------------------------- */
.route-hero {
  background:
    radial-gradient(90% 160% at 100% 0%, color-mix(in srgb, var(--line, var(--brand-2)) 14%, transparent), transparent 60%),
    var(--surface);
  box-shadow: inset 4px 0 0 var(--line, var(--brand-2));
}
.route-hero__mark {
  width: 64px; height: 64px; flex-shrink: 0;
  display: grid; place-items: center;
  border-radius: 50%;
  background: var(--line, var(--brand-2));
  color: #fff;
  font-family: var(--font-mono); font-weight: 700; font-size: var(--t-md); letter-spacing: 0.02em; text-transform: uppercase;
  box-shadow: 0 0 0 4px var(--surface), 0 0 0 6px color-mix(in srgb, var(--line, var(--brand-2)) 35%, transparent);
}
.route-hero__ends { display: inline-flex; align-items: center; gap: var(--s-2); font-size: var(--t-sm); font-weight: 500; }
.route-hero__ends a { color: var(--ink); text-decoration: none; }
.route-hero__ends a:hover { color: var(--brand-2); }

.timetables { list-style: none; margin: 0; padding: 0; }
.timetable {
  display: grid; grid-template-columns: auto 1fr; grid-template-rows: auto auto;
  gap: 2px var(--s-3); align-items: center;
  padding: 10px var(--s-5); border-top: 1px solid var(--rule);
}
.timetable:first-child { border-top: 0; }
.timetable.is-off { opacity: 0.55; }
.timetable__time { grid-row: 1 / 3; font-size: var(--t-md); font-weight: 600; color: var(--ink); text-decoration: none; }
.timetable__time:hover { color: var(--brand-2); }
.timetable__days { display: inline-flex; gap: 3px; }
.timetable__day {
  width: 18px; height: 18px; display: grid; place-items: center;
  border-radius: 4px; font-family: var(--font-mono); font-size: 10px; font-weight: 600;
  background: var(--surface-sunken); color: var(--ink-3);
}
.timetable__day.is-on { background: var(--brand-2); color: var(--brand-2-ink); }
.timetable__meta { font-size: var(--t-2xs); color: var(--ink-3); }

/* ---- crew app sign-in presence (crew_presence) -------------------------- */
.presence--online { --sc: var(--positive); }
.presence--signed-in { --sc: var(--info); }
.presence--signed-out { --sc: var(--ink-3); }
.presence--never { --sc: var(--caution); }
.lamp-label.presence--online .lamp {
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--sc) 22%, transparent), 0 0 8px var(--sc);
  animation: lamp-breathe 1.8s var(--ease) infinite;
}
@media (prefers-reduced-motion: reduce) { .lamp-label.presence--online .lamp { animation: none; } }
.status-key + .status-key { border-top: 0; padding-top: 0; }

/* ---- maintenance records ------------------------------------------------- */
table.data tr.maint-row td:first-child { box-shadow: inset 3px 0 0 var(--sc, transparent); }
table.data a.maint-date:not(.btn) { font-weight: 600; text-decoration: none; }
.maint-work .lamp-label { color: var(--sc); }
.maint-desc { display: block; max-width: 360px; margin-top: 2px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--ink); font-size: var(--t-sm); }

/* Route stop page: the current stop on its route's diagram, and the end
   of a fare leg that is this stop. */
table.data tr.stop-row.is-current { background: color-mix(in srgb, var(--line, var(--brand-2)) 9%, transparent); }
tr.stop-row.is-current .stop__node { width: 16px; height: 16px; background: var(--line, var(--brand-2)); border-color: var(--surface); box-shadow: 0 0 0 3px var(--line, var(--brand-2)); }
.leg__stop.is-here { font-weight: 700; color: var(--ink); }
.route-chip { display: inline-flex; align-items: center; gap: 6px; margin: 0 var(--s-3) 4px 0; text-decoration: none; }

/* Maintenance record page. */
.maint-hero__mark {
  width: 64px; height: 64px; flex-shrink: 0; display: grid; place-items: center; border-radius: 50%;
  background: color-mix(in srgb, var(--sc, var(--ink-3)) 16%, var(--surface)); color: var(--sc, var(--ink-2));
  box-shadow: 0 0 0 4px var(--surface), 0 0 0 6px color-mix(in srgb, var(--sc, var(--ink-3)) 30%, transparent);
}
.maint-hero__mark .ico { width: 28px; height: 28px; }
.maint-notes { margin: 0; font-size: var(--t-base); line-height: var(--lh-body, 1.55); color: var(--ink); white-space: normal; }
table.data tr.maint-row.is-current { background: color-mix(in srgb, var(--sc, var(--brand-2)) 8%, transparent); }

/* ---- trip manifest (boarding/manifest.html) ------------------------------
   The gate screen in the shared theme: boarding progress up top, the code
   box big and focused, passengers grouped by where they get on — boarded
   rows set back, the ones still due standing out. */
.board-progress { display: block; height: 6px; margin: 6px 0 2px; border-radius: 3px; background: color-mix(in srgb, var(--positive) 14%, var(--surface-sunken)); overflow: hidden; }
.board-progress > span { display: block; height: 100%; border-radius: 3px; background: var(--positive); transition: width var(--dur-fast) var(--ease); }
.summary-card__of { font-size: var(--t-md); color: var(--ink-3); font-weight: 500; }
.board-card { margin-top: var(--s-4); border-color: color-mix(in srgb, var(--brand-2) 30%, var(--rule)); }
.board-card .card-head { gap: var(--s-3); }
.board-input { font-size: var(--t-md); min-height: 48px; letter-spacing: 0.06em; }
.seat-chip {
  display: inline-grid; place-items: center; min-width: 38px; height: 30px; padding: 0 6px;
  border-radius: var(--r-sm); border: 1px solid var(--rule-strong); background: var(--surface);
  font-weight: 700; font-size: var(--t-sm); color: var(--ink);
}
.manifest-name { font-weight: 600; color: var(--ink); }
table.data tr.manifest-row td:first-child { box-shadow: inset 3px 0 0 var(--sc, transparent); }
tr.manifest-row.is-issued .seat-chip { border-color: color-mix(in srgb, var(--caution) 55%, transparent); background: var(--caution-wash); color: var(--caution); }
tr.manifest-row.is-boarded .seat-chip { border-color: color-mix(in srgb, var(--positive) 45%, transparent); background: var(--positive-wash); color: var(--positive); }
tr.manifest-row.is-boarded .manifest-name, tr.manifest-row.is-boarded td { color: var(--ink-3); }
tr.manifest-row.is-cancelled td { color: var(--ink-3); }
tr.manifest-row.is-cancelled .manifest-name { text-decoration: line-through; }
table.data tr[hidden] { display: none; }
.card-head .seg { margin-left: auto; }

.scan-log { list-style: none; margin: 0; padding: 0; }
.scan-log__item {
  display: grid; grid-template-columns: auto 1fr auto; align-items: center; gap: var(--s-3);
  padding: 10px var(--s-5); border-top: 1px solid var(--rule);
}
.scan-log__item:first-child { border-top: 0; }
.scan-log__what strong { font-family: var(--font-mono); font-size: var(--t-xs); }
.scan-log__item.sc-bad .scan-log__what .cell-sub { color: var(--critical); }
.scan-log__when { text-align: right; font-size: var(--t-xs); color: var(--ink-2); }

/* Terminals list: route chips and counter lines. */
table.data a.route-chip-link:not(.btn) { border-color: color-mix(in srgb, var(--line, var(--ink-3)) 45%, var(--rule)); font-family: var(--font-mono); font-size: var(--t-2xs); }
.counter-line { display: block; margin-bottom: 4px; }
.counter-line:last-child { margin-bottom: 0; }

/* ---- overview (home.html) ------------------------------------------------ */
.overview-hero { background: radial-gradient(90% 160% at 100% 0%, color-mix(in srgb, var(--brand-2) 12%, transparent), transparent 60%), var(--surface); }
.delta { font-family: var(--font-mono); font-weight: 600; }
.delta--up { color: var(--positive); }
.delta--down { color: var(--critical); }
.delta--flat { color: var(--ink-3); }
.load { display: inline-flex; align-items: center; gap: var(--s-2); }
.load__bar { width: 70px; height: 6px; border-radius: 3px; background: var(--surface-sunken); overflow: hidden; }
.load__bar > span { display: block; height: 100%; background: var(--info); border-radius: inherit; }
.attention { list-style: none; margin: 0; padding: 0; }
.attention__item {
  display: grid; grid-template-columns: auto 1fr auto auto; align-items: center; gap: var(--s-3);
  padding: 11px var(--s-5); border-top: 1px solid var(--rule); color: var(--ink); text-decoration: none;
}
.attention li:first-child .attention__item { border-top: 0; }
.attention__item:hover { background: var(--surface-hover); text-decoration: none; }
.attention__item .lamp { background: var(--sc); }
.attention__label { font-size: var(--t-sm); }
.attention__count { font-weight: 700; color: var(--sc); }
.attention__item .ico { color: var(--ink-3); }
.bar-row--stacked .bar-label { white-space: normal; }
.overview-strip { margin-top: var(--s-5); padding: var(--s-3) var(--s-4); border: 1px dashed var(--rule-strong); border-radius: var(--r-md); }

/* ---- bookings list -------------------------------------------------------- */
table.data tr.booking-row td:first-child { box-shadow: inset 3px 0 0 var(--sc, transparent); }
table.data a.booking-no:not(.btn) { font-weight: 600; text-decoration: none; }
.seat-chip--sm { min-width: 30px; height: 22px; font-size: var(--t-2xs); }
.person__label { display: block; font-weight: 500; }
.paid-bar { display: block; width: 90px; height: 5px; margin-top: 4px; border-radius: 3px; background: var(--caution-wash); overflow: hidden; }
.paid-bar > span { display: block; height: 100%; background: var(--caution); border-radius: inherit; }

/* ---- payments / refunds ---------------------------------------------------- */
.amount { font-weight: 600; font-size: var(--t-base); }
tr.is-reversed .amount { text-decoration: line-through; color: var(--ink-3); }
.method-tag {
  display: inline-block; padding: 2px 8px; border-radius: var(--r-pill);
  font-size: var(--t-2xs); font-weight: 600; border: 1px solid var(--rule-strong); background: var(--surface-sunken); color: var(--ink-2);
}
.method-tag--cash { background: var(--positive-wash); color: var(--positive); border-color: color-mix(in srgb, var(--positive) 35%, transparent); }
.method-tag--mobile_banking { background: var(--info-wash); color: var(--info); border-color: color-mix(in srgb, var(--info) 35%, transparent); }
.method-tag--online_gateway, .method-tag--card { background: color-mix(in srgb, var(--brand-2) 12%, transparent); color: var(--brand-2); border-color: color-mix(in srgb, var(--brand-2) 35%, transparent); }

/* The chevron that is a row's stretched link (lists converted from a
   "View" button). */
/* A visible "View" button — the whole row opens the record too, but an arrow
   on its own wasn't obvious enough to find. */
table.data .row-link__go,
table.data a.row-link__go:not(.btn) {
  display: inline-flex; align-items: center; gap: 4px;
  min-height: 30px; padding: 0 12px;
  border: 1px solid var(--rule-strong); border-radius: var(--r-md); background: var(--surface);
  font-size: var(--t-xs); font-weight: 600; color: var(--ink); text-decoration: none;
}
table.data tr.row-link:hover .row-link__go { border-color: var(--brand-2); color: var(--brand-2); }

/* ---- lifecycle steps (trip page) ------------------------------------------ */
.steps { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-2); }
.steps__item { display: inline-flex; align-items: center; gap: var(--s-2); color: var(--ink-3); font-size: var(--t-sm); }
.steps__item + .steps__item::before { content: ""; width: 28px; height: 2px; background: var(--rule-strong); margin-right: var(--s-1); }
.steps__item.is-done + .steps__item::before { background: var(--positive); }
.steps__dot {
  width: 26px; height: 26px; display: grid; place-items: center; border-radius: 50%;
  border: 2px solid var(--rule-strong); background: var(--surface);
  font-family: var(--font-mono); font-size: var(--t-2xs); font-weight: 700;
}
.steps__item.is-done { color: var(--positive); }
.steps__item.is-done .steps__dot { background: var(--positive); border-color: var(--positive); color: #fff; }
.steps__item.is-current { color: var(--ink); font-weight: 600; }
.steps__item.is-current .steps__dot { border-color: var(--brand-2); color: var(--brand-2); box-shadow: 0 0 0 4px color-mix(in srgb, var(--brand-2) 18%, transparent); }
.steps__item.is-cancelled { text-decoration: line-through; }

/* Who's running a trip (side card). */
.crew-list { list-style: none; margin: 0; padding: 0; }
.crew-list li { display: grid; grid-template-columns: 84px 1fr auto; align-items: center; gap: var(--s-3); padding: 10px var(--s-5); border-top: 1px solid var(--rule); }
.crew-list li:first-child { border-top: 0; }
.crew-list__who { color: var(--ink); text-decoration: none; min-width: 0; }
a.crew-list__who:hover .person__label, a.crew-list__who:hover .u-num { color: var(--brand-2); }

/* ---- timeline (booking page) ---------------------------------------------- */
.timeline { list-style: none; margin: 0; padding: 0 0 0 18px; position: relative; }
.timeline::before { content: ""; position: absolute; left: 5px; top: 6px; bottom: 6px; width: 2px; background: var(--rule-strong); }
.timeline li { position: relative; display: flex; justify-content: space-between; gap: var(--s-3); padding: 6px 0; font-size: var(--t-sm); }
.timeline li::before {
  content: ""; position: absolute; left: -18px; top: 50%; transform: translateY(-50%);
  width: 12px; height: 12px; border-radius: 50%; border: 2px solid var(--rule-strong); background: var(--surface); box-sizing: border-box;
}
.timeline li.is-done::before { background: var(--positive); border-color: var(--positive); }
.timeline li.is-due::before { border-color: var(--caution); background: var(--caution-wash); }
.timeline li.is-bad::before { background: var(--critical); border-color: var(--critical); }
.timeline li.is-next::before { border-color: var(--info); }

/* ---- boarding pass (tickets/detail.html) --------------------------------
   The ticket drawn as a pass: a main panel washed in the route's line colour
   (--line) and a perforated stub with the seat, QR and verification code.
   The notches and dashed tear line are pure CSS; no images. */
.pass {
  position: relative; display: grid; grid-template-columns: minmax(0, 1fr) 220px;
  border-radius: 20px; overflow: hidden;
  background: var(--surface); border: 1px solid var(--rule);
  box-shadow: 0 10px 30px color-mix(in srgb, var(--line, var(--brand-2)) 18%, transparent);
}
.pass__main {
  padding: var(--s-5) var(--s-6);
  color: #fff;
  background:
    radial-gradient(120% 140% at 100% 0%, rgb(255 255 255 / 0.18), transparent 55%),
    linear-gradient(135deg, var(--line, var(--brand-2)), color-mix(in srgb, var(--line, var(--brand-2)) 55%, #0b1430));
}
.pass--cancelled .pass__main { filter: grayscale(0.85); }
.pass__top { display: flex; justify-content: space-between; align-items: center; gap: var(--s-3); }
.pass__brand { font-family: var(--font-display); font-weight: 700; font-size: var(--t-md); letter-spacing: -0.01em; }
.pass__status { background: rgb(255 255 255 / 0.95); padding: 3px 10px; border-radius: var(--r-pill); }

.pass__journey { display: grid; grid-template-columns: auto 1fr auto; align-items: center; gap: var(--s-4); margin: var(--s-5) 0; }
.pass__end { display: grid; gap: 2px; }
.pass__end--to { text-align: right; }
.pass__time { font-family: var(--font-mono); font-variant-numeric: tabular-nums; font-size: var(--t-3xl); font-weight: 700; line-height: 1; letter-spacing: -0.03em; }
.pass__place { font-size: var(--t-sm); font-weight: 600; opacity: 0.9; }
.pass__line { display: flex; align-items: center; gap: var(--s-2); min-width: 60px; }
.pass__line span { flex: 1; height: 0; border-top: 2px dashed rgb(255 255 255 / 0.65); }
.pass__line .ico { width: 26px; height: 26px; opacity: 0.95; }

.pass__facts { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--s-3); margin: 0; padding-top: var(--s-4); border-top: 1px solid rgb(255 255 255 / 0.25); }
.pass__facts dt { font-family: var(--font-mono); font-size: 10px; letter-spacing: var(--track-label); text-transform: uppercase; opacity: 0.75; }
.pass__facts dd { margin: 2px 0 0; font-weight: 700; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pass__tba { font-weight: 500; opacity: 0.8; font-family: var(--font-body); }

/* The stub: tear line with a notch top and bottom, then seat, QR, code. */
.pass__stub {
  position: relative; display: grid; justify-items: center; align-content: center; gap: 4px;
  padding: var(--s-5) var(--s-4); text-align: center;
  border-left: 2px dashed var(--rule-strong);
}
.pass__stub::before, .pass__stub::after {
  content: ""; position: absolute; left: -13px; width: 24px; height: 24px; border-radius: 50%;
  background: var(--paper); border: 1px solid var(--rule);
}
.pass__stub::before { top: -13px; }
.pass__stub::after { bottom: -13px; }
.pass__seat-label, .pass__code-label { font-family: var(--font-mono); font-size: 10px; letter-spacing: var(--track-label); text-transform: uppercase; color: var(--ink-3); }
.pass__seat {
  font-family: var(--font-mono); font-size: var(--t-2xl); font-weight: 800; line-height: 1;
  color: var(--line, var(--brand-2)); margin-bottom: var(--s-2);
}
.pass__qr { width: 128px; height: 128px; padding: 6px; background: #fff; border-radius: var(--r-md); border: 1px solid var(--rule); }
.pass__code { font-family: var(--font-mono); font-size: var(--t-xl); font-weight: 700; letter-spacing: 0.18em; color: var(--ink); }
.pass__number { font-family: var(--font-mono); font-size: var(--t-2xs); color: var(--ink-3); }

@media (max-width: 760px) {
  .pass { grid-template-columns: minmax(0, 1fr); }
  .pass__stub { border-left: 0; border-top: 2px dashed var(--rule-strong); }
  .pass__stub::before, .pass__stub::after { top: -13px; bottom: auto; }
  .pass__stub::before { left: -13px; }
  .pass__stub::after { left: auto; right: -13px; }
  .pass__facts { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .pass__time { font-size: var(--t-2xl); }
}

/* ---- payment receipt (module_detail__payments.html) ----------------------
   The amount on a panel tinted by how it was paid — cash green, mobile
   banking blue, card/online in the operator's colour — beside the facts.
   A reversed payment greys out and strikes its amount. */
.pay-receipt {
  display: grid; grid-template-columns: minmax(0, 0.9fr) minmax(0, 1.1fr);
  border-radius: 20px; overflow: hidden;
  background: var(--surface); border: 1px solid var(--rule);
  --tone: var(--brand-2);
  box-shadow: 0 10px 30px color-mix(in srgb, var(--tone) 16%, transparent);
}
.pay-receipt--cash { --tone: var(--positive); }
.pay-receipt--mobile_banking { --tone: var(--info); }
.pay-receipt__main {
  padding: var(--s-5) var(--s-6); color: #fff;
  background:
    radial-gradient(120% 140% at 100% 0%, rgb(255 255 255 / 0.2), transparent 55%),
    linear-gradient(135deg, var(--tone), color-mix(in srgb, var(--tone) 50%, #0b1430));
  display: grid; align-content: start; gap: 4px;
}
.pay-receipt.is-reversed .pay-receipt__main { filter: grayscale(0.9); }
.pay-receipt__top { display: flex; justify-content: space-between; align-items: center; gap: var(--s-2); margin-bottom: var(--s-4); }
.pay-receipt__top .method-tag { background: rgb(255 255 255 / 0.95); border-color: transparent; }
.pay-receipt__status { background: rgb(255 255 255 / 0.95); padding: 3px 10px; border-radius: var(--r-pill); }
.pay-receipt__label { margin: 0; font-family: var(--font-mono); font-size: 10px; letter-spacing: var(--track-label); text-transform: uppercase; opacity: 0.8; }
.pay-receipt__amount { margin: 0; font-family: var(--font-mono); font-variant-numeric: tabular-nums; font-size: var(--t-4xl); font-weight: 800; line-height: 1.05; letter-spacing: -0.03em; }
.pay-receipt.is-reversed .pay-receipt__amount { text-decoration: line-through; text-decoration-thickness: 3px; }
.pay-receipt__when { margin: var(--s-2) 0 0; font-size: var(--t-sm); opacity: 0.85; }

.pay-receipt__facts {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--s-4) var(--s-5);
  margin: 0; padding: var(--s-5) var(--s-6);
  border-left: 2px dashed var(--rule-strong);
}
.pay-receipt__facts dt { font-family: var(--font-mono); font-size: 10px; letter-spacing: var(--track-label); text-transform: uppercase; color: var(--ink-3); }
.pay-receipt__facts dd { margin: 3px 0 0; font-weight: 600; min-width: 0; overflow-wrap: anywhere; }
.pay-receipt__reversal { grid-column: 1 / -1; padding: var(--s-3); border-radius: var(--r-md); background: var(--critical-wash); }
.pay-receipt__reversal dt { color: var(--critical); }
table.data tr.is-current { background: color-mix(in srgb, var(--brand-2) 8%, transparent); }

@media (max-width: 760px) {
  .pay-receipt { grid-template-columns: minmax(0, 1fr); }
  .pay-receipt__facts { border-left: 0; border-top: 2px dashed var(--rule-strong); }
  .pay-receipt__amount { font-size: var(--t-3xl); }
}

/* Passenger page: their own seat among a booking's seats. */
.seat-chip.is-theirs { background: var(--brand-2); border-color: var(--brand-2); color: var(--brand-2-ink); }

/* Agent page: logo in the header, commission rate as a strong pill. */
.agent-logo { width: 72px; height: 72px; object-fit: contain; border-radius: 16px; background: #fff; padding: 6px; border: 1px solid var(--rule); box-shadow: 0 0 0 4px var(--surface); }
.pill-strong { display: inline-block; padding: 2px 10px; border-radius: var(--r-pill); background: var(--positive-wash); color: var(--positive); font-family: var(--font-mono); font-weight: 700; }

/* Cash variance: short in red, over in amber. */
.variance--short { color: var(--critical); font-weight: 700; }
.variance--over { color: var(--caution); font-weight: 700; }

/* ---- expense voucher (module_detail__expenses.html) ----------------------
   The paper form a cost is approved on: organisation and voucher number,
   one line of account, a total, four signature lines. Prints on A4 by
   itself (a named page — the thermal-roll @page below stays for receipts),
   with the attached receipt on the following sheet. */
.voucher {
  background: var(--surface); border: 1px solid var(--rule); border-radius: 18px;
  border-top: 6px solid var(--critical);
  padding: var(--s-5) var(--s-6); display: grid; gap: var(--s-5);
  box-shadow: 0 10px 28px color-mix(in srgb, var(--critical) 8%, transparent);
}
.voucher__head { display: flex; justify-content: space-between; align-items: flex-start; gap: var(--s-4); flex-wrap: wrap; }
.voucher__org { display: flex; align-items: center; gap: var(--s-3); }
.voucher__org strong { display: block; font-size: var(--t-lg); }
.voucher__logo { height: 44px; width: auto; max-width: 140px; object-fit: contain; }
.voucher__logo--wide { height: 52px; max-width: 170px; }
.voucher__meta { display: flex; gap: var(--s-5); margin: 0; }
.voucher__meta dt, .voucher__sign-role {
  font-family: var(--font-mono); font-size: 10px; letter-spacing: var(--track-label); text-transform: uppercase; color: var(--ink-3);
}
.voucher__meta dd { margin: 2px 0 0; font-weight: 700; }
.voucher__lines { width: 100%; border-collapse: collapse; }
.voucher__lines th {
  text-align: left; padding: var(--s-2) var(--s-3); border-bottom: 2px solid var(--ink);
  font-family: var(--font-mono); font-size: 10px; letter-spacing: var(--track-label); text-transform: uppercase; color: var(--ink-3);
}
.voucher__lines td { padding: var(--s-3); border-bottom: 1px solid var(--rule); vertical-align: top; }
.voucher__lines .num { text-align: right; white-space: nowrap; }
.voucher__lines tfoot td { border-bottom: 0; border-top: 2px solid var(--ink); font-weight: 800; font-size: var(--t-lg); }
.voucher__lines tfoot td.num { color: var(--critical); }
.voucher__note { margin: 0; font-size: var(--t-sm); color: var(--ink-2); }
.voucher__missing { color: var(--caution); }
.voucher__signs { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--s-4); padding-top: var(--s-6); }
.voucher__signs > div { display: grid; gap: 4px; text-align: center; }
.voucher__sign-name { border-bottom: 1px solid var(--ink-3); padding-bottom: 4px; font-size: var(--t-sm); font-weight: 600; min-height: 1.6em; }

.receipt-doc__frame { display: block; background: var(--surface-2, var(--bg)); border-top: 1px solid var(--rule); text-align: center; }
.receipt-doc__frame img { display: block; margin: 0 auto; max-width: 100%; max-height: 720px; object-fit: contain; }
.receipt-doc__frame--pdf iframe { display: block; width: 100%; height: 720px; border: 0; }
.receipt-doc__empty { display: flex; gap: var(--s-3); align-items: center; padding: var(--s-6) var(--s-5); margin: 0 var(--s-5) var(--s-4); border: 2px dashed var(--rule-strong); border-radius: var(--r-md); color: var(--ink-2); }
.receipt-doc__empty p { margin: 0; }
.receipt-doc__empty strong { display: block; }
.receipt-doc__upload { display: grid; gap: var(--s-2); padding: var(--s-4) var(--s-5); border-top: 1px solid var(--rule); }
.receipt-doc__upload input[type="file"] { padding: 6px; }
.receipt-doc__upload input[type="file"]::file-selector-button {
  margin-right: var(--s-3); padding: 6px 14px; border-radius: var(--r-pill); cursor: pointer;
  border: 1px solid var(--rule-strong); background: var(--surface); color: var(--ink); font: inherit; font-weight: 600;
}

@media (max-width: 760px) {
  .voucher { padding: var(--s-4); }
  /* The account line stacks: head of account, then particulars beside the
     amount — three columns don't fit a phone. */
  .voucher__lines, .voucher__lines tbody, .voucher__lines tfoot { display: block; }
  .voucher__lines thead { display: none; }
  .voucher__lines tr { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 4px var(--s-3); padding: var(--s-3) 0; }
  .voucher__lines tbody tr { border-bottom: 1px solid var(--rule); }
  .voucher__lines tfoot tr { border-top: 2px solid var(--ink); }
  .voucher__lines td, .voucher__lines tfoot td { padding: 0; border: 0; }
  .voucher__lines tbody td:first-child { grid-column: 1 / -1; }
  .voucher__lines .cell-sub { overflow-wrap: anywhere; }
  .voucher__signs { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .receipt-doc__frame--pdf iframe { height: 480px; }
}

/* Subscription invoice (module_detail__subscription-invoices.html): the
   voucher layout, its top rule and stamp in the invoice's own tone. */
.voucher--invoice { border-top-color: var(--sc, var(--brand-2)); box-shadow: 0 10px 28px color-mix(in srgb, var(--sc, var(--brand-2)) 10%, transparent); }
.voucher--invoice tfoot td.num { color: var(--ink); }
.invoice-parties { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--s-4); padding: var(--s-4); border-radius: var(--r-md); background: var(--surface-sunken); }
.invoice-parties > div { display: grid; gap: 3px; min-width: 0; }
.invoice-stamp {
  display: flex; align-items: center; gap: var(--s-3); padding: var(--s-3) var(--s-4); border-radius: var(--r-md);
  background: color-mix(in srgb, var(--sc, var(--ink-3)) 10%, transparent); border: 1px dashed color-mix(in srgb, var(--sc, var(--ink-3)) 45%, transparent);
  font-size: var(--t-sm);
}
.invoice-stamp strong { color: var(--sc, var(--ink)); }
@media (max-width: 760px) { .invoice-parties { grid-template-columns: minmax(0, 1fr); } }

@page voucher { size: A4; margin: 14mm; }
@media print {
  .voucher-grid { display: block; page: voucher; }
  .voucher-grid ~ *, .crumbs, .page-head { display: none !important; }
  .voucher { box-shadow: none; border: 1px solid #999; border-top: 6px solid #333; border-radius: 0; }
  .receipt-doc { break-before: page; border: 0; }
  .receipt-doc .card-head, .receipt-doc__empty, .receipt-doc__frame--pdf { display: none; }
  .receipt-doc__frame img { max-height: 250mm; }
  /* Plain paper: no tinted panels or coloured rules, black text and lines
     (vouchers and subscription invoices alike). */
  .voucher, .voucher *, .invoice-parties, .invoice-stamp { background: none !important; box-shadow: none !important; }
  .voucher { border-top: 2px solid #000 !important; }
  .voucher, .voucher .cell-sub, .voucher dt, .voucher__sign-role, .voucher__lines th { color: #000 !important; }
  .voucher__lines tfoot td.num, .invoice-stamp strong, .voucher__missing { color: #000 !important; }
  .invoice-parties { border: 1px solid #999; }
  .invoice-stamp { border: 1px solid #000; }
  .voucher .lamp, .invoice-stamp .lamp { display: none; }
  .voucher .lamp-label { color: #000 !important; }
}

/* ---- subscription & usage (subscriptions/detail.html) ---------------- */
.sub-hero__price { font-family: var(--font-mono); font-weight: 800; font-size: var(--t-lg); }
.sub-hero__price small { font-size: var(--t-xs); font-weight: 500; color: var(--ink-3); }
.sub-hero__period {
  margin-left: auto; display: grid; gap: 2px; min-width: min(260px, 100%);
  padding: var(--s-3) var(--s-4); border-radius: var(--r-md); background: var(--surface-sunken); border: 1px solid var(--rule);
}
.sub-hero__period .board-progress > span { background: var(--sc, var(--positive)); }
.usage-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: var(--s-3); padding: var(--s-4) var(--s-5) var(--s-5); }
.usage-tile { display: grid; gap: 4px; padding: var(--s-4); border-radius: var(--r-md); border: 1px solid var(--rule); background: var(--surface); }
.usage-tile__value { font-family: var(--font-mono); font-size: var(--t-2xl); font-weight: 800; line-height: 1.1; color: var(--sc, var(--ink)); }
.usage-tile.sc-none .usage-tile__value { color: var(--ink); }
.usage-tile__value small { font-size: var(--t-sm); font-weight: 500; color: var(--ink-3); }
.usage-tile .board-progress { background: color-mix(in srgb, var(--sc, var(--positive)) 14%, var(--surface-sunken)); }
.usage-tile .board-progress > span { background: var(--sc, var(--positive)); }
.feature-list { list-style: none; margin: 0; padding: var(--s-2) var(--s-5) var(--s-4); display: grid; gap: var(--s-3); }
.feature-list li { display: flex; gap: var(--s-3); align-items: flex-start; font-size: var(--t-sm); }
.feature-list li .ico { margin-top: 3px; flex-shrink: 0; }
.feature-list li.is-on .ico { color: var(--positive); }
.feature-list li.is-off { color: var(--ink-3); }
.feature-list li.is-off strong { font-weight: 500; text-decoration: line-through; text-decoration-color: color-mix(in srgb, var(--ink-3) 50%, transparent); }

/* ---- status lamps -----------------------------------------------------
   An indicator light in the status tone (.sc-* sets --sc). A lamp for
   something happening right now (boarding, on the road) glows and breathes;
   everything else is a steady dot. Text always accompanies it. */
.lamp {
  display: inline-block;
  width: 8px; height: 8px;
  border-radius: 50%;
  flex-shrink: 0;
  background: var(--sc, var(--ink-3));
}
.lamp-label {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-family: var(--font-mono);
  font-size: var(--t-2xs);
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--sc, var(--ink-2));
}
.trip-row--boarding .lamp,
.trip-row--departed .lamp {
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--sc) 22%, transparent), 0 0 8px var(--sc);
  animation: lamp-breathe 1.8s var(--ease) infinite;
}
@keyframes lamp-breathe { 50% { box-shadow: 0 0 0 5px color-mix(in srgb, var(--sc) 10%, transparent), 0 0 3px var(--sc); } }
@media (prefers-reduced-motion: reduce) { .trip-row .lamp { animation: none; } }

/* ---- trips: departures board ------------------------------------------
   A band per local day (trips_by_day), then one row per trip styled by its
   status: a status-coloured edge, live trips washed, finished ones set back
   and cancelled ones struck through, so what still has to run stands out. */
table.data tr.day-band th {
  position: static;
  padding: 7px var(--s-4);
  background: color-mix(in srgb, var(--ink) 4%, var(--surface));
  border-top: 1px solid var(--rule-strong);
  border-bottom: 1px solid var(--rule);
  font-family: inherit;
  font-size: var(--t-xs);
  font-weight: 500;
  letter-spacing: 0;
  text-transform: none;
  color: var(--ink-2);
}
table.data tbody tr.day-band:first-child th { border-top: 0; }
.day-band__inner { display: flex; flex-wrap: wrap; align-items: center; gap: 4px var(--s-3); }
.day-band__tag {
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 2px 7px;
  border-radius: var(--r-sm);
  background: var(--surface);
  border: 1px solid var(--rule-strong);
  color: var(--ink-2);
}
.day-band__date { font-weight: 600; color: var(--ink); }
.day-band__count { font-family: var(--font-mono); font-size: var(--t-2xs); color: var(--ink-3); }
.day-band__tally { display: inline-flex; flex-wrap: wrap; gap: 4px var(--s-3); margin-left: auto; }
.tally {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-family: var(--font-mono);
  font-size: var(--t-2xs);
  color: var(--ink-3);
}
.tally .lamp { width: 6px; height: 6px; }
tr.day-band.is-today th { box-shadow: inset 3px 0 0 var(--brand-2); background: color-mix(in srgb, var(--brand-2) 7%, var(--surface)); }
tr.day-band.is-today .day-band__tag { background: var(--brand-2); border-color: var(--brand-2); color: var(--brand-2-ink); }
tr.day-band.is-past .day-band__date { color: var(--ink-2); }

table.data tr.trip-row td { padding-block: 8px; }
table.data tr.trip-row td:first-child { box-shadow: inset 3px 0 0 var(--sc, transparent); }
.trip-time { white-space: nowrap; font-family: var(--font-mono); font-variant-numeric: tabular-nums; }
table.data .trip-time a.row-link__main {
  font-size: var(--t-base);
  font-weight: 600;
  color: var(--ink);
  text-decoration: none;
  letter-spacing: -0.01em;
}
.trip-arr { margin-left: 6px; font-size: var(--t-2xs); color: var(--ink-3); }
.trip-route { font-weight: 500; color: var(--ink); margin-left: 4px; }
.unassigned {
  font-family: var(--font-mono);
  font-size: var(--t-2xs);
  padding: 1px 6px;
  border: 1px dashed var(--rule-strong);
  border-radius: var(--r-sm);
  color: var(--ink-3);
}
table.data tr.trip-row:hover .trip-time a.row-link__main { color: var(--brand-2); }
/* Live: boarding or on the road. */
table.data tr.trip-row--boarding,
table.data tr.trip-row--departed { background: color-mix(in srgb, var(--sc) 6%, transparent); }
/* Upcoming with a gap in the roster — the thing a dispatcher has to fix. */
tr.trip-row--scheduled .unassigned { color: var(--caution); border-color: color-mix(in srgb, var(--caution) 45%, transparent); }
/* Done: set back, still legible. */
tr.trip-row--arrived td,
tr.trip-row--arrived .trip-route,
table.data tr.trip-row--arrived .trip-time a.row-link__main { color: var(--ink-3); font-weight: 500; }
tr.trip-row--cancelled td { color: var(--ink-3); }
tr.trip-row--cancelled .trip-route,
table.data tr.trip-row--cancelled .trip-time a.row-link__main { color: var(--ink-3); text-decoration: line-through; text-decoration-color: color-mix(in srgb, var(--critical) 60%, transparent); }
tr.trip-row--cancelled .person, tr.trip-row--cancelled .line-dot { opacity: 0.55; }

/* ---- seat layout thumbnail -------------------------------------------
   A postage-stamp of a seat map, for list rows where "which layout is
   this" is easier to answer by shape than by name. Capped in size by
   SeatLayout.preview_grid on the model side, so an unusually large layout
   can't blow up a table row. */
.seat-mini { display: flex; flex-direction: column; gap: 2px; flex-shrink: 0; }
.seat-mini-row { display: flex; gap: 2px; }
.seat-mini-dot { width: 5px; height: 5px; border-radius: 1.5px; background: var(--rule-strong); }
.seat-mini-dot.empty { background: transparent; }

/* ---- thermal receipt --------------------------------------------------
   The counter's print view. Not a styled web page that happens to be
   printable — a fixed-width monospace column that the browser's own print
   dialog hands to whatever printer is attached, thermal or otherwise. No
   ESC/POS driver involved.

   Both print templates (a single ticket, and every seat on a booking) used
   to carry their own copy of these rules, including the @page size. They
   are identical apart from the page break between receipts, so they live
   here once and the templates differ only in that.

   Two width subtleties worth keeping:

   * On screen, `content-box` is deliberate. With `white-space: pre` nothing
     wraps, so the 44-character rule lines that printing.py draws must have
     exactly 44ch of text area; border-box would let the padding eat into
     that and push the rules past the border, producing a horizontal
     scrollbar.
   * In print, `border-box` is equally deliberate — paper cannot scroll, so
     the risk inverts: padding must stay inside the roll's printable width. */
.receipt {
  font-family: "Courier New", ui-monospace, monospace;
  white-space: pre;
  background: #fff;
  color: #000;
  border: 1px solid var(--rule-strong);
  border-radius: var(--r-md);
  padding: var(--s-3);
  width: 44ch;
  box-sizing: content-box;
  margin: 0 auto var(--s-6);
  overflow-x: auto;
  font-size: 13px;
  line-height: 1.3;
}
/* Overrides the inherited `pre`, or this block's own source indentation
   renders as literal whitespace on top of its margin. */
.receipt-qr-block { text-align: center; margin: 0; white-space: normal; }
.receipt-qr {
  display: inline-block;
  width: 140px; height: 140px;
  background: #fff;
  padding: 6px;
  border: 1px solid var(--rule);
}
.receipt-qr-caption { font-family: inherit; font-size: 0.85em; }

@media print {
  /* An 80mm thermal roll, not the browser's default Letter/A4 — otherwise a
     short receipt prints as a few lines lost in the corner of a full sheet.
     `auto` height lets the roll cut wherever the content ends. */
  @page { size: 80mm auto; margin: 2mm; }
  .receipt {
    width: 100%;
    max-width: 76mm;
    box-sizing: border-box;
    border: none;
    border-radius: 0;
    padding: 0 3mm;
    margin: 0 auto;
    font-size: 10px;
    line-height: 1.2;
  }
  .receipt-qr { width: 100px; height: 100px; border: none; padding: 0; }
  /* One receipt per roll section when a whole booking is printed at once. */
  .receipt--sheet { page-break-after: always; break-after: page; }
  .receipt--sheet:last-of-type { page-break-after: auto; break-after: auto; }
}

/* ---- route map -------------------------------------------------------
   Three views of one route's stops. Both timelines are laid out with grid
   and normal flow — never absolute-positioned pixel maths — so a stop label
   is exactly as tall as its own content and nothing can overflow the card
   however many stops a route has or however long a terminal's name is.

   The horizontal view alternates labels above and below the line, which is
   what lets a ten-stop route fit across a screen without the labels
   colliding. */
.route-tabs {
  display: flex;
  gap: var(--s-1);
  border-bottom: 1px solid var(--rule);
  padding: 0 var(--s-5);
  overflow-x: auto;
  /* The tab strip scrolls on a narrow screen, but a visible scrollbar
     sitting on the card's top edge reads as a stray UI artefact. */
  scrollbar-width: none;
}
.route-tabs::-webkit-scrollbar { display: none; }
.route-tab {
  appearance: none;
  border: 0;
  background: none;
  padding: var(--s-3) var(--s-4);
  font-family: inherit;
  font-size: var(--t-sm);
  font-weight: 500;
  color: var(--ink-3);
  cursor: pointer;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  white-space: nowrap;
  transition: color var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease);
}
.route-tab:hover:not(:disabled) { color: var(--ink); }
.route-tab.active { color: var(--brand-2); border-bottom-color: var(--brand-2); font-weight: 600; }
.route-tab:disabled { opacity: 0.4; cursor: not-allowed; }

.route-dot {
  width: 13px; height: 13px;
  border-radius: 50%;
  background: var(--surface);
  border: 2.5px solid var(--rule-strong);
  flex-shrink: 0;
  position: relative;
  z-index: 1;
}
.route-dot.origin { border-color: var(--positive); background: var(--positive); }
.route-dot.destination { border-color: var(--critical); background: var(--critical); }

/* Horizontal: a three-row grid per stop — label-above, the line, label-below
   — so every dot lines up regardless of how tall its neighbours' labels are. */
.route-line-h {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: minmax(120px, 1fr);
  grid-template-rows: auto auto auto;
  align-items: center;
  overflow-x: auto;
  padding: var(--s-5) 0;
}
.route-h-slot { display: flex; align-items: flex-end; justify-content: center; padding: 0 var(--s-2); }
.route-h-slot.row-above { grid-row: 1; align-items: flex-end; padding-bottom: var(--s-3); }
.route-h-slot.row-below { grid-row: 3; align-items: flex-start; padding-top: var(--s-3); }
.route-h-track {
  grid-row: 2;
  display: flex;
  align-items: center;
  justify-content: center;
  position: relative;
  min-height: 22px;
}
.route-h-track::before {
  content: "";
  position: absolute;
  left: 0; right: 0; top: 50%;
  border-top: 2px solid var(--rule-strong);
}
.route-h-track.first::before { left: 50%; }
.route-h-track.last::before { right: 50%; }
.route-h-label { text-align: center; min-width: 0; }

/* Vertical: one row per stop, rail drawn on the dot column. */
.route-line-v { padding: var(--s-2) 0; }
.route-node-v {
  display: grid;
  grid-template-columns: 13px minmax(0, 1fr);
  gap: var(--s-4);
  align-items: start;
  padding-bottom: var(--s-5);
  position: relative;
}
.route-node-v:last-child { padding-bottom: 0; }
.route-node-v::before {
  content: "";
  position: absolute;
  left: 6px; top: 14px; bottom: -6px;
  border-left: 2px solid var(--rule-strong);
}
.route-node-v:last-child::before { display: none; }

.route-label-name { font-size: var(--t-sm); font-weight: 600; overflow-wrap: anywhere; }
.route-label-city { font-size: var(--t-xs); color: var(--ink-3); font-weight: 400; }
.route-label-offset {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: var(--t-2xs);
  color: var(--ink-3);
  margin-top: 2px;
}
.route-label-badges { display: flex; flex-wrap: wrap; gap: 4px; justify-content: center; margin-top: 5px; }
.route-node-v .route-label-badges { justify-content: flex-start; }

/* Geo view: a launch link out to Google Maps, plus a per-stop list. No
   embedded map — this project has no Maps API key or billing account
   configured, so a link is the only honest option. */
.route-geo-launch {
  display: flex;
  align-items: flex-start;
  gap: var(--s-3);
  padding: var(--s-4);
  border: 1px solid var(--rule-strong);
  border-radius: var(--r-md);
  background: var(--surface-sunken);
  color: var(--ink);
  text-decoration: none;
  margin-bottom: var(--s-5);
}
.route-geo-launch:hover { border-color: var(--brand-2); text-decoration: none; }
.route-geo-launch strong { display: block; font-size: var(--t-sm); }
.route-geo-launch small { display: block; color: var(--ink-3); font-size: var(--t-xs); margin-top: 2px; }
.route-geo-row {
  display: grid;
  grid-template-columns: 13px minmax(0, 1fr) auto;
  gap: var(--s-3);
  align-items: center;
  padding: var(--s-3) 0;
  border-bottom: 1px solid var(--rule);
}
.route-geo-row:last-child { border-bottom: 0; }


/* ---- seat layout builder ---------------------------------------------
   The visual editor for SeatLayout.layout_map. Unlike the booking seat map,
   an empty slot here has to be VISIBLE — the whole interaction is toggling
   cells off to carve out the gangway, and a gap you can't see is a gap you
   can't aim at. Hence the dashed outline, where the booking map uses an
   invisible .seat-gap. */
.sb-controls {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-3);
  align-items: flex-end;
  padding: var(--s-4);
  background: var(--surface-sunken);
  border: 1px solid var(--rule);
  border-radius: var(--r-md);
  margin-bottom: var(--s-4);
}
.sb-controls .field { flex: 0 0 92px; }
.sb-decks { display: flex; flex-wrap: wrap; gap: var(--s-6); }
.sb-deck { min-width: 0; }
.sb-grid { display: flex; flex-direction: column; gap: 6px; }
.sb-grid .seat-row { display: flex; gap: 6px; }
.sb-grid .seat { cursor: pointer; }
.sb-grid .seat.aisle {
  background: transparent;
  border-style: dashed;
  border-color: var(--rule-strong);
  color: transparent;
}
.sb-grid .seat.aisle:hover { border-color: var(--brand-2); background: var(--brand-2-wash); }


/* ---- horizontal field row --------------------------------------------
   A single line of fields plus a submit button, where the controls line up.

   `align-items: flex-end` on a flex row does NOT do this: it aligns the
   bottom edge of each field's BOX, so one field carrying a hint underneath
   its input pushes that input upward relative to its neighbours, and the
   labels end up on three different baselines. That is exactly what happened
   to the member-invite form.

   So the row is a three-row grid — label / control / hint — and each field
   is a subgrid spanning all three, which puts every label on one line, every
   control on one line, and hints underneath without disturbing anything.
   Fields without a hint simply leave the third row empty.

   Columns are the caller's business (they vary per form); set
   `grid-template-columns` on the element. This deliberately does not wrap —
   for a wrapping bank of filters use `.filters`, which is flex precisely
   because it has to reflow. */
.form-row {
  display: grid;
  grid-template-rows: auto auto auto;
  gap: 6px var(--s-3);
  align-items: end;
}
.form-row > .field {
  grid-row: 1 / -1;
  display: grid;
  grid-template-rows: subgrid;
  gap: 6px;
  margin: 0;
}
.form-row > .field > label { grid-row: 1; align-self: end; }
.form-row > .field > input,
.form-row > .field > select,
.form-row > .field > textarea { grid-row: 2; }
.form-row > .field > .form-hint,
.form-row > .field > .form-error { grid-row: 3; }
/* The button belongs on the control line, not at the bottom of the block. */
.form-row > .btn { grid-row: 2; }

@media (max-width: 720px) {
  /* Below this the row genuinely can't hold four columns, so it becomes a
     normal stack — where box-bottom alignment is irrelevant anyway. */
  .form-row { grid-template-columns: minmax(0, 1fr) !important; grid-template-rows: none; }
  .form-row > .field { grid-row: auto; grid-template-rows: none; }
  .form-row > .field > label,
  .form-row > .field > input,
  .form-row > .field > select,
  .form-row > .field > textarea,
  .form-row > .field > .form-hint,
  .form-row > .field > .form-error { grid-row: auto; }
  .form-row > .btn { grid-row: auto; }
}


/* ---- main column + aside ----------------------------------------------
   A 2:1 split, for pages where the main column carries a table and the
   aside carries a summary. `.grid-2` (an even auto-fit split) is the right
   tool for two peers, but it is the wrong one here: an even split at 1440px
   leaves each side ~600px, which is narrower than a seven-column seats
   table needs, so the table scrolls sideways while the summary beside it
   sits half-empty.

   minmax(0, …) rather than bare fr units — a grid item defaults to
   min-width:auto, which lets wide content push a column past its share and
   quietly undo the ratio. */
.grid-aside {
  display: grid;
  grid-template-columns: minmax(0, 2fr) minmax(0, 1fr);
  gap: var(--s-5);
  align-items: start;
}
@media (max-width: 1100px) {
  /* Below this the aside is too narrow to be useful, so it goes under the
     main column rather than squeezing it further. */
  .grid-aside { grid-template-columns: minmax(0, 1fr); }
}


/* ---- checkbox field ---------------------------------------------------
   A checkbox is a statement you toggle, not a field you fill, so it breaks
   the label-above/control-below rule the other fields follow: the box comes
   first and the label sits beside it, in sentence case rather than the
   uppercase mono every other field label uses. Clicking the text toggles it,
   which also gives the 17px box a full-width hit area. */
.field > label.check-inline {
  font-family: var(--font-body);
  font-size: var(--t-sm);
  font-weight: 400;
  letter-spacing: normal;
  text-transform: none;
  color: var(--ink);
  cursor: pointer;
  align-self: start;
  min-height: 32px;
}
.field > label.check-inline input[type="checkbox"] { flex-shrink: 0; }


/* ---- file field -------------------------------------------------------
   Django's ClearableFileInput emits its own markup as direct children of
   the field: "Currently: <link>", a clear checkbox with its own <label>, a
   <br>, then "Change: <file input>". The field's flex column would put each
   of those on a separate row, so this one field type flows normally instead
   and spaces its parts by hand. */
.field--file { display: block; }
.field--file > label:first-child { display: block; margin-bottom: 6px; }
/* A stored path can be 80 characters of UUID; let it wrap rather than push
   the form wide, and keep it visibly secondary to the control below it. */
.field--file a {
  display: inline-block;
  max-width: 100%;
  overflow-wrap: anywhere;
  font-size: var(--t-xs);
  line-height: var(--lh-snug);
}
.field--file > input[type="checkbox"] { margin: 0 6px 0 0; vertical-align: middle; }
/* The widget's own "Clear" label — a control caption, not a field name. */
.field--file > label:not(:first-child) {
  font-family: var(--font-body);
  font-size: var(--t-sm);
  font-weight: 400;
  text-transform: none;
  letter-spacing: normal;
  color: var(--ink-2);
  cursor: pointer;
  vertical-align: middle;
}
.field--file > input[type="file"] { margin-top: 6px; }
.field--file > .form-hint { display: block; margin-top: 6px; }
.field--file > .form-error { display: flex; margin-top: 6px; }

/* ---- view switch ------------------------------------------------------
   A two-option segmented control, not two buttons: the pair is one setting
   with one active value, so they share a border and the active half is
   filled rather than both sitting loose in a row. */
.view-switch {
  display: inline-flex;
  margin-bottom: var(--s-5);
  border: 1px solid var(--rule);
  border-radius: var(--r-md);
  background: var(--surface);
  overflow: hidden;
}
.view-switch__opt {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 8px var(--s-4);
  font-family: var(--font-mono);
  font-size: var(--t-2xs);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--ink-2);
  text-decoration: none;
  transition: background-color var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}
.view-switch__opt + .view-switch__opt { border-left: 1px solid var(--rule); }
.view-switch__opt:hover { background: var(--surface-hover); color: var(--ink); }
.view-switch__opt.is-on { background: var(--ink); color: var(--surface); }

/* The view switch plus a one-line note of what the list is scoped to
   (trips: "today and upcoming, N past hidden"). */
.list-scope-row { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-2) var(--s-4); margin-bottom: var(--s-5); }
.list-scope-row .view-switch { margin-bottom: 0; }
.list-scope { display: inline-flex; flex-wrap: wrap; align-items: center; gap: 6px var(--s-2); margin: 0; font-size: var(--t-xs); color: var(--ink-3); }
.list-scope a { font-weight: 600; color: var(--brand-2); }

/* ---- trips calendar ---------------------------------------------------
   A month of departures. Each cell carries a count and one dot per trip,
   coloured by status through the same .sc-* accents the tiles and badges
   use — so "amber" means BOARDING here exactly as it does everywhere else,
   and the mapping still lives only in status_tone. */
.cal__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-4);
  flex-wrap: wrap;
  margin-bottom: var(--s-4);
}
.cal__nav { display: flex; align-items: center; gap: var(--s-2); }
.cal__month {
  font-size: var(--t-lg);
  margin: 0 var(--s-2);
  min-width: 8.5ch;   /* stops the arrows shuffling between "May" and "September" */
}

.cal__grid {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: 1px;
  background: var(--rule);
  border: 1px solid var(--rule);
  border-radius: var(--r-md);
  overflow: hidden;
}
/* The 1px gap over a ruled background is what draws the grid lines, so each
   cell has to paint its own opaque ground or the rule shows through. */
.cal__wd {
  background: var(--surface-sunken);
  padding: 8px var(--s-3);
  font-family: var(--font-mono);
  font-size: var(--t-2xs);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--ink-3);
  text-align: center;
}
.cal__day {
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-height: 92px;
  padding: var(--s-3);
  background: var(--surface);
  text-decoration: none;
  color: var(--ink);
  transition: background-color var(--dur-fast) var(--ease);
}
a.cal__day:hover { background: var(--surface-hover); }
.cal__day--empty { background: var(--surface-sunken); }
/* Days from the neighbouring months keep the grid rectangular but aren't
   this month's business — dimmed, never hidden, so the weeks stay aligned. */
.cal__day--out { opacity: 0.45; }
.cal__day--past .cal__num { color: var(--ink-3); }
.cal__day--today { box-shadow: inset 0 0 0 2px var(--brand-2); }
.cal__day--on { box-shadow: inset 0 0 0 2px var(--ink); }

.cal__num {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: var(--t-sm);
  font-weight: 600;
}
.cal__dots { display: flex; flex-wrap: wrap; align-items: center; gap: 3px; }
.cal__dot {
  display: block;   /* an inline span ignores width/height */
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--sc, var(--ink-3));
  flex-shrink: 0;
}
.cal__more { font-family: var(--font-mono); font-size: var(--t-2xs); color: var(--ink-3); }
.cal__count {
  margin-top: auto;
  font-size: var(--t-2xs);
  color: var(--ink-3);
  line-height: var(--lh-snug);
}

.cal__key {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-2) var(--s-4);
  margin-top: var(--s-4);
  font-size: var(--t-2xs);
  color: var(--ink-3);
}
.cal__key-item { display: inline-flex; align-items: center; gap: 6px; }

@media (max-width: 860px) {
  /* Seven columns of 92px stop being legible well before the phone
     breakpoint; shorter cells keep the whole month on one screen. */
  .cal__day { min-height: 68px; padding: 6px; }
  .cal__count { display: none; }
  .cal__wd { padding: 6px 2px; }
}

/* Trip page: the delay and counter that terminal TV screens show. */
.board-info {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--s-3) var(--s-4);
  margin-top: var(--s-4);
  padding-top: var(--s-4);
  border-top: 1px solid var(--rule);
}
.board-info .field { width: 170px; }

/* ---- bus type page (module_detail__bus-types.html) --------------------- */
.bt-hero__mark {
  width: 64px; height: 64px; flex: none;
  display: grid; place-items: center;
  border-radius: var(--r-lg);
  background: linear-gradient(135deg, #e8590c, #f59f00);
  color: #fff;
  box-shadow: 0 8px 18px -8px rgb(232 89 12 / .6);
}
.bt-hero__mark .ico { width: 32px; height: 32px; }
.bt-hero__bn { font-size: .6em; color: var(--ink-3); font-weight: 500; }
.bt-hero__amen { display: inline-flex; gap: 6px; flex-wrap: wrap; }

/* Each amenity in its own colour, as on the booking site. */
.bt-amen--ac { --tone: #1c7ed6; } .bt-amen--wifi { --tone: #7048e8; } .bt-amen--usb_charging { --tone: #e67700; }
.bt-amen--blanket { --tone: #c2255c; } .bt-amen--water { --tone: #1098ad; } .bt-amen--meals { --tone: #e8590c; }
.bt-amen--toilet { --tone: #0c8599; } .bt-amen--entertainment { --tone: #9c36b5; } .bt-amen--reading_light { --tone: #f08c00; }
.bt-amen--gps_tracked { --tone: #2b8a3e; }
.bt-amen-dot {
  width: 28px; height: 28px; display: grid; place-items: center; border-radius: var(--r-sm);
  background: color-mix(in srgb, var(--tone) 14%, var(--surface));
  color: light-dark(var(--tone), color-mix(in oklab, var(--tone) 62%, white));
}
.bt-amen-dot .ico { width: 16px; height: 16px; }
.bt-amens { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s-2); }
.bt-amen {
  display: flex; align-items: center; gap: var(--s-2);
  padding: var(--s-2); border-radius: var(--r-md);
  border: 1px solid color-mix(in srgb, var(--tone) 30%, var(--rule));
  background: color-mix(in srgb, var(--tone) 7%, var(--surface));
  font-size: var(--t-xs); font-weight: 600; color: var(--ink);
}
.bt-amen__ico {
  width: 28px; height: 28px; flex: none; display: grid; place-items: center; border-radius: var(--r-sm);
  background: color-mix(in srgb, var(--tone) 16%, var(--surface));
  color: light-dark(var(--tone), color-mix(in oklab, var(--tone) 62%, white));
}
.bt-amen__ico .ico { width: 16px; height: 16px; }
/* Not on this type: greyed and struck, so the list also says what's missing. */
.bt-amen.is-off { --tone: #868e96; opacity: .55; font-weight: 500; text-decoration: line-through; text-decoration-color: var(--ink-3); }

/* How full, as columns: the share of seats sold each day. */
.bt-cols { display: grid; grid-template-columns: repeat(14, 1fr); gap: 6px; align-items: end; height: 190px; }
.bt-col { display: flex; flex-direction: column; align-items: center; gap: 4px; height: 100%; min-width: 0; }
.bt-col__val { font-family: var(--font-mono); font-size: 10px; color: var(--ink-3); height: 14px; }
.bt-col__track {
  flex: 1; width: 100%; max-width: 30px; display: flex; align-items: flex-end;
  border-radius: 6px; background: var(--surface-sunken); overflow: hidden;
}
.bt-col__fill { width: 100%; border-radius: 6px 6px 0 0; min-height: 3px; transition: height 400ms ease; }
.bt-col__day { font-family: var(--font-mono); font-size: 10px; color: var(--ink-3); text-align: center; line-height: 1.2; text-transform: uppercase; }
.bt-col__day b { color: var(--ink-2); font-size: 11px; }
.bt-fill--hi { background: linear-gradient(#37b24d, #2b8a3e); }
.bt-fill--mid { background: linear-gradient(#fcc419, #f08c00); }
.bt-fill--lo { background: linear-gradient(#ff8787, #e03131); }
.bt-fill--none { background: var(--rule-strong); }
.bt-legend { display: flex; flex-wrap: wrap; gap: var(--s-2) var(--s-4); margin: var(--s-4) 0 0; font-size: var(--t-xs); color: var(--ink-3); }
.bt-legend span { display: inline-flex; align-items: center; gap: 6px; }
.bt-legend i { width: 12px; height: 12px; border-radius: 3px; display: inline-block; }

/* The seat map: the bus seen from above, front at the top. */
.bt-bus {
  border: 2px solid var(--rule-strong); border-radius: 22px 22px 10px 10px;
  padding: var(--s-3); background: var(--surface-sunken);
}
.bt-bus__front {
  display: flex; align-items: center; justify-content: flex-end; gap: 4px;
  font-family: var(--font-mono); font-size: 10px; letter-spacing: .1em; text-transform: uppercase; color: var(--ink-3);
  padding-bottom: var(--s-2); margin-bottom: var(--s-2); border-bottom: 1px dashed var(--rule-strong);
}
.bt-seats { display: grid; gap: 5px; }
.bt-seat {
  aspect-ratio: 1; display: grid; place-items: center;
  border-radius: 6px 6px 3px 3px;
  background: color-mix(in srgb, #7048e8 14%, var(--surface));
  border: 1px solid color-mix(in srgb, #7048e8 35%, var(--rule));
  border-bottom-width: 3px;
  color: light-dark(#5f3dc4, #b9a7f7);
  font-family: var(--font-mono); font-size: 10px; font-weight: 700;
}
.bt-seat--sleeper { aspect-ratio: auto; min-height: 44px; background: color-mix(in srgb, #0c8599 14%, var(--surface)); border-color: color-mix(in srgb, #0c8599 35%, var(--rule)); color: light-dark(#0b7285, #66d9e8); }
.bt-aisle { aspect-ratio: 1; }

@media (max-width: 560px) {
  .bt-cols { gap: 3px; height: 160px; }
  .bt-col__val { display: none; }
}
.bt-seats { justify-content: center; }

/* Bus page: servicing as a short timeline. */
.bus-services { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--s-3); }
.bus-services li { display: flex; gap: var(--s-3); align-items: flex-start; }
.bus-services__dot {
  width: 28px; height: 28px; flex: none; display: grid; place-items: center; border-radius: var(--r-sm);
  background: color-mix(in srgb, #5c6b7a 14%, var(--surface)); color: light-dark(#495057, #ced4da);
}
.bus-services__dot .ico { width: 15px; height: 15px; }
.bus-services .cell-sub { display: block; }

/* Crew page: assign a trip. */
.assign-trip { padding: var(--s-4) var(--s-5); border-bottom: 1px solid var(--rule); background: var(--surface-sunken); }
.assign-trip[hidden] { display: none; }
.assign-trip__lede { margin: 0 0 var(--s-3); font-size: var(--t-sm); color: var(--ink-2); }
.assign-trip__list { display: grid; gap: var(--s-2); max-height: 360px; overflow: auto; padding: 2px; }
.assign-opt {
  display: grid; grid-template-columns: auto 88px minmax(0, 1fr); gap: var(--s-3); align-items: center;
  padding: var(--s-2) var(--s-3); border: 1px solid var(--rule); border-radius: var(--r-md);
  background: var(--surface); cursor: pointer;
}
.assign-opt:hover { border-color: var(--brand-2); }
.assign-opt:has(input:checked) { border-color: var(--brand-2); background: var(--brand-2-wash); }
.assign-opt input { margin: 0; accent-color: var(--brand-2); }
.assign-opt__when { display: grid; font-size: var(--t-xs); color: var(--ink-3); line-height: 1.2; }
.assign-opt__when b { font-size: var(--t-md); color: var(--ink); }
.assign-opt__what { display: grid; gap: 2px; min-width: 0; }
.assign-opt__what strong { display: inline-flex; align-items: center; gap: 6px; }
.assign-opt__free { color: var(--positive); font-weight: 600; }
.assign-opt__warn { color: var(--critical); font-weight: 600; }
.assign-opt--clash { opacity: .6; cursor: not-allowed; }
.assign-trip__actions { display: flex; gap: var(--s-2); margin-top: var(--s-3); }

/* Admin → Site pages. */
.site-pages { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: var(--s-4); }
.site-page .card-body { display: flex; flex-direction: column; height: 100%; }
.site-page__top { display: flex; gap: var(--s-3); align-items: center; margin-bottom: var(--s-3); }
.site-page__title { font-size: var(--t-lg); margin: 0 0 4px; }
.site-page__icon { width: 44px; height: 44px; flex: none; display: grid; place-items: center; border-radius: var(--r-md); }
.site-page__icon .ico { width: 22px; height: 22px; }
.site-page__icon--help { background: color-mix(in srgb, #1098ad 14%, var(--surface)); color: light-dark(#0b7285, #66d9e8); }
.site-page__icon--terms { background: color-mix(in srgb, #e8590c 14%, var(--surface)); color: light-dark(#d9480f, #ffa94d); }
.site-page__icon--privacy { background: color-mix(in srgb, #2b8a3e 14%, var(--surface)); color: light-dark(#2b8a3e, #69db7c); }
.site-page__hint { font-size: var(--t-sm); color: var(--ink-2); margin: 0 0 var(--s-3); flex: 1; }

/* ---- terminal page (module_detail__terminals.html) -------------------- */
.tm-hero__mark { background: linear-gradient(135deg, #e03131, #f76707); box-shadow: 0 8px 18px -8px rgb(224 49 49 / .6); }
.tm-fact { display: inline-flex; align-items: center; gap: 4px; font-size: var(--t-sm); color: var(--ink-2); }
/* Today's board, in the departures-board colours of the TV screens. */
.tm-board__cols { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s-4); padding: var(--s-4) var(--s-5) var(--s-5); }
.tm-side { background: #0b1426; border-radius: var(--r-lg); padding: var(--s-4); color: #f4f6fb; min-width: 0; }
.tm-side__title { display: flex; align-items: center; gap: var(--s-2); margin: 0 0 var(--s-3); font-size: var(--t-sm); letter-spacing: .12em; text-transform: uppercase; color: #fff; }
.tm-side__icon { width: 26px; height: 26px; display: grid; place-items: center; border-radius: 7px; }
.tm-side__icon .ico { width: 15px; height: 15px; }
.tm-side__title--dep .tm-side__icon { background: #ffd54a; color: #1a1300; }
.tm-side__title--arr .tm-side__icon { background: #4fc3f7; color: #002233; }
.tm-side .muted { color: #9fb0cf; }
.tm-rows { list-style: none; margin: 0; padding: 0; display: grid; gap: 2px; }
.tm-row { display: grid; grid-template-columns: 88px minmax(0, 1fr) auto; gap: var(--s-3); align-items: center; padding: 8px 10px; border-radius: 8px; background: #0e1930; }
.tm-row__time { font-family: var(--font-mono); font-weight: 700; color: #ffd54a; font-size: var(--t-md); }
.tm-row__time s { color: #8090ad; font-size: .7em; font-weight: 500; }
.tm-row__place { font-weight: 600; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tm-row__place .cell-sub { display: block; color: #9fb0cf; font-weight: 400; }
.tm-row__status { font-size: 10px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; padding: 3px 8px; border-radius: 99px; }
.tm-row.tone-ok .tm-row__status { background: #12391f; color: #6ee7a0; }
.tm-row.tone-go .tm-row__status { background: #1d6b38; color: #fff; }
.tm-row.tone-warn .tm-row__status { background: #4a3300; color: #ffc94a; }
.tm-row.tone-bad .tm-row__status { background: #4f1218; color: #ff8a93; }
.tm-row.tone-done .tm-row__status { background: #1a2338; color: #8fa0bf; }
.tm-row.tone-info .tm-row__status { background: #0d3550; color: #7fd3ff; }
/* Departures by hour of day. */
.tm-hours { display: grid; grid-template-columns: repeat(24, 1fr); gap: 3px; align-items: end; height: 150px; }
.tm-hour { display: flex; flex-direction: column; align-items: center; gap: 4px; height: 100%; }
.tm-hour__bar { flex: 1; width: 100%; display: flex; align-items: flex-end; background: var(--surface-sunken); border-radius: 4px; overflow: hidden; }
.tm-hour__fill { width: 100%; border-radius: 4px 4px 0 0; }
.tm-hour__label { font-family: var(--font-mono); font-size: 10px; color: var(--ink-3); height: 12px; }
.tm-hour--morning { background: linear-gradient(#ffa94d, #f76707); }
.tm-hour--day { background: linear-gradient(#ffe066, #f59f00); }
.tm-hour--evening { background: linear-gradient(#f783ac, #c2255c); }
.tm-hour--night { background: linear-gradient(#748ffc, #4263eb); }
/* Routes through: the part this terminal plays. */
.tm-role { display: inline-block; padding: 2px 8px; border-radius: 99px; font-size: var(--t-2xs); font-weight: 700; letter-spacing: .06em; text-transform: uppercase; }
.tm-role--start { background: color-mix(in srgb, #2b8a3e 14%, var(--surface)); color: light-dark(#2b8a3e, #69db7c); }
.tm-role--end { background: color-mix(in srgb, #c2255c 14%, var(--surface)); color: light-dark(#c2255c, #f783ac); }
.tm-role--stop { background: color-mix(in srgb, #1c7ed6 14%, var(--surface)); color: light-dark(#1971c2, #74c0fc); }
.tm-tag { display: inline-block; margin-right: 4px; padding: 1px 7px; border-radius: 5px; font-size: var(--t-2xs); font-weight: 700; }
.tm-tag--on { background: color-mix(in srgb, #2b8a3e 14%, var(--surface)); color: light-dark(#2b8a3e, #69db7c); }
.tm-tag--off { background: color-mix(in srgb, #e8590c 14%, var(--surface)); color: light-dark(#d9480f, #ffa94d); }
.tm-map { overflow: hidden; padding: 0; }
.tm-map iframe { display: block; width: 100%; height: 240px; border: 0; }
.tm-list { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--s-3); }
.tm-list li { display: flex; gap: var(--s-3); align-items: center; }
.tm-list__main { flex: 1; min-width: 0; display: grid; }
.tm-list__ico { width: 32px; height: 32px; flex: none; display: grid; place-items: center; border-radius: var(--r-sm); }
.tm-list__ico .ico { width: 16px; height: 16px; }
.tm-list__ico--counter { background: color-mix(in srgb, #2b8a3e 14%, var(--surface)); color: light-dark(#2b8a3e, #69db7c); }
.tm-list__ico--screen { background: color-mix(in srgb, #9c36b5 14%, var(--surface)); color: light-dark(#9c36b5, #e599f7); }
.tm-list__ico--device { background: color-mix(in srgb, #1c7ed6 14%, var(--surface)); color: light-dark(#1971c2, #74c0fc); }
@media (max-width: 760px) {
  .tm-board__cols { grid-template-columns: minmax(0, 1fr); }
  .tm-hours { gap: 1px; }
}

/* ---- fare rule page (module_detail__fare-rules.html) -------------------- */
.fr-hero__mark { background: linear-gradient(135deg, #2b8a3e, #94d82d); box-shadow: 0 8px 18px -8px rgb(43 138 62 / .6); }
.fr-hero__arrow { color: #2b8a3e; }
.fr-hero__fare {
  margin-left: auto; padding: var(--s-3) var(--s-5); border-radius: var(--r-lg);
  font-family: var(--font-display); font-size: var(--t-2xl); font-weight: 700;
  background: color-mix(in srgb, #2b8a3e 12%, var(--surface)); color: light-dark(#2b8a3e, #69db7c);
}
/* The route as a line of stops; this fare's stretch drawn in green. */
.fr-line { list-style: none; margin: 0; padding: 0; display: flex; overflow-x: auto; padding-bottom: var(--s-2); }
.fr-line li { position: relative; flex: 1 0 96px; display: grid; justify-items: center; gap: 6px; text-align: center; padding-top: 4px; }
.fr-line li::before, .fr-line li::after { content: ""; position: absolute; top: 11px; height: 4px; background: var(--rule-strong); }
.fr-line li::before { left: 0; right: 50%; }
.fr-line li::after { left: 50%; right: 0; }
.fr-line li:first-child::before, .fr-line li:last-child::after { display: none; }
.fr-line li.seg-in::before, .fr-line li.seg-out::after { background: #37b24d; }
.fr-line__dot { position: relative; z-index: 1; width: 18px; height: 18px; border-radius: 50%; background: var(--surface); border: 3px solid var(--rule-strong); }
.fr-line li.is-on .fr-line__dot { border-color: #37b24d; }
.fr-line li.is-end .fr-line__dot { background: #37b24d; border-color: #2b8a3e; box-shadow: 0 0 0 4px color-mix(in srgb, #37b24d 25%, transparent); }
.fr-line__name { font-size: var(--t-xs); font-weight: 600; color: var(--ink-2); }
.fr-line li.is-on .fr-line__name { color: var(--ink); }
.fr-line__time { font-family: var(--font-mono); font-size: 10px; color: var(--ink-3); }
/* The fare ladder. */
.fr-ladder { display: grid; gap: var(--s-2); }
.fr-rung {
  display: grid; grid-template-columns: minmax(0, 1.2fr) minmax(0, 1fr) 96px; gap: var(--s-3); align-items: center;
  padding: var(--s-2) var(--s-3); border-radius: var(--r-md); color: inherit; text-decoration: none; border: 1px solid transparent;
}
.fr-rung:hover { background: var(--surface-sunken); text-decoration: none; }
.fr-rung.is-this { border-color: color-mix(in srgb, #2b8a3e 45%, var(--rule)); background: color-mix(in srgb, #2b8a3e 8%, var(--surface)); }
.fr-rung__leg { font-size: var(--t-sm); font-weight: 600; min-width: 0; }
.fr-rung__bar { height: 10px; border-radius: 99px; background: var(--surface-sunken); overflow: hidden; }
.fr-rung__bar span { display: block; height: 100%; border-radius: inherit; background: linear-gradient(90deg, #74c0fc, #1c7ed6); }
.fr-rung.is-this .fr-rung__bar span { background: linear-gradient(90deg, #8ce99a, #2b8a3e); }
.fr-rung__fare { font-family: var(--font-mono); font-weight: 700; text-align: right; }
.fr-fill { background: linear-gradient(#69db7c, #2b8a3e); }
.fr-cols { height: 170px; }
.fr-type { background: color-mix(in srgb, #1c7ed6 14%, var(--surface)); color: light-dark(#1971c2, #74c0fc); }
.fr-type.is-this { background: color-mix(in srgb, #2b8a3e 16%, var(--surface)); color: light-dark(#2b8a3e, #69db7c); }
.fr-type__fare { font-family: var(--font-mono); font-weight: 700; }
@media (max-width: 640px) {
  .fr-rung { grid-template-columns: minmax(0, 1fr) 90px; }
  .fr-rung__bar { grid-column: 1 / -1; grid-row: 2; }
  .fr-hero__fare { margin-left: 0; }
}

/* ---- schedule page (module_detail__schedules.html) -------------------- */
.sc-hero__mark { background: linear-gradient(135deg, #4263eb, #15aabf); box-shadow: 0 8px 18px -8px rgb(66 99 235 / .6); }
.sc-hero__time {
  display: inline-block; margin-right: 6px; padding: 2px 10px; border-radius: var(--r-md);
  background: #1b2230; color: #ffd54a; font-family: var(--font-mono); font-size: .85em;
}
.sc-week { display: inline-flex; gap: 3px; }
.sc-day {
  width: 26px; height: 22px; display: grid; place-items: center; border-radius: 5px;
  font-family: var(--font-mono); font-size: 10px; font-weight: 700;
  background: var(--surface-sunken); color: var(--ink-3);
}
.sc-day.is-on { background: linear-gradient(#4c6ef5, #4263eb); color: #fff; }
/* Four weeks as a calendar. */
.sc-cal { display: grid; grid-template-columns: repeat(7, 1fr); gap: 6px; }
.sc-cal__dow { font-family: var(--font-mono); font-size: 10px; letter-spacing: .1em; text-transform: uppercase; color: var(--ink-3); text-align: center; }
.sc-cal__day {
  position: relative; min-height: 48px; border-radius: var(--r-md); padding: 6px 8px;
  display: flex; align-items: flex-start; justify-content: space-between; gap: 4px;
  font-family: var(--font-mono); font-size: var(--t-sm); border: 1px solid transparent;
}
.sc-cal__day small { font-size: 10px; opacity: .75; text-transform: uppercase; }
.sc-cal--made { background: linear-gradient(135deg, #4c6ef5, #4263eb); color: #fff; }
.sc-cal--missing { background: repeating-linear-gradient(-45deg, color-mix(in srgb, #f59f00 22%, var(--surface)) 0 6px, color-mix(in srgb, #f59f00 10%, var(--surface)) 6px 12px); color: light-dark(#a35d00, #ffc94a); border-color: color-mix(in srgb, #f59f00 50%, var(--rule)); }
.sc-cal--ran { background: color-mix(in srgb, #2b8a3e 18%, var(--surface)); color: light-dark(#2b8a3e, #69db7c); }
.sc-cal--past { background: color-mix(in srgb, #e03131 12%, var(--surface)); color: light-dark(#c92a2a, #ff8787); }
.sc-cal--off { background: var(--surface-sunken); color: var(--ink-3); }
.sc-cal__day.is-today { box-shadow: 0 0 0 2px var(--surface), 0 0 0 4px #15aabf; }
.bt-legend i.sc-cal--made, .bt-legend i.sc-cal--missing, .bt-legend i.sc-cal--ran, .bt-legend i.sc-cal--off { min-height: 0; padding: 0; border-radius: 3px; }
.sc-warn { color: var(--critical); font-weight: 600; font-size: var(--t-xs); }
/* The timetable, stop by stop. */
.sc-tt { list-style: none; margin: 0; padding: 0; }
.sc-tt li { display: grid; grid-template-columns: 58px 18px minmax(0, 1fr); gap: var(--s-2); align-items: start; position: relative; padding-bottom: var(--s-3); }
.sc-tt li::before { content: ""; position: absolute; left: 66px; top: 10px; bottom: -4px; width: 3px; background: linear-gradient(#4263eb, #15aabf); }
.sc-tt li.is-last::before { display: none; }
.sc-tt__time { font-family: var(--font-mono); font-weight: 700; color: var(--ink); }
.sc-tt__time sup { color: var(--caution); font-size: 9px; }
.sc-tt__dot { position: relative; z-index: 1; width: 13px; height: 13px; margin-top: 4px; border-radius: 50%; background: var(--surface); border: 3px solid #4263eb; }
.sc-tt li.is-first .sc-tt__dot, .sc-tt li.is-last .sc-tt__dot { background: #4263eb; }
.sc-tt__name { font-weight: 600; font-size: var(--t-sm); }
.sc-tt__name .cell-sub { display: block; font-weight: 400; }

/* ---- passengers list ------------------------------------------------------ */
table.data th.pax-ava, table.data td.pax-ava { width: 1%; padding-right: 0; }
table.data tr.pax-row td { vertical-align: middle; }
tr.pax-row .lamp-label { vertical-align: middle; }
.pax-when { margin-right: var(--s-2); color: var(--ink-3); font-size: var(--t-xs); }
.pax-trips {
  display: inline-block; min-width: 3ch; text-align: center; padding: 1px 8px; border-radius: 999px;
  background: color-mix(in srgb, var(--ink) 7%, transparent); color: var(--ink); font-weight: 600;
}
.pax-trips.is-repeat { background: color-mix(in srgb, var(--brand-2) 16%, transparent); color: var(--brand-2); }

/* ---- promo page (module_detail__promos.html) ---------------------------- */
/* The code as a coupon: the details on the left, the discount on a
   perforated stub on the right, notched where the two meet. */
.pr-coupon { display: flex; flex-wrap: wrap; overflow: hidden; padding: 0; position: relative;
  background:
    radial-gradient(80% 160% at 0% 0%, color-mix(in srgb, #d6336c 9%, transparent), transparent 60%),
    var(--surface); }
.pr-coupon__main { flex: 1 1 420px; display: flex; align-items: center; gap: var(--s-4) var(--s-5); padding: var(--s-5); min-width: 0; }
.pr-hero__mark { background: linear-gradient(135deg, #d6336c, #f76707); box-shadow: 0 8px 18px -8px rgb(214 51 108 / .6); }
.pr-code {
  display: inline-block; align-self: flex-start; margin: 4px 0 2px; padding: 4px 14px;
  font-family: var(--font-mono); font-size: var(--t-2xl); font-weight: 700; letter-spacing: .12em; line-height: 1.2;
  border: 2px dashed color-mix(in srgb, #d6336c 55%, var(--rule)); border-radius: var(--r-md);
  background: color-mix(in srgb, #d6336c 6%, var(--surface)); color: var(--ink);
}
.pr-desc { margin: 0; color: var(--ink-2); font-size: var(--t-sm); }
.pr-desc__bn { color: var(--ink-3); }
.pr-coupon__stub {
  position: relative; flex: 0 0 210px; display: grid; place-content: center; justify-items: center; gap: 0;
  padding: var(--s-5) var(--s-4); text-align: center; color: #fff;
  background: linear-gradient(150deg, #d6336c, #f76707);
  border-left: 2px dashed rgb(255 255 255 / .55);
}
.pr-coupon__stub::before, .pr-coupon__stub::after {
  content: ""; position: absolute; left: -13px; width: 24px; height: 24px; border-radius: 50%; background: var(--paper);
}
.pr-coupon__stub::before { top: -12px; }
.pr-coupon__stub::after { bottom: -12px; }
.pr-coupon.sc-none .pr-coupon__stub { background: linear-gradient(150deg, #868e96, #adb5bd); }
.pr-stub__value { font-family: var(--font-display); font-size: 2.6rem; font-weight: 800; line-height: 1; letter-spacing: -.02em; }
.pr-stub__off { font-family: var(--font-mono); font-size: var(--t-sm); font-weight: 700; letter-spacing: .3em; text-transform: uppercase; }
.pr-coupon__stub small { margin-top: 4px; font-size: var(--t-xs); opacity: .85; }
@media (max-width: 640px) {
  .pr-coupon__stub { flex-basis: 100%; border-left: 0; border-top: 2px dashed rgb(255 255 255 / .55); }
  .pr-coupon__stub::before, .pr-coupon__stub::after { display: none; }
}

/* Savings at different spends: each bar is the spend, its pink end the discount. */
.pr-saves { display: grid; gap: var(--s-3); }
.pr-save { display: grid; grid-template-columns: 92px minmax(0, 1fr) 170px; gap: var(--s-3); align-items: center; }
.pr-save__spend { font-weight: 700; text-align: right; }
.pr-save__track { height: 22px; }
.pr-save__bar { display: flex; height: 100%; border-radius: 6px; overflow: hidden; min-width: 8px; }
.pr-save__paid { flex: 1; background: color-mix(in srgb, #1c7ed6 22%, var(--surface-sunken)); }
.pr-save__off { flex: none; background: linear-gradient(90deg, #f06595, #d6336c); }
.pr-save.is-below .pr-save__paid { background: repeating-linear-gradient(135deg, var(--surface-sunken) 0 6px, color-mix(in srgb, var(--ink) 8%, transparent) 6px 12px); }
.pr-save.is-below .pr-save__spend { color: var(--ink-3); }
.pr-save__what { font-size: var(--t-sm); }
.pr-save__amt { color: light-dark(#c2255c, #faa2c1); }
.pr-cap { display: inline-block; padding: 0 6px; border-radius: 99px; font-family: var(--font-mono); font-size: 10px; font-weight: 700;
  text-transform: uppercase; letter-spacing: .06em; background: color-mix(in srgb, #f76707 16%, transparent); color: light-dark(#d9480f, #ffa94d); }
.pr-key--paid { background: color-mix(in srgb, #1c7ed6 22%, var(--surface-sunken)); }
.pr-key--off { background: linear-gradient(90deg, #f06595, #d6336c); }
@media (max-width: 640px) {
  .pr-save { grid-template-columns: 76px minmax(0, 1fr); }
  .pr-save__what { grid-column: 2; }
}

/* Validity window, with today marked. */
.pr-window__track { position: relative; height: 12px; border-radius: 99px; background: var(--surface-sunken); margin: var(--s-5) 0 var(--s-3); }
.pr-window__fill { position: absolute; inset: 0 auto 0 0; border-radius: inherit; background: linear-gradient(90deg, #f06595, #f76707); }
.pr-window.sc-none .pr-window__fill { background: linear-gradient(90deg, #ced4da, #868e96); }
.pr-window__now { position: absolute; top: -6px; width: 3px; height: 24px; margin-left: -1px; border-radius: 2px; background: var(--ink); }
.pr-window__now b { position: absolute; bottom: 100%; left: 50%; transform: translateX(-50%); margin-bottom: 2px;
  font-family: var(--font-mono); font-size: 10px; text-transform: uppercase; letter-spacing: .06em; white-space: nowrap; }
.pr-window__ends { display: flex; justify-content: space-between; gap: var(--s-4); }
.pr-window__ends > span { display: grid; gap: 2px; font-size: var(--t-sm); }
.pr-open { display: flex; align-items: center; gap: var(--s-2); margin: 0; font-size: var(--t-sm); color: var(--ink-2); }
.pr-open .ico { width: 18px; height: 18px; color: #d6336c; flex: none; }

/* Uses per day, 30 columns. */
.pr-cols { grid-template-columns: repeat(30, 1fr); gap: 3px; height: 170px; }
.pr-cols .bt-col__val { font-size: 9px; }
.pr-fill { background: linear-gradient(#f06595, #d6336c); }
.pr-saved { color: light-dark(#c2255c, #faa2c1); }

/* Uses gauge. */
.pr-gauge-wrap { display: grid; justify-items: center; gap: var(--s-3); }
.pr-gauge {
  --p: 0; width: 150px; height: 150px; border-radius: 50%; display: grid; place-items: center;
  background: conic-gradient(#d6336c calc(var(--p) * 1%), var(--surface-sunken) 0);
}
.pr-gauge--open { background: conic-gradient(from 0deg, #f06595, #f76707, #fcc419, #f06595); }
.pr-gauge > span { width: 112px; height: 112px; border-radius: 50%; background: var(--surface); display: grid; place-content: center; text-align: center; }
.pr-gauge b { font-size: var(--t-2xl); line-height: 1; }
.pr-gauge small { color: var(--ink-3); font-size: var(--t-xs); }
.pr-gauge__note { margin: 0; font-size: var(--t-sm); color: var(--ink-2); text-align: center; }

/* Routes it's used on. */
.pr-routes { display: grid; gap: var(--s-3); }
.pr-route { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 4px var(--s-3); color: inherit; text-decoration: none; }
.pr-route:hover .pr-route__name { color: var(--brand-2); }
.pr-route__name { font-weight: 600; font-size: var(--t-sm); min-width: 0; }
.pr-route__n { font-weight: 700; align-self: center; }
.pr-route__bar { grid-column: 1 / -1; height: 6px; border-radius: 99px; background: var(--surface-sunken); overflow: hidden; }
.pr-route__bar span { display: block; height: 100%; border-radius: inherit; background: linear-gradient(90deg, #f06595, #f76707); }

/* ---- cash session page (module_detail__cash-sessions.html) -------------- */
.cs-hero__mark { background: linear-gradient(135deg, #0c8599, #20c997); box-shadow: 0 8px 18px -8px rgb(12 133 153 / .6); }
.cs-verdict {
  margin-left: auto; display: inline-flex; align-items: center; gap: var(--s-2);
  padding: var(--s-3) var(--s-5); border-radius: var(--r-lg);
  font-family: var(--font-display); font-size: var(--t-xl); font-weight: 700;
}
.cs-verdict .ico { width: 22px; height: 22px; }
.cs-verdict--balanced { background: color-mix(in srgb, #2f9e44 13%, var(--surface)); color: light-dark(#2b8a3e, #69db7c); }
.cs-verdict--short { background: color-mix(in srgb, #e03131 12%, var(--surface)); color: light-dark(#c92a2a, #ff8787); }
.cs-verdict--over { background: color-mix(in srgb, #f08c00 14%, var(--surface)); color: light-dark(#d9480f, #ffc078); }

/* The drawer check: float + cash taken against what was counted, one scale. */
.cs-drawer { display: grid; gap: var(--s-4); }
.cs-drawer__row { display: grid; grid-template-columns: 130px minmax(0, 1fr); gap: var(--s-3); align-items: center; }
.cs-drawer__label { display: grid; font-size: var(--t-xs); color: var(--ink-3); text-transform: uppercase; letter-spacing: .06em; font-family: var(--font-mono); }
.cs-drawer__label strong { font-size: var(--t-lg); color: var(--ink); letter-spacing: 0; text-transform: none; }
.cs-drawer__track { display: flex; height: 34px; border-radius: 8px; background: var(--surface-sunken); overflow: hidden; }
.cs-seg { display: flex; align-items: center; padding: 0 10px; min-width: 0; overflow: hidden; white-space: nowrap;
  font-family: var(--font-mono); font-size: 11px; font-weight: 700; color: #fff; }
.cs-seg--float, .cs-key--float { background: linear-gradient(90deg, #4dabf7, #1c7ed6); }
.cs-seg--cash, .cs-key--cash { background: linear-gradient(90deg, #38d9a9, #0ca678); }
.cs-seg--counted.cs-seg--balanced, .cs-key--counted.cs-seg--balanced { background: repeating-linear-gradient(135deg, #2f9e44 0 10px, #37b24d 10px 20px); }
.cs-seg--counted.cs-seg--short, .cs-key--counted.cs-seg--short { background: repeating-linear-gradient(135deg, #e03131 0 10px, #f03e3e 10px 20px); }
.cs-seg--counted.cs-seg--over, .cs-key--counted.cs-seg--over { background: repeating-linear-gradient(135deg, #f08c00 0 10px, #f59f00 10px 20px); }
.cs-drawer__wait { align-self: center; padding: 0 var(--s-3); font-size: var(--t-xs); color: var(--ink-3); }
.cs-fill { background: linear-gradient(#38d9a9, #0ca678); }
.cs-cols { height: 170px; }
.cs-cols .bt-col__val { font-size: 9px; white-space: nowrap; }
@media (max-width: 640px) { .cs-drawer__row { grid-template-columns: 1fr; } .cs-verdict { margin-left: 0; } }

/* Takings by route: one colour per route, shared by the bar and its dots. */
.cs-methods { display: flex; height: 14px; border-radius: 99px; overflow: hidden; background: var(--surface-sunken); }
.cs-dot { width: 12px; height: 12px; border-radius: 4px; flex: none; }
.cs-c0 { background: #0ca678; } .cs-c1 { background: #1c7ed6; } .cs-c2 { background: #f08c00; }
.cs-c3 { background: #d6336c; } .cs-c4 { background: #7048e8; }
.cs-amt { font-weight: 700; }
tr.cs-reversed .cs-amt { text-decoration: line-through; color: var(--ink-3); }
.cs-close { border-color: color-mix(in srgb, #0ca678 40%, var(--rule)); }

/* ---- Booking seat, ticket delivery, boarding scan, commission and rating
   pages (module_detail__booking-seats … __trip-ratings.html) ------------ */
/* Seat map: this seat, and the trip's other taken seats. */
.bt-seat.is-this { background: linear-gradient(#ffa94d, #f76707); border-color: #d9480f; color: #fff; box-shadow: 0 0 0 3px color-mix(in srgb, #f76707 30%, transparent); }
.bt-seat.is-taken { background: var(--surface-sunken); border-color: var(--rule-strong); color: var(--ink-3); }
.seat-key { display: flex; flex-wrap: wrap; gap: var(--s-4); justify-content: center; }
.seat-key > span { display: inline-flex; align-items: center; gap: 6px; }
.seat-key .bt-seat { width: 16px; height: 16px; box-shadow: none; }
.seat-chip-sm { width: 30px; height: 30px; flex-shrink: 0; font-size: 10px; }

/* The passenger's leg: the whole route, their part drawn bold. */
.seat-leg { list-style: none; margin: 0; padding: 0; }
.seat-leg__stop { display: grid; grid-template-columns: 46px 14px minmax(0, 1fr); gap: var(--s-3); align-items: start; padding-bottom: var(--s-4); position: relative; color: var(--ink-3); }
.seat-leg__stop:last-child { padding-bottom: 0; }
.seat-leg__stop::before { content: ""; position: absolute; left: calc(46px + var(--s-3) + 6px); top: 14px; bottom: -4px; border-left: 2px dotted var(--rule-strong); }
.seat-leg__stop:last-child::before { display: none; }
.seat-leg__stop.is-board::before, .seat-leg__stop.is-ride::before { border-left: 3px solid #f76707; left: calc(46px + var(--s-3) + 5px); }
.seat-leg__time { font-size: var(--t-xs); padding-top: 1px; }
.seat-leg__dot { width: 14px; height: 14px; border-radius: 50%; border: 2px solid var(--rule-strong); background: var(--surface); position: relative; z-index: 1; margin-top: 2px; }
.seat-leg__stop.is-ride .seat-leg__dot { border-color: #f76707; }
.seat-leg__stop.is-board .seat-leg__dot, .seat-leg__stop.is-drop .seat-leg__dot { border-color: #f76707; background: #f76707; }
.seat-leg__name { font-size: var(--t-sm); display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
.seat-leg__name .cell-sub { flex-basis: 100%; }
.seat-leg__stop.is-board, .seat-leg__stop.is-ride, .seat-leg__stop.is-drop { color: var(--ink); }
.seat-leg__stop.is-board .seat-leg__name, .seat-leg__stop.is-drop .seat-leg__name { font-weight: 700; }

/* A failed delivery's reason. */
.note-box { padding: var(--s-3) var(--s-4); border-radius: var(--r-md, 8px); border: 1px solid var(--rule); background: var(--surface-sunken); font-size: var(--t-sm); }
.note-box--bad { border-color: color-mix(in srgb, var(--critical) 40%, var(--rule)); background: color-mix(in srgb, var(--critical) 7%, var(--surface)); }
.note-box--bad strong { color: var(--critical); }

/* The current row in a log of its siblings. */
.scan-log__item.is-current { background: color-mix(in srgb, var(--sc, var(--brand-2)) 9%, transparent); border-radius: 6px; }
.scan-log__item.is-current .scan-log__what strong { color: var(--ink); }

/* A scan's verdict, big, in its result's tone. */
.scan-verdict { display: flex; align-items: center; gap: var(--s-4); padding: var(--s-4) var(--s-5); border-radius: var(--r-lg, 12px); border: 1px solid color-mix(in srgb, var(--sc) 40%, var(--rule)); background: color-mix(in srgb, var(--sc) 9%, var(--surface)); }
.scan-verdict__icon { width: 52px; height: 52px; border-radius: 50%; display: grid; place-items: center; background: var(--sc); color: #fff; flex-shrink: 0; }
.scan-verdict__title { margin: 0; font-size: var(--t-xl, 1.5rem); font-weight: 700; color: var(--ink); }
.scan-verdict__sub { margin: 2px 0 0; font-size: var(--t-xs); color: var(--ink-2); }

/* A commission against its fare. */
.ac-split { display: flex; height: 30px; border-radius: 8px; overflow: hidden; background: var(--surface-sunken); }
.ac-split__agent { background: linear-gradient(90deg, #f59f00, #f08c00); min-width: 6px; }
.ac-split__keep { flex: 1; background: linear-gradient(90deg, #4dabf7, #1c7ed6); }
.ac-split__key { display: flex; flex-wrap: wrap; gap: var(--s-2) var(--s-5); margin-top: var(--s-3); font-size: var(--t-sm); }
.ac-split__key > span { display: inline-flex; align-items: center; gap: 6px; }
.ac-swatch { width: 12px; height: 12px; border-radius: 3px; }
.ac-swatch--agent { background: #f08c00; }
.ac-swatch--keep { background: #1c7ed6; }
.ac-month { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--s-3); margin-bottom: var(--s-2); }
.ac-month > div { display: grid; gap: 2px; }
.ac-month strong { font-size: var(--t-lg); }

/* A rating as the passenger's quote; this score's bar on the route's spread. */
.rating-quote { padding: var(--s-5); border-radius: var(--r-lg, 12px); border: 1px solid color-mix(in srgb, var(--sc) 35%, var(--rule)); background: color-mix(in srgb, var(--sc) 7%, var(--surface)); }
.rating-quote__stars { margin: 0; font-size: 1.9rem; letter-spacing: 3px; }
.rating-quote__text { margin: var(--s-3) 0 0; font-size: var(--t-lg); line-height: 1.45; color: var(--ink); }
.rating-quote__who { margin: var(--s-3) 0 0; font-size: var(--t-xs); color: var(--ink-3); }
.bar-row.is-this .bar-fill { background: var(--caution); }
.bar-row.is-this .bar-value { font-weight: 700; }
@media (max-width: 560px) {
  .ac-month strong { font-size: var(--t-md); }
  .scan-verdict { padding: var(--s-3) var(--s-4); }
}
