/* ============================================================
   DPSR — 103rd Infantry (MABALASIK) Battalion
   Palette taken from the unit seal: black, gold, silver.

   Dark is the default, full stop — not "follows the OS," a fixed choice,
   because this runs on ops-room and field-adjacent screens where a bright
   white page is the wrong thing to be looking at. Light is available as a
   deliberate opt-in via the toggle in the topbar (session.js), which sets
   data-theme="light" on <html> and remembers it in localStorage. Only the
   values that actually change between the two live in the override block
   below; unit-identity colours (gold, the sidebar panel) are the same in
   both and are defined once, here.
   ============================================================ */
:root {
  /* Tells the browser which palette its OWN native widgets should draw in —
     a <select>'s open dropdown list, checkboxes, scrollbars. Without this,
     the browser assumes light and renders that popup with a light native
     background while our CSS still hands it light text meant for a dark
     one: unreadable either way it lands. This one declaration fixes every
     dropdown in the app at once, because every page here loads this file. */
  color-scheme: dark;
  --bg:        #0f1114;
  --surface:   #171a1f;
  --surface-2: #1c2026;
  --surface-3: #22272e;
  --line:      #2a2f37;
  --line-soft: #232830;
  --ink:       #e9ecf0;
  --ink-2:     #a6aebb;
  --ink-3:     #7b8492;
  --muted:     var(--ink-2);

  /* unit identity — same in both themes */
  --panel:     #0b0d09;      /* sidebar / seal black */
  --panel-2:   #14170f;
  --panel-ink: #e7e9e2;
  --panel-ink-2:#9aa08e;
  --gold:      #e8b23e;
  --gold-2:    #fec85c;
  --gold-ink:  #241c05;

  --accent:    #3d8557;      /* field green for primary actions */
  --accent-2:  #4d9c68;
  --accent-ink:#08120c;

  --ok:   #5cc48b;  --ok-bg:   #15301f;
  --warn: #d9a441;  --warn-bg: #332708;
  --err:  #e97b7b;  --err-bg:  #3a1a1a;
  --info: #6fb0e6;  --info-bg: #132738;

  /* officer / enlisted name colour — distinct at a glance, on either theme,
     without drowning enlisted personnel (the majority) in tint */
  --rank-off: var(--gold-2);
  --rank-ep:  var(--ink);

  --radius: 10px;
  --radius-sm: 6px;
  --shadow: 0 1px 2px rgba(0,0,0,.5);
  --shadow-lg: 0 10px 34px rgba(0,0,0,.6);
  --mono: ui-monospace, "Cascadia Mono", "SF Mono", Menlo, Consolas, monospace;
  --sans: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --sidebar-w: 238px;
}

:root[data-theme="light"] {
  color-scheme: light;
  --bg:        #f2f3f5;
  --surface:   #ffffff;
  --surface-2: #f8f9fa;
  --surface-3: #eef0f2;
  --line:      #dfe3e8;
  --line-soft: #eceef1;
  --ink:       #16181d;
  --ink-2:     #565e6b;
  --ink-3:     #868e9b;

  --panel:     #14160f;
  --panel-2:   #1e2118;

  --accent:    #2f6a45;
  --accent-2:  #3d8557;
  --accent-ink:#ffffff;

  --ok:   #1f7a45;  --ok-bg:   #e7f4ec;
  --warn: #8a5d0d;  --warn-bg: #fdf3e0;
  --err:  #a32b2b;  --err-bg:  #fdeceb;
  --info: #1b4f78;  --info-bg: #e9f2fa;

  --rank-off: #a9740f;

  --shadow: 0 1px 2px rgba(16,24,40,.05), 0 1px 3px rgba(16,24,40,.06);
  --shadow-lg: 0 10px 34px rgba(16,24,40,.12);
}

* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
  margin: 0; background: var(--bg); color: var(--ink);
  font: 14px/1.5 var(--sans); -webkit-font-smoothing: antialiased;
}
a { color: var(--accent-2); }
h1,h2,h3 { margin: 0; font-weight: 650; letter-spacing: -.01em; }
h1 { font-size: 21px; } h2 { font-size: 15px; }
img { max-width: 100%; }

/* ---------------------------------------------------------- shell */
.shell { display: grid; grid-template-columns: var(--sidebar-w) 1fr; min-height: 100vh; }
.navtoggle { display: none; }
.topbar { display: none; }

.sidebar {
  background: var(--panel); color: var(--panel-ink);
  display: flex; flex-direction: column;
  position: sticky; top: 0; height: 100vh; z-index: 30;
}
.brand {
  display: flex; gap: 11px; align-items: center; padding: 16px 14px 14px;
  text-decoration: none; color: inherit;
  border-bottom: 1px solid rgba(255,255,255,.07);
}
.brand-logo { display: block; width: 40px; height: auto; flex: none;
  filter: drop-shadow(0 1px 3px rgba(0,0,0,.5)); }
.brand-text { display: flex; flex-direction: column; line-height: 1.25; min-width: 0; }
.brand-text strong { font-size: 15px; letter-spacing: .12em; color: var(--gold-2); }
.brand-text > span { font-size: 10.5px; color: var(--panel-ink-2);
  text-transform: uppercase; letter-spacing: .08em;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.nav { padding: 8px; display: flex; flex-direction: column; gap: 1px;
  overflow-y: auto; flex: 1; }
.nav-item {
  display: flex; align-items: center; gap: 10px; padding: 8px 10px;
  border-radius: var(--radius-sm); color: var(--panel-ink-2);
  text-decoration: none; font-size: 13.5px; font-weight: 500;
}
.nav-item svg { width: 17px; height: 17px; flex: none; opacity: .85; }
.nav-item > span { flex: 1; }
.nav-item:hover { background: rgba(255,255,255,.06); color: var(--panel-ink); }
.nav-item.is-active {
  background: linear-gradient(90deg, rgba(232,178,62,.20), rgba(232,178,62,.06));
  color: var(--gold-2); font-weight: 650;
  box-shadow: inset 2px 0 0 var(--gold);
}
.nav-count {
  background: var(--gold); color: var(--gold-ink); font: 700 10.5px/1 var(--sans);
  padding: 3px 6px; border-radius: 20px; min-width: 18px; text-align: center;
}

.sidebar-foot { padding: 10px; border-top: 1px solid rgba(255,255,255,.07); }
.who { display: flex; gap: 9px; align-items: center; margin-bottom: 8px;
  text-decoration: none; color: inherit; }
.who-avatar {
  width: 30px; height: 30px; border-radius: 50%; flex: none;
  background: rgba(255,255,255,.08); border: 1px solid rgba(255,255,255,.14);
  display: grid; place-items: center; font-size: 11px; font-weight: 700;
  color: var(--gold-2);
}
.who-text { display: flex; flex-direction: column; min-width: 0; line-height: 1.3; }
.who-text strong { font-size: 12.5px; overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap; color: var(--panel-ink); }
.who-text > span { font-size: 10px; color: var(--panel-ink-2); letter-spacing: .06em; }
.foot-row { display: flex; gap: 6px; }
.foot-row .btn { flex: 1; color: var(--panel-ink-2); }
.foot-row .btn:hover { background: rgba(255,255,255,.07); color: var(--panel-ink);
  border-color: transparent; }

.main { padding: 22px 26px 70px; min-width: 0; max-width: 1180px; }
.main.is-wide { max-width: none; }

/* ---------------------------------------------------------- header */
.page-head { display: flex; justify-content: space-between; align-items: flex-end;
  gap: 16px; margin-bottom: 18px; flex-wrap: wrap; }
.page-head p { margin: 4px 0 0; max-width: 70ch; }
.head-actions { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }

/* DDLT: status and navigation above the grouped document actions. */
.ddlt-header { align-items: center; }
.ddlt-title-row { display: flex; align-items: center; flex-wrap: wrap; gap: 12px; }
.ddlt-title-row h1 { margin: 0; }
.ddlt-board-link { font-size: 13px; font-weight: 600; white-space: nowrap; }
.ddlt-actions { width: 100%; padding-top: 14px; border-top: 1px solid var(--line); }
.ddlt-action-group { display: flex; flex-wrap: wrap; gap: 8px; }
.ddlt-actions .btn { min-height: 40px; gap: 16px; }
.ddlt-finalise { margin-left: auto; }
.ddlt-menu { position: relative; }
.ddlt-menu > summary { list-style: none; user-select: none; }
.ddlt-menu > summary::-webkit-details-marker { display: none; }
.ddlt-menu[open] > summary { background: var(--surface-2); border-color: var(--ink-3); }
.ddlt-menu-panel { position: absolute; top: calc(100% + 6px); left: 0; z-index: 20;
  width: 248px; max-width: calc(100vw - 40px); padding: 6px;
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  background: var(--surface); box-shadow: var(--shadow-lg); }
.ddlt-menu-panel a { display: flex; flex-direction: column; gap: 3px;
  padding: 10px 12px; border-radius: 4px; color: var(--ink); text-decoration: none; }
.ddlt-menu-panel a:hover { background: var(--surface-3); }
.ddlt-actions :is(summary, a, button):focus-visible, .ddlt-board-link:focus-visible {
  outline: 2px solid var(--accent-2); outline-offset: 3px; }
@media (max-width: 600px) {
  .ddlt-header { align-items: stretch; }
  .ddlt-actions { gap: 12px; }
  .ddlt-action-group { position: relative; }
  .ddlt-menu { position: static; }
  .ddlt-menu > summary.btn { gap: 8px; padding-inline: 10px; }
  .ddlt-finalise { flex-basis: 100%; margin-left: 0; }
  .ddlt-finalise .btn { width: 100%; }
  .ddlt-menu .ddlt-menu-panel { left: 0; right: auto; }
}

.help-content h2 { display: flex; align-items: center; gap: 9px; font-size: 15px;
  padding-bottom: 8px; border-bottom: 1px solid var(--line-soft); }
.help-content h2::before { content: ''; width: 5px; height: 16px; border-radius: 3px;
  background: linear-gradient(180deg, var(--gold), var(--accent-2)); flex: none; }
.help-content h2:first-child { margin-top: 0; }
.help-content p { margin: 8px 0; line-height: 1.6; max-width: 68ch; }
.help-content ul { margin: 6px 0 14px; padding-left: 20px; line-height: 1.6; }
.help-content li { margin: 3px 0; }
.help-content code { background: var(--surface-2); border: 1px solid var(--line);
  border-radius: 4px; padding: 1px 5px; font-family: var(--mono); font-size: 12px; }

/* ---------------------------------------------------------- help quick-start */
.help-quickstart { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: 10px; margin-bottom: 18px; }
.help-quickstart-card { background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 14px 16px; text-decoration: none; color: inherit;
  display: flex; flex-direction: column; gap: 4px; transition: border-color .15s, transform .15s; }
.help-quickstart-card:hover { border-color: var(--gold); transform: translateY(-1px); }
.help-quickstart-card b { font-size: 13.5px; display: flex; align-items: center; gap: 7px; }
.help-quickstart-card span { font-size: 12px; color: var(--ink-3); line-height: 1.4; }
.help-quickstart-num { display: inline-flex; align-items: center; justify-content: center;
  width: 19px; height: 19px; border-radius: 50%; background: var(--gold); color: #1a1a12;
  font-size: 11px; font-weight: 700; flex: none; }

.page-head-id { display: flex; gap: 14px; align-items: flex-start; }
.soi-photo { width: 72px; height: 90px; object-fit: cover; border-radius: 6px;
  border: 1px solid var(--line); background: var(--surface-2); flex: none; }
.soi-photo-empty { display: grid; place-items: center; font-size: 10px;
  color: var(--ink-3); text-align: center; text-transform: uppercase;
  letter-spacing: .04em; }
.photo-form { display: flex; gap: 6px; align-items: center; margin-top: 8px; }
.photo-form input[type=file] { font-size: 11.5px; max-width: 190px; }

/* ---------------------------------------------------------- stakeholders */
.stakeholder-photo { width: 96px; height: 120px; object-fit: cover; border-radius: 6px;
  border: 1px solid var(--line); background: var(--surface-2); }
.stakeholder-photo-empty { display: grid; place-items: center; font-size: 10px;
  color: var(--ink-3); text-align: center; text-transform: uppercase; letter-spacing: .04em; }
.stakeholder-thumb { width: 32px; height: 40px; object-fit: cover; border-radius: 4px;
  border: 1px solid var(--line); background: var(--surface-2); display: block; }
.stakeholder-thumb-empty {}
.section-title { margin: 26px 0 10px; font-size: 12px; text-transform: uppercase;
  letter-spacing: .08em; color: var(--ink-3); }

/* ---------------------------------------------------------- cards */
.card { background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); box-shadow: var(--shadow);
  overflow: hidden; margin-bottom: 16px; }
.card.pad { padding: 16px; }
.pad-x { padding: 14px 16px 0; }
.card-title { font-size: 11.5px; text-transform: uppercase; letter-spacing: .08em;
  color: var(--ink-3); margin-bottom: 12px; }
/* A <details> wrapping the same .card/.card-title markup every card already
   uses — a long record (Awards, Designations, Change history…) can start
   closed without a different visual language from the cards around it. */
summary.card-title { display: flex; align-items: center; gap: 6px; cursor: pointer;
  list-style: none; user-select: none; }
summary.card-title::-webkit-details-marker { display: none; }
summary.card-title::before { content: '▸'; display: inline-block; font-size: 9px;
  transition: transform .12s ease; flex-shrink: 0; }
details[open] > summary.card-title::before { transform: rotate(90deg); }
summary.card-title .count { font-weight: 600; color: var(--ink-2); text-transform: none;
  letter-spacing: normal; }
details.card:not([open]) > summary.card-title.pad-x { padding-bottom: 14px; margin-bottom: 0; }
.narrow { max-width: 460px; }
.grid-2 { display: grid; grid-template-columns: repeat(auto-fit, minmax(330px, 1fr)); gap: 16px; }

.tile-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(225px, 1fr)); gap: 12px; }
.tile { display: flex; flex-direction: column; gap: 5px; padding: 16px;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); text-decoration: none; color: inherit;
  box-shadow: var(--shadow); }
.tile:hover { border-color: var(--gold); box-shadow: var(--shadow-lg); }
.tile span { font-size: 12.5px; color: var(--ink-2); }

.stat-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(128px, 1fr));
  gap: 10px; margin-bottom: 16px; }
.stat { background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 12px 14px; box-shadow: var(--shadow); }
.stat span { display: block; font-size: 10.5px; text-transform: uppercase;
  letter-spacing: .07em; color: var(--ink-3); }
.stat strong { font-size: 24px; font-weight: 650; letter-spacing: -.02em;
  font-variant-numeric: tabular-nums; }
.stat.is-gold { border-color: var(--gold); }
.stat.is-gold strong { color: var(--gold); }

/* A compact pill switch between two (or a few) views of the same page —
   a sub-tab, not a KPI, so it stays small: a stat tile's own 24px number
   reads like the headline of the page, which this isn't. Still plainly
   "click one of these" (a border, a hover lift), just sized for that. */
.choice-row { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 16px; }
a.choice-pill { display: inline-flex; align-items: center; gap: 7px;
  padding: 7px 14px; border: 1px solid var(--line); border-radius: 999px;
  background: var(--surface); text-decoration: none; color: var(--ink-2);
  font-size: 12px; font-weight: 650; text-transform: uppercase; letter-spacing: .04em;
  transition: transform .1s ease, box-shadow .1s ease; }
a.choice-pill:hover { transform: translateY(-1px); box-shadow: 0 2px 8px rgba(0,0,0,.1); }
a.choice-pill strong { font-size: 14px; font-weight: 700; text-transform: none;
  letter-spacing: 0; color: var(--ink); font-variant-numeric: tabular-nums; }
a.choice-pill.is-active { border-color: var(--gold); color: var(--gold);
  background: rgba(232, 178, 62, .12); }
a.choice-pill.is-active strong { color: var(--gold); }

/* ---------------------------------------------------------- forms */
.field { display: flex; flex-direction: column; gap: 5px; margin-bottom: 12px; }
.field > span { font-size: 11px; font-weight: 600; color: var(--ink-2);
  text-transform: uppercase; letter-spacing: .06em; }
.field-row { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.field-inline { display: flex; align-items: center; gap: 8px; }
.field-inline > span { font-size: 11px; font-weight: 600; color: var(--ink-2);
  text-transform: uppercase; letter-spacing: .06em; white-space: nowrap; }
.field-inline.grow { flex: 1; min-width: 170px; }
/* min-width:0 is the fix: a flex item's default min-width is auto, which
   ignores an explicit width:100% and lets the input overflow past its
   own label instead of shrinking to the space actually available — the
   exact clipping seen on the DDLT toolbar's Find field below ~560px. */
.field-inline.grow input { width: 100%; min-width: 0; }
.form-inline-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(165px, 1fr));
  gap: 12px; align-items: end; }
.form-actions { margin-top: 14px; display: flex; gap: 8px; flex-wrap: wrap; }
/* For a form long enough to scroll (the person-edit page, mainly) — Save
   stays reachable at the bottom of the viewport instead of only turning up
   once you've scrolled past every card, wherever in the form you happen to
   be editing. Not applied to .form-actions generally: a short form (a
   modal, an inline row) has no need of it, and sticking a button inside a
   scrolling container it wasn't designed for can misbehave. */
.form-actions-sticky { position: sticky; bottom: 0; background: var(--bg);
  padding: 12px 0; border-top: 1px solid var(--line); z-index: 5; }
.ro { padding: 7px 0; color: var(--ink-2); border-bottom: 1px dashed var(--line); }

input, select, textarea, button { font: inherit; }
input, select, textarea {
  color: var(--ink); background: var(--surface);
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  padding: 8px 9px; min-width: 0; width: 100%;
}
textarea { resize: vertical; min-height: 90px; }
input:focus, select:focus, textarea:focus {
  outline: none; border-color: var(--accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 18%, transparent);
}
input[type=checkbox], input[type=radio] { width: auto; }
input[type=file] { padding: 6px; background: var(--surface-2); }

.pw-field { position: relative; }
.pw-field input { padding-right: 34px; }
.pw-toggle {
  position: absolute; top: 0; right: 0; bottom: 0; width: 32px;
  display: grid; place-items: center; border: 0; background: transparent;
  color: var(--ink-3); cursor: pointer; border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
}
.pw-toggle:hover { color: var(--ink); background: color-mix(in srgb, var(--ink) 6%, transparent); }
.pw-toggle svg { width: 17px; height: 17px; }
.w-60 { width: 60px; }

.toolbar { display: flex; gap: 14px; align-items: center; flex-wrap: wrap;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 11px 14px; margin-bottom: 14px;
  box-shadow: var(--shadow); }

.btn {
  display: inline-flex; align-items: center; gap: 6px; justify-content: center;
  padding: 8px 13px; border-radius: var(--radius-sm); cursor: pointer;
  border: 1px solid var(--line); background: var(--surface); color: var(--ink);
  font: 600 13px/1.2 var(--sans); text-decoration: none; white-space: nowrap;
}
.btn:hover { background: var(--surface-2); border-color: var(--ink-3); }
.btn-primary { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); }
.btn-primary:hover { background: var(--accent-2); border-color: var(--accent-2); }
.btn-gold { background: var(--gold); border-color: var(--gold); color: var(--gold-ink); }
.btn-gold:hover { background: var(--gold-2); border-color: var(--gold-2); }
.btn-danger { color: var(--err); border-color: color-mix(in srgb, var(--err) 35%, var(--line)); }
.btn-danger:hover { background: var(--err-bg); border-color: var(--err); }
.btn-ghost { background: transparent; border-color: transparent; color: var(--ink-2); }
.btn-ghost:hover { background: var(--surface-2); border-color: var(--line); }
.btn-sm { padding: 4px 9px; font-size: 12px; }
.btn-lg { padding: 12px 20px; font-size: 15px; }
.btn-block { width: 100%; }
.inline { display: inline; }
.summary-btn { cursor: pointer; font-weight: 600; color: var(--accent-2); padding: 2px 0; }
.inline-details { display: inline-block; margin-left: 6px; text-align: left; }
.inline-details summary { display: inline; }

/* ---------------------------------------------------------- tables */
/* overscroll-behavior-x keeps a horizontal drag inside the table from
   bleeding into the page's own back/forward swipe gesture on a phone —
   -webkit-overflow-scrolling is dead weight on current iOS but costs
   nothing and keeps momentum scrolling on anything older still in the
   field. Every genuinely wide table in this app must sit inside one of
   these; a naked <table> in a .card gets silently clipped by the card's
   own overflow:hidden instead of becoming reachable by scrolling. */
.table-wrap { overflow-x: auto; overscroll-behavior-x: contain;
  -webkit-overflow-scrolling: touch; }
.table { width: 100%; border-collapse: collapse; font-size: 13px; }
.table th { text-align: left; font-size: 10.5px; text-transform: uppercase;
  letter-spacing: .07em; color: var(--ink-3); font-weight: 700; padding: 9px 12px;
  border-bottom: 1px solid var(--line); background: var(--surface-2);
  position: sticky; top: 0; z-index: 2; white-space: nowrap; }
.table th a { color: inherit; text-decoration: none; display: inline-flex;
  align-items: center; gap: 4px; }
.table th a:hover { color: var(--ink); }
.table th .sortmark { color: var(--gold); font-size: 9px; }
.table td { padding: 7px 12px; border-bottom: 1px solid var(--line-soft);
  vertical-align: middle; }
.table tbody tr:hover td { background: var(--surface-2); }
.table .num { text-align: right; font-variant-numeric: tabular-nums; }
.table .center { text-align: center; }
.table .zero { color: var(--ink-3); }
.strong { font-weight: 700; }
.mono { font-family: var(--mono); font-size: 12px; }
.small { font-size: 11.5px; }
.muted { color: var(--ink-2); }
/* A secondary annotation line under some primary text — "voided: <reason>",
   "from the SOI — no document on file", a note beside a value. Used across
   a dozen pages (dpsr.mjs, modules.mjs, px.mjs, rolesadmin.mjs, roster.mjs,
   selfupdate.mjs, ...) on the assumption a rule for it already existed;
   the only one that did was scoped to one specific DDLT table cell, so
   everywhere else it rendered as plain full-contrast text running straight
   into whatever came before it with no line break — hard to read, and
   easy to mistake for part of the primary text rather than a note about
   it. This is the rule that was actually meant to apply everywhere. Scoped
   to span/div/p rather than a bare .sub: one board table (dpsr.mjs) reuses
   the same class name on a <tr> for its own, unrelated totals-row styling,
   which display:block would have quietly broken. .table-grid .cell-name
   .sub below still wins in that one dense DDLT table, deliberately
   subtler there. */
span.sub, div.sub, p.sub { display: block; font-size: 11px; color: var(--ink-2); }
.truncate { max-width: 240px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.link { color: var(--accent-2); font-weight: 600; text-decoration: none; }
.link:hover { text-decoration: underline; }

/* officer / enlisted distinction, wherever a name is shown — rankClass()
   applies these on every roster row, board name and DDLT entry. */
.r-off { color: var(--rank-off); }
.r-ep  { color: var(--rank-ep); }
.empty { padding: 34px; text-align: center; color: var(--ink-3); }
/* align-items defaults to stretch — with no explicit cross-axis alignment,
   the button (inside a blockified <form class="inline">) stretched to the
   full height of a row tallened by a wrapping name in another column, and
   rendered pinned to the top of that stretch instead of level with
   everything else in the row. */
.row-actions { display: flex; gap: 4px; justify-content: flex-end; align-items: center; }
.is-cancelled td { opacity: .5; }
.is-cancelled td:first-child { text-decoration: line-through; }

.row-section td { background: var(--surface-3); font-weight: 700; font-size: 10.5px;
  text-transform: uppercase; letter-spacing: .07em; color: var(--ink-2);
  padding: 7px 12px; border-top: 1px solid var(--line); }
.row-section .sec-kind { color: var(--ink-3); font-weight: 600; letter-spacing: .04em; }

/* editable grid */
.table-grid td { padding: 3px 6px; }
.table-grid .cell-name { padding: 5px 12px; }
.table-grid .cell-name strong { font-weight: 600; }
.table-grid .cell-name .sub { display: block; font-size: 11px; color: var(--ink-3); }
.table-grid select, .table-grid input, .table-grid textarea { padding: 5px 7px; font-size: 12.5px;
  border-color: transparent; background: transparent; width: 100%; box-sizing: border-box; }
.table-grid select:hover, .table-grid input:hover, .table-grid textarea:hover { border-color: var(--line);
  background: var(--surface); }
.table-grid select:focus, .table-grid input:focus, .table-grid textarea:focus { background: var(--surface); }
/* Remarks used to be a single-line input, so anything past what the
   column's width could show was simply gone until you clicked in and
   scrolled the text sideways inside it — "too small to read" was the
   input doing exactly what a single-line input does. A textarea that
   grows with its own content (see app.js) fixes that without needing a
   wider column: short remarks stay one line, a longer one just becomes a
   taller row instead of a truncated one. */
.f-textarea { min-height: 30px; max-height: 200px; line-height: 1.4; resize: none; }
.table-grid .col-nr { width: 40px; text-align: right; }
/* Personnel, Position, Disposition and Location were all free to grow as
   wide as their longest value, which is exactly what starved Remarks down
   to a sliver too narrow to hold even a short line without wrapping every
   word onto its own — capping the first four gives Remarks (below) the
   room a "how long can a remark run" column is actually meant to have. */
.table-grid .col-name { max-width: 190px; }
.table-grid .col-dispo { width: 150px; }
.table-grid .col-role { width: 130px; }
.table-grid .col-loc { width: 130px; }
.table-grid .col-eff { width: 130px; }
.table-grid .col-status { width: 88px; text-align: center; }
.table-grid .col-remarks { min-width: 240px; }
/* This many columns means scrolling right on a narrow screen to reach
   Disposition, Location or Remarks is unavoidable — losing sight of
   *whose* row that is while you do it is not. Pin the leading
   identity columns (select checkbox when present, row number, name) so
   they stay put through the scroll; the sticky offsets below assume the
   fixed col-sel/col-nr widths set elsewhere in this file. */
#dpsr-table .col-nr, #dpsr-table .cell-name, #dpsr-table .col-name {
  position: sticky; z-index: 2; background: var(--surface);
}
#dpsr-table .col-nr { left: 0; }
#dpsr-table .cell-name, #dpsr-table .col-name { left: 40px; }
#dpsr-table.has-sel .col-sel { position: sticky; left: 0; z-index: 2; background: var(--surface); }
#dpsr-table.has-sel .col-nr { left: 34px; }
#dpsr-table.has-sel .cell-name, #dpsr-table.has-sel .col-name { left: 74px; }
/* Row-level state colors (hover, section header, saving/saved) must still
   show through the sticky cells, not the flat background above — same
   specificity as those existing rules (one class + a descendant), so the
   later one in source order wins; kept immediately after them for that
   reason rather than relying on !important. */
#dpsr-table tbody tr:hover .col-nr, #dpsr-table tbody tr:hover .cell-name,
#dpsr-table tbody tr:hover .col-name { background: var(--surface-2); }
#dpsr-table .row-section .col-nr, #dpsr-table .row-section .cell-name,
#dpsr-table .row-section .col-name { background: var(--surface-3); }
#dpsr-table .row-line.saving .col-nr, #dpsr-table .row-line.saving .cell-name,
#dpsr-table .row-line.saving .col-name {
  background: color-mix(in srgb, var(--info) 8%, var(--surface));
}
#dpsr-table .row-line.saved .col-nr, #dpsr-table .row-line.saved .cell-name,
#dpsr-table .row-line.saved .col-name {
  background: color-mix(in srgb, var(--ok) 13%, var(--surface));
}
.cell-status { text-align: center; }
/* display:flex directly on a <td> defeats the table's own column-width
   algorithm in some engines — the flex box stops behaving as a table-cell
   for sizing purposes, and the column collapses onto its neighbor (this is
   exactly what was happening: Effective From/To rendered on top of
   Location/Detail). The flex context now lives one level down, on a plain
   <div> inside the cell, so the <td> itself stays a normal table-cell. */
/* Was a row (From, To side by side) inside a fixed 220px column — the two
   date pickers plus their labels never actually fit that width, which is
   the other half of "we can't see the end": the To field was the part
   getting squeezed off. Stacking them costs height, which this table has
   room for, instead of width, which it does not. */
.cell-eff-flex { display: flex; flex-direction: column; gap: 4px; }
.cell-eff input { font-size: 11.5px; }
.cell-loc-flex { display: flex; flex-direction: column; gap: 4px; }
.f-date-lab { display: flex; flex-direction: column; gap: 2px; font-size: 10.5px;
  font-weight: 600; color: var(--ink-2); text-transform: uppercase; letter-spacing: .04em; }
.row-line.is-ineff .cell-name strong { color: var(--err); }
.row-line.saving td { background: color-mix(in srgb, var(--info) 8%, transparent); }
.row-line.saved td { background: color-mix(in srgb, var(--ok) 13%, transparent);
  transition: background .8s; }
.save-hint { margin-top: 10px; }

.c-pick { font-size: 12.5px; }
.c-pick.is-editable { cursor: pointer; border-radius: var(--radius-sm); }
.c-pick.is-editable:hover { background: var(--surface); box-shadow: inset 0 0 0 1px var(--line); }
.c-pick.is-open { padding: 3px 4px; }
.kind-picker { width: 100%; font-size: 12.5px; padding: 4px 6px; }
kbd { font: 600 11px var(--mono); background: var(--surface-2); border: 1px solid var(--line);
  border-bottom-width: 2px; border-radius: 4px; padding: 1px 5px; }

/* summary board */
.table-summary .col-lab { width: 250px; }
.table-summary td { padding: 5px 12px; }
.row-band td { background: var(--panel); color: var(--gold-2); font-weight: 700;
  font-size: 10.5px; letter-spacing: .12em; padding: 6px 12px; }
.row-band.band-ineff td { background: var(--ink-2); color: #fff; }
.row-total td { border-top: 1px solid var(--line); font-weight: 700; background: var(--surface-2); }
.row-grand td { background: var(--surface-3); font-weight: 800; font-size: 14px;
  border-top: 2px solid var(--gold); }
.table-audit td { font-size: 12px; }
.overview-actions { margin-bottom: 16px; }
.overview-actions .todo form { margin-top: 8px; }
.overview-awol { display: block; border: 2px solid var(--line); background: var(--surface-2);
  color: var(--ink); text-decoration: none; }
.overview-awol.has-awol { border-color: var(--err); background: var(--err-bg); }
.overview-awol.has-awol .card-title, .overview-awol.has-awol .overview-awol-count { color: var(--err); }
.overview-awol-count { display: block; font-size: clamp(26px, 3vw, 44px); line-height: 1.2; }
.table-audit { width: 100%; min-width: 0; table-layout: fixed; }
.table-audit th, .table-audit td { padding: 9px 8px; white-space: normal;
  overflow-wrap: anywhere; vertical-align: top; }
.table-audit th:nth-child(1) { width: 10%; }
.table-audit th:nth-child(2) { width: 10%; }
.table-audit th:nth-child(3) { width: 7%; }
.table-audit th:nth-child(4) { width: 14%; }
.table-audit th:nth-child(5) { width: 9%; }
.table-audit th:nth-child(6), .table-audit th:nth-child(7) { width: 12%; }
.table-audit th:nth-child(8) { width: 10%; }
.table-audit th:nth-child(9) { width: 16%; }
.table-audit td.truncate { max-width: none; white-space: normal; overflow: visible; }
.table-audit td.row-actions { display: table-cell; text-align: left; }
.table-audit .chip { max-width: 100%; white-space: normal; overflow-wrap: anywhere; }
.full-text-bubble { position: fixed; z-index: 10000; max-width: min(440px, calc(100vw - 24px));
  max-height: 45vh; overflow: auto; padding: 10px 12px; border: 1px solid var(--line);
  border-radius: 8px; background: var(--surface-2); color: var(--ink);
  box-shadow: 0 6px 24px rgba(0,0,0,.35); font: 13px/1.5 var(--sans);
  white-space: pre-wrap; overflow-wrap: anywhere; }
@media (max-width: 800px) {
  .table-audit { min-width: 850px; }
}

/* sex quick-pick */
.sexpick { display: inline-flex; border: 1px solid var(--line);
  border-radius: var(--radius-sm); overflow: hidden; }
.sx { border: 0; background: transparent; padding: 3px 9px; cursor: pointer;
  font: 600 11px var(--sans); color: var(--ink-3); }
.sx:hover { background: var(--surface-2); color: var(--ink); }
.sx.on { background: var(--accent); color: #fff; }
.col-sex { width: 74px; }

/* ---------------------------------------------------------- chips, flash */
.chip { display: inline-block; padding: 2px 8px; border-radius: 20px; font-size: 10.5px;
  font-weight: 700; letter-spacing: .04em; background: var(--surface-2);
  border: 1px solid var(--line); color: var(--ink-2); white-space: nowrap; }
.chip-ok, .chip-signed, .chip-approved, .chip-confirmed, .chip-create, .chip-login
  { background: var(--ok-bg); color: var(--ok); border-color: transparent; }
.chip-warn, .chip-draft, .chip-pending, .chip-proposed
  { background: var(--warn-bg); color: var(--warn); border-color: transparent; }
.chip-final, .chip-update, .chip-in_route
  { background: var(--info-bg); color: var(--info); border-color: transparent; }
.chip-delete, .chip-login_fail, .chip-returned, .chip-cancelled, .chip-rejected, .chip-err
  { background: var(--err-bg); color: var(--err); border-color: transparent; }
.chip-sm { padding: 1px 6px; font-size: 9.5px; }
/* A skill, club, or status word is always short — nowrap keeps those tidy.
   A course or school name copied straight from an SOI is not; forcing it
   onto one line just pushes it past the card's edge instead, clipped by
   the card's own overflow:hidden. This modifier lets that one kind of chip
   wrap onto a second line and stay inside the card instead. */
.chip-wrap { white-space: normal; max-width: 100%; line-height: 1.35; }
.chip-gold { background: color-mix(in srgb, var(--gold) 22%, transparent);
  color: var(--warn); border-color: transparent; }
.chip-row { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.chip-x { border: none; background: none; color: inherit; cursor: pointer;
  font-size: 10px; padding: 0 0 0 5px; opacity: .65; }
.chip-x:hover { opacity: 1; color: var(--err); }

.flash { padding: 11px 14px; border-radius: var(--radius); margin-bottom: 14px;
  font-size: 13px; border: 1px solid transparent; }
.flash-ok    { background: var(--ok-bg);   color: var(--ok);   border-color: color-mix(in srgb, var(--ok) 25%, transparent); }
.flash-warn  { background: var(--warn-bg); color: var(--warn); border-color: color-mix(in srgb, var(--warn) 25%, transparent); }
.flash-error { background: var(--err-bg);  color: var(--err);  border-color: color-mix(in srgb, var(--err) 25%, transparent); }
.flash-info  { background: var(--info-bg); color: var(--info); border-color: color-mix(in srgb, var(--info) 25%, transparent); }
.flash a { color: inherit; font-weight: 700; }
.code-pill { font-family: var(--mono); background: var(--surface); padding: 3px 8px;
  border-radius: 4px; border: 1px solid var(--line); font-size: 13px;
  user-select: all; display: inline-block; }

.tabs { display: flex; gap: 3px; flex-wrap: wrap; margin-bottom: 14px;
  border-bottom: 1px solid var(--line); overflow-x: auto; }
.tab { padding: 8px 13px; text-decoration: none; color: var(--ink-2); font-size: 13px;
  font-weight: 600; border-bottom: 2px solid transparent; margin-bottom: -1px;
  white-space: nowrap; }
.tab:hover { color: var(--ink); }
.tab.is-active { color: var(--ink); border-bottom-color: var(--gold); }

.pager { display: flex; gap: 12px; align-items: center; justify-content: center;
  padding: 6px 0 24px; }
.kv { display: grid; grid-template-columns: max-content 1fr; gap: 6px 20px; margin: 0 0 16px; }
.kv dt { font-size: 11px; text-transform: uppercase; letter-spacing: .06em; color: var(--ink-3); }
.kv dd { margin: 0; }
.role-help { display: grid; grid-template-columns: max-content 1fr; gap: 4px 16px;
  margin: 14px 0 0; font-size: 12.5px; padding-top: 12px; border-top: 1px solid var(--line-soft); }
.role-help dt { font-weight: 700; color: var(--ink-2); }
.role-help dd { margin: 0; color: var(--ink-2); }

/* ---------------------------------------------------------- signing */
.route { list-style: none; margin: 0; padding: 0; }
.route li { display: flex; gap: 12px; padding: 10px 0; border-bottom: 1px solid var(--line-soft); }
.route li:last-child { border-bottom: 0; }
.route-dot { width: 26px; height: 26px; border-radius: 50%; flex: none;
  display: grid; place-items: center; font: 700 11px var(--sans);
  background: var(--surface-2); border: 1px solid var(--line); color: var(--ink-3); }
.route li.is-signed .route-dot { background: var(--ok-bg); color: var(--ok); border-color: transparent; }
.route li.is-pending .route-dot { background: var(--gold); color: var(--gold-ink); border-color: transparent; }
.route li.is-returned .route-dot { background: var(--err-bg); color: var(--err); border-color: transparent; }
.route-body { flex: 1; min-width: 0; }
.route-body strong { display: block; font-size: 13.5px; }
.route-body .meta { font-size: 11.5px; color: var(--ink-3); }
.route-body .comment { margin-top: 4px; font-size: 12.5px; padding: 6px 9px;
  background: var(--surface-2); border-radius: var(--radius-sm); }

.sign-actions { display: flex; gap: 10px; flex-wrap: wrap; margin-top: 8px; }
.doc-frame { width: 100%; height: 68vh; min-height: 380px; border: 1px solid var(--line);
  border-radius: var(--radius); background: var(--surface-2); }

/* ---------------------------------------------------------- auth */
.auth-body { display: grid; place-items: center; min-height: 100vh; padding: 20px;
  background: radial-gradient(120% 90% at 50% 0%, #1e2118 0%, #0b0d09 70%); }
.auth-card { width: 100%; max-width: 372px; background: var(--surface);
  border-radius: 14px; padding: 28px; box-shadow: 0 24px 70px rgba(0,0,0,.5); }
.auth-logo { display: block; width: 74px; height: auto; margin: 0 auto 14px; }
.auth-card h1 { font-size: 17px; text-align: center; margin-bottom: 3px; }
.auth-sub { margin: 0 0 20px; font-size: 12.5px; color: var(--ink-3); text-align: center; }
.auth-note { margin: 16px 0 0; font-size: 11.5px; color: var(--ink-3); text-align: center; }

/* ---------------------------------------------------------- public */
.public-body { background: var(--bg); }
.public-wrap { max-width: 1000px; margin: 0 auto; padding: 26px 18px 60px; }
.public-head { display: flex; gap: 14px; align-items: center; margin-bottom: 20px; }
.public-head img { width: 46px; height: auto; }
.public-head h1 { font-size: 19px; }
.public-head p { margin: 3px 0 0; font-size: 12.5px; }
.public-foot { text-align: center; color: var(--ink-3); font-size: 12px; }

/* ---------------------------------------------------------- mobile */
@media (max-width: 900px) {
  .shell { grid-template-columns: 1fr; }
  .topbar {
    display: flex; align-items: center; gap: 10px; padding: 10px 14px;
    background: var(--panel); color: var(--panel-ink);
    position: sticky; top: 0; z-index: 40;
    padding-top: max(10px, env(safe-area-inset-top));
  }
  .topbar-logo { width: 26px; height: auto; }
  .topbar-title { font-weight: 650; font-size: 15px; overflow: hidden;
    text-overflow: ellipsis; white-space: nowrap; }
  .burger { display: grid; place-items: center; width: 34px; height: 34px;
    border-radius: var(--radius-sm); cursor: pointer; color: var(--panel-ink); }
  .burger svg { width: 21px; height: 21px; }
  .burger:hover { background: rgba(255,255,255,.08); }

  .sidebar {
    position: fixed; inset: 0 auto 0 0; width: 268px; height: 100dvh;
    transform: translateX(-100%); transition: transform .2s ease;
    box-shadow: 0 0 40px rgba(0,0,0,.5);
    /* The drawer starts at the very top of the screen, same as the sticky
       topbar sitting above it (z-index 40 vs 30) — without this, whatever
       renders in that overlap is simply painted over and invisible, which
       used to be "Overview" specifically: hiding .brand below closed the
       gap that logo left, but shifted the very next item, the first real
       nav link, straight into the same covered strip. */
    padding-top: calc(54px + env(safe-area-inset-top, 0px));
  }
  .navtoggle:checked ~ .sidebar { transform: none; }
  /* The topbar carries its own logo on mobile and sits above the sidebar
     (z-index 40 vs 30), so the sidebar's own .brand block is both redundant
     and half-hidden under the topbar when the drawer opens — a logo that
     reads as cut off is worse than no logo at all. */
  .brand { display: none; }
  /* Dims the page and closes the menu on tap. This has to be a real element
     stacked between .main and .sidebar, not a pseudo-element hung off
     .topbar — .topbar has its own z-index (40, above the sidebar's 30), so
     anything attached to it stacks there too, no matter what z-index the
     pseudo-element itself asks for. That mistake put the "dimmer" on top of
     the open sidebar, silently swallowing every tap meant for a nav link. */
  .scrim { display: none; }
  .navtoggle:checked ~ .scrim {
    display: block; position: fixed; inset: 0; z-index: 25; background: rgba(0,0,0,.5);
  }
  .main { padding: 16px 14px 60px; grid-column: 1; }
  .field-row { grid-template-columns: 1fr; }
  .page-head { align-items: flex-start; flex-direction: column; gap: 10px; }
  .table th, .table td { padding: 8px 10px; }
  .truncate { max-width: 150px; }
  .btn { padding: 9px 14px; }          /* comfortable touch targets */
  .doc-frame { height: 52vh; }
}

@media (max-width: 560px) {
  .stat-row { grid-template-columns: repeat(2, 1fr); }
  .toolbar { gap: 10px; }
  .field-inline { width: 100%; }
  .field-inline.grow { min-width: 0; }
}

@media print {
  .sidebar, .topbar, .toolbar, .head-actions, .save-hint { display: none !important; }
  .shell { grid-template-columns: 1fr; }
  .main { padding: 0; max-width: none; }
  .card { border: 0; box-shadow: none; }
}

/* ---------------------------------------------------------- top bar + clock */
.topbar {
  display: flex; align-items: center; gap: 12px; padding: 8px 18px;
  background: var(--panel); color: var(--panel-ink);
  position: sticky; top: 0; z-index: 40; grid-column: 2;
  border-bottom: 1px solid rgba(255,255,255,.08);
}
.shell { grid-template-rows: auto 1fr; }
.sidebar { grid-row: 1 / span 2; }
.topbar-logo { display: none; }
.topbar-title { font-weight: 650; font-size: 14px; letter-spacing: .01em;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.burger { display: none; }
/* The scrim is the mobile nav's dimmer, and it only ever appears there — but
   it still has to be switched off explicitly here. `.shell` is a two-column
   grid, and a label with no display rule is a block that claims a cell: it
   took the one meant for `.main`, which then auto-placed into the sidebar's
   column on the row below — a 238px-wide main pushed a screenful down the
   page, i.e. an apparently empty Overview. The mobile rules override this by
   specificity (`.navtoggle:checked ~ .scrim`), so the menu still dims. */
.scrim { display: none; }

.zulu {
  margin-left: auto; display: flex; align-items: baseline; gap: 10px;
  font-family: var(--mono); font-variant-numeric: tabular-nums; white-space: nowrap;
}
.zulu-date { font-size: 12px; color: var(--panel-ink-2); letter-spacing: .06em; }
.zulu-time { font-size: 17px; font-weight: 700; color: var(--gold-2); letter-spacing: .04em; }
.zulu-z    { font-size: 11px; color: var(--panel-ink-2); letter-spacing: .05em; }

@media (max-width: 900px) {
  .topbar { grid-column: 1; padding-top: max(8px, env(safe-area-inset-top)); }
  .topbar-logo { display: block; }
  .burger { display: grid; }
  .sidebar { grid-row: auto; }
  .zulu { gap: 7px; }
  .zulu-date, .zulu-z { display: none; }
  .zulu-time { font-size: 15px; }
}
@media (max-width: 420px) { .topbar-title { display: none; } }
@media print { .zulu { display: none; } }

/* ---------------------------------------------------------- batch select */
.col-sel { width: 34px; text-align: center; }
.col-sel input { width: auto; }
tr.is-picked td { background: color-mix(in srgb, var(--gold) 13%, transparent) !important; }

/* Floats on desktop, where there's room for it to follow the scroll
   without ever covering the grid it's editing — the mobile breakpoint
   below overrides back to a plain in-flow section, which is the one
   that actually caused the covering-the-screen problem in the first
   place on a phone's much shorter viewport. */
.batchbar {
  display: flex; gap: 14px; align-items: center; flex-wrap: wrap;
  background: var(--panel); color: var(--panel-ink);
  border-radius: var(--radius); padding: 12px 16px;
  box-shadow: 0 6px 20px rgba(0,0,0,.22); margin-top: 12px;
  position: sticky; bottom: 12px; z-index: 20;
}
.batchbar .field-inline > span { color: var(--panel-ink-2); }
.batchbar select, .batchbar input {
  background: rgba(255,255,255,.08); border-color: rgba(255,255,255,.18);
  color: var(--panel-ink);
}
.batchbar select option { color: var(--ink); background: var(--surface); }
.batchbar-count { font-weight: 700; color: var(--gold-2); white-space: nowrap; }
.batchbar-count b { font-size: 18px; }
/* A phone's viewport is short enough that a sticky bar pinned to the
   bottom eats a real fraction of the visible screen — this is what was
   actually covering the grid it edited before; a desktop's much taller
   viewport never has that problem. Placed after the rule above so it
   wins the cascade at this width regardless of source order elsewhere. */
@media (max-width: 900px) {
  .batchbar { position: static; bottom: auto; }
}

/* ---------------------------------------------------------- person picker */
.pick-wrap { position: relative; }
.pick-list {
  position: absolute; top: calc(100% + 4px); left: 0; right: 0; z-index: 60;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); box-shadow: var(--shadow-lg);
  max-height: 280px; overflow: auto;
}
.pick-item {
  display: flex; flex-direction: column; gap: 1px; width: 100%; text-align: left;
  padding: 8px 12px; border: 0; background: transparent; cursor: pointer;
  border-bottom: 1px solid var(--line-soft);
}
.pick-item:last-child { border-bottom: 0; }
.pick-item:hover, .pick-item.is-active { background: var(--surface-2); }
.pick-item strong { font-size: 13px; color: var(--ink); }
.pick-item span { font-size: 11.5px; color: var(--ink-3); }
.pick-empty { padding: 14px; font-size: 12.5px; color: var(--ink-3); }

/* ---------------------------------------------------------- searchable selects */
/* Every plain <select> is progressively enhanced (select-search.js) into a
   type-to-filter combobox. The original <select> stays in the DOM — visually
   hidden but still focusable and still what the form submits, so `required`
   and everything server-side keeps working — while this input is what is
   actually seen and typed into. */
.ssearch-wrap { position: relative; width: 100%; }
.ssearch-native {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0;
}
.ssearch-input { cursor: text; }
.ssearch-list {
  position: fixed; z-index: 300;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); box-shadow: var(--shadow-lg); overflow: auto;
}
.ssearch-list .pick-item { font-size: 13px; padding: 7px 12px; }
input.is-valid { border-color: var(--ok);
  background: color-mix(in srgb, var(--ok) 6%, var(--surface)); }

/* ---------------------------------------------------------- offline copy */
.offline-bar {
  background: repeating-linear-gradient(45deg, var(--warn-bg), var(--warn-bg) 12px,
              color-mix(in srgb, var(--warn) 12%, var(--warn-bg)) 12px,
              color-mix(in srgb, var(--warn) 12%, var(--warn-bg)) 24px);
  color: var(--warn); border: 1px solid color-mix(in srgb, var(--warn) 35%, transparent);
  border-radius: var(--radius); padding: 10px 14px; margin-bottom: 16px; font-size: 13px;
}

/* ---------------------------------------------------------- dashboard */
.callout {
  display: flex; flex-direction: column; gap: 3px; padding: 14px 18px;
  border-radius: var(--radius); text-decoration: none; margin-bottom: 16px;
  border: 1px solid var(--line); background: var(--surface); box-shadow: var(--shadow);
}
.callout strong { color: var(--ink); font-size: 15px; }
.callout span { color: var(--ink-2); font-size: 12.5px; }
.callout-gold { border-color: var(--gold);
  background: linear-gradient(90deg, color-mix(in srgb, var(--gold) 14%, var(--surface)),
              var(--surface)); }
.callout-gold:hover { box-shadow: var(--shadow-lg); }

.bar-row { display: flex; align-items: center; gap: 12px; margin-bottom: 9px; }
.bar-label { width: 92px; font-size: 12.5px; font-weight: 650; flex: none; }
.bar-label em { display: block; font-style: normal; font-size: 10.5px;
  color: var(--ink-3); font-weight: 500; }
.bar { flex: 1; height: 10px; border-radius: 6px; background: var(--surface-3);
  overflow: hidden; box-shadow: inset 0 1px 3px rgba(0,0,0,.25); }
.bar-fill { display: block; height: 100%; border-radius: 6px;
  background: linear-gradient(90deg, var(--accent), var(--gold));
  box-shadow: 0 0 8px color-mix(in srgb, var(--gold) 45%, transparent);
  transition: width .5s cubic-bezier(.2,.7,.3,1); }
.bar-num { width: 66px; text-align: right; font-family: var(--mono); font-size: 12.5px;
  font-variant-numeric: tabular-nums; flex: none; }
.bar-num em { font-style: normal; color: var(--ink-3); }

/* A secondary/supporting chart reads quieter than a hero one — same colours,
   thinner bars, tighter rows, no glow. */
.chart-card.is-compact .bar-row { margin-bottom: 7px; }
.chart-card.is-compact .bar { height: 7px; }
.chart-card.is-compact .bar-fill { box-shadow: none; }
.chart-card.is-compact .bar-label,
.chart-card.is-compact .bar-num { font-size: 11.5px; }

.todo { list-style: none; margin: 0; padding: 0; }
.todo li { border-bottom: 1px solid var(--line-soft); }
.todo li:last-child { border-bottom: 0; }
.todo a { display: block; padding: 10px 0; text-decoration: none; color: var(--ink-2);
  font-size: 13px; }
.todo a:hover { color: var(--ink); }
.todo strong { color: var(--ink); }

.quick { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 8px; }
.quick-item { display: flex; flex-direction: column; gap: 2px; padding: 11px 13px;
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  text-decoration: none; background: var(--surface-2); }
.quick-item:hover { border-color: var(--gold); background: var(--surface); }
.quick-item b { font-size: 13px; color: var(--ink); }
.quick-item i { font-style: normal; font-size: 11px; color: var(--ink-3); }

/* ---------------------------------------------------------------- build mark
   Version and provenance, kept quiet: a line in the sidebar foot that names
   the build and, on hover, who made it. */
.buildmark {
  display: flex;
  align-items: center;
  gap: 7px;
  margin-top: 10px;
  padding: 6px 4px 2px;
  font-size: 11px;
  letter-spacing: .04em;
  color: var(--muted);
  text-decoration: none;
  opacity: .72;
  transition: opacity .15s ease;
}
.buildmark:hover { opacity: 1; }
.buildmark img { flex: none; border-radius: 50%; }

/* the same badge and credit, shown properly under Admin → About */
.about-head {
  display: flex;
  align-items: center;
  gap: 18px;
  flex-wrap: wrap;
}
.about-head img { flex: none; }
.about-ver {
  font-family: var(--mono, ui-monospace, monospace);
  font-size: 22px;
  letter-spacing: .06em;
}
.about-by { margin: 4px 0 0; font-size: 14px; }
.auth-build {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  margin: 16px 0 0;
  font-size: 11px;
  letter-spacing: .04em;
  color: var(--muted);
  text-align: center;
}
.auth-build img { border-radius: 50%; }

/* ------------------------------------------------ reporting state, overview
   A company that has not reported is the single most important thing on this
   page, so it gets the loudest treatment on it. */
.callout-alert {
  display: block;
  border: 1px solid color-mix(in srgb, var(--err) 45%, transparent);
  background: color-mix(in srgb, var(--err) 12%, transparent);
  border-radius: 10px;
  padding: 14px 16px;
  margin-bottom: 14px;
}
.callout-alert strong { display: block; color: var(--err); font-size: 15px; }
.callout-alert span { display: block; margin-top: 3px; font-size: 13px; color: var(--muted); }
.callout-act { margin-top: 10px; }

/* A short, checkbox-per-row list inside a callout — the roster-gap and
   reactivation-candidate lists a DDLT import surfaces, and anywhere else
   that needs "review each one, opt in individually" rather than a table. */
.check-list { margin-top: 10px; display: flex; flex-direction: column; gap: 2px;
  max-height: 260px; overflow-y: auto; }
.check-row { display: flex; align-items: center; gap: 8px; padding: 5px 6px;
  border-radius: 6px; font-size: 13px; color: var(--ink); }
.check-row:hover { background: color-mix(in srgb, var(--err) 8%, transparent); }
.check-row input { flex-shrink: 0; }

.callout-ok {
  display: block;
  border: 1px solid color-mix(in srgb, var(--ok) 35%, transparent);
  background: color-mix(in srgb, var(--ok) 10%, transparent);
  border-radius: 10px;
  padding: 12px 16px;
  margin-bottom: 14px;
}
.callout-ok strong { display: block; color: var(--ok); font-size: 14px; }
.callout-ok span { display: block; margin-top: 2px; font-size: 12.5px; color: var(--muted); }

/* the tick or bang at the end of each company bar */
.bar-flag {
  flex: none;
  width: 20px;
  text-align: center;
  font-weight: 700;
  font-size: 13px;
  cursor: default;
}
.bar-flag.is-in  { color: var(--ok); }
.bar-flag.is-out { color: var(--err); }

/* effective strength read at a glance rather than measured */
.bar-fill.is-good { background: var(--ok);
  box-shadow: 0 0 8px color-mix(in srgb, var(--ok) 50%, transparent); }
.bar-fill.is-fair { background: var(--warn);
  box-shadow: 0 0 8px color-mix(in srgb, var(--warn) 50%, transparent); }
.bar-fill.is-poor { background: var(--err);
  box-shadow: 0 0 8px color-mix(in srgb, var(--err) 50%, transparent); }
/* the plain, single-colour bar every other chart.mjs barList() uses when
   there is no good/fair/poor read to make — a rank pyramid or an awards
   count has no "bad" value the way an effective rate does. */
.bar-fill.is-accent { background: var(--gold); }

/* ---------------------------------------------------------- charts */
.chart-empty { display: flex; align-items: center; justify-content: center;
  color: var(--muted); font-size: 12.5px; border: 1px dashed var(--line);
  border-radius: var(--radius-sm); }
.sparkline { display: block; }
.chart-card .card-title { margin-bottom: 2px; }
.chart-foot { display: flex; justify-content: space-between; color: var(--muted);
  font-size: 11px; margin-top: 4px; }

.stat.is-caa strong { color: var(--accent-2); }

/* a break-glass session is never allowed to look like an ordinary one */
.master-bar {
  background: var(--err-bg);
  color: var(--err);
  border-bottom: 2px solid var(--err);
  padding: 10px 18px;
  font-size: 13px;
  line-height: 1.5;
}
.master-bar strong { display: inline-block; margin-right: 6px; letter-spacing: .02em; }

/* ---------------------------------------------------------------- AWOL banner
   This is the loudest thing on the board, deliberately. A soldier missing
   from today's disposition is not a warning — it is a hole in the record. */
.awol-banner {
  display: flex;
  gap: 14px;
  align-items: flex-start;
  border: 2px solid var(--err);
  background: var(--err-bg);
  border-radius: 10px;
  padding: 16px 18px;
  margin-bottom: 16px;
}
.awol-mark {
  flex: none;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  background: var(--err);
  color: #fff;
  font: 800 20px/34px system-ui, sans-serif;
  text-align: center;
}
.awol-body strong { display: block; color: var(--err); font-size: 16px; }
.awol-body > span { display: block; margin-top: 4px; color: var(--muted); font-size: 13px; }
.awol-list {
  list-style: none;
  margin: 10px 0 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 6px 14px;
}
.awol-list li {
  font-size: 13px;
  display: flex;
  gap: 6px;
  align-items: baseline;
}
.awol-list .muted { font-size: 11px; }

/* the battalion effective-rate donut */
.board-donut-card { display: flex; flex-direction: column; align-items: center; text-align: center; }
.donut { display: block; margin: 6px auto 2px; }
.donut-pct { font: 800 17px/1 var(--sans); fill: var(--ink); letter-spacing: -.02em; }
.donut-lab { font: 700 7px var(--sans); letter-spacing: .1em; fill: var(--muted); }
.donut-legend { display: flex; flex-wrap: wrap; gap: 8px 16px; justify-content: center; margin-top: 4px; }
.donut-legend span { display: inline-flex; align-items: center; gap: 6px;
  font-size: 11.5px; color: var(--ink-2); }
.donut-legend i { width: 9px; height: 9px; border-radius: 50%; display: inline-block; }

/* the milder form: everyone is accounted for, just missing a company */
.awol-banner.is-mild {
  border-color: var(--warn);
  background: var(--warn-bg);
}
.awol-banner.is-mild .awol-mark { background: var(--warn); }
.awol-banner.is-mild .awol-body strong { color: var(--warn); }

/* the "Others" effective/ineffective switch — only this one disposition
   needs a human to say which way it counts */
.others-eff {
  display: flex;
  align-items: center;
  gap: 5px;
  font-size: 10.5px;
  color: var(--muted);
  white-space: nowrap;
}
.others-eff.is-hidden { display: none; }
.others-eff input { margin: 0; }

/* the Totem's sidebar entry — deliberately set apart from the official tools */
.nav-divider {
  margin: 10px 6px 4px;
  padding-top: 10px;
  border-top: 1px dashed rgba(255,255,255,.12);
  font-size: 9px;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--panel-ink-2);
  opacity: .7;
}
.nav-item.is-apart { color: var(--gold-2); font-style: italic; }
.nav-item.is-apart:hover { background: rgba(232,178,62,.10); color: var(--gold-2); }

/* ---------------------------------------------------------- data analytics */
/* A section header that groups several cards under one question, rather
   than a flat stack of equally-weighted boxes with nothing separating
   "readiness" from "equipment" but a few pixels of margin. */
.analytics-section {
  font-size: 12px; text-transform: uppercase; letter-spacing: .1em;
  color: var(--gold); font-weight: 700;
  margin: 30px 0 14px; padding-bottom: 8px;
  border-bottom: 1px solid var(--line);
}
.analytics-section:first-of-type { margin-top: 22px; }

.analytics-freshness {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  font-size: 12px; color: var(--ink-3); margin: -6px 0 18px;
}
.analytics-live {
  display: inline-flex; align-items: center; gap: 5px;
  color: var(--ok); font-weight: 700; letter-spacing: .04em;
  text-transform: uppercase; font-size: 10.5px;
}

/* Every fact is a direct readout of one query — numbered like a short
   briefing rather than a plain bulleted list, so a reader can point at
   "item 4" instead of describing which line they mean. */
.analytics-facts { border-left: 3px solid var(--gold); }
.analytics-fact-grid { display: grid; gap: 10px; }
.analytics-fact {
  display: flex; gap: 10px; align-items: flex-start;
  font-size: 13px; line-height: 1.5;
}
.analytics-fact-n {
  flex: none; display: grid; place-items: center;
  width: 20px; height: 20px; border-radius: 50%;
  background: color-mix(in srgb, var(--gold) 20%, transparent);
  color: var(--gold); font-size: 10.5px; font-weight: 700;
  margin-top: 1px;
}

.analytics-donut-row { display: flex; align-items: center; gap: 18px; flex-wrap: wrap; }

.analytics-recent-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 8px; }
.analytics-recent-list li { display: flex; flex-direction: column; gap: 1px; font-size: 13px; }

/* ---------------------------------------------------------- mobility & POL */
.grid-2-uneven { display: grid; grid-template-columns: 2fr 1fr; gap: 16px; align-items: start; }
@media (max-width: 980px) { .grid-2-uneven { grid-template-columns: 1fr; } }
.mobility-fund-cell { display: flex; gap: 6px; align-items: center; }
.mobility-fund-cell select { flex: 1; min-width: 90px; }
.mobility-fund-cell input { width: 80px; flex: none; }
.table-grid .col-fund { width: 200px; }
td.nr { text-align: right; width: 34px; color: var(--ink-3); }
.mobility-row-form { display: flex; align-items: center; gap: 6px; padding: 6px 12px; flex-wrap: wrap; }
.mobility-row-cell { flex: 1; min-width: 110px; }
.mobility-row-cell input, .mobility-row-cell select { width: 100%; box-sizing: border-box; }
