/* Flight Deck — the deck's stylesheet.
 *
 * The first .css file the shell has ever had. Until now every deck style was
 * inline in templates/base.html, under a comment that said so
 * ("SKELETON: inline styles only — no static asset pipeline yet").
 *
 * TWO SOURCES, deliberately:
 *
 *   1. The deck's own tokens and chrome, lifted VERBATIM from that inline
 *      block so the existing pages (title, login, profile, lane stubs) render
 *      exactly as they did before. Same values, same selectors, new home.
 *
 *   2. HALO's component vocabulary — the class NAMES and shapes a reader
 *      already knows from apps/halo/halo/static/css/{app,halo}.css: .card,
 *      .btn/.btn--primary, .eyebrow/.headline/.lede/.hint, .section-title,
 *      .dtable. HALO's live theme (halo.css) is itself dark
 *      and already names its tokens --bg/--surface/--line/--ink/--muted/
 *      --accent, the same six the deck names. So harmonizing is not a
 *      repaint: the two apps keep their own colours and share the vocabulary.
 *      The deck's palette is unchanged by this file.
 *
 * Page-specific looks stay with their page. The Title Page's cockpit theme is
 * still a {% block head %} in title.html — it is one landing screen's identity,
 * not a component anyone else reuses.
 */

/* ---- tokens --------------------------------------------------------------
 * The first seven are the deck's own, unchanged (base.html:13-16). The rest
 * are the scale HALO carries and the deck did not yet need: a second surface
 * for hovers and insets, a third text weight, a stronger border, the radius
 * and shadow steps, and the danger family the Danger zone will want. */
:root {
  --bg: #0e1116;
  --surface: #161b22;
  --line: #2a313c;
  --ink: #e6edf3;
  --muted: #8b949e;
  --accent: #3b82f6;
  --accent-ink: #fff;

  --surface-2: #1c222b;
  --faint: #6e7781;
  --line-2: #39414e;
  /* ⚑ ADMIN READABILITY (Lin, 2026-09-07: "admin still looks like one
     continuous slop of data and confusion... you have not done the alternating
     color with the cards within the same sections... the titles of section are
     the same size as the body"). Five tokens added, all named here rather than
     inlined, and all measured — see .adminlayout below for the numbers. */
  --row-a: #1c222b;          /* a card on an odd row — the ground cards had */
  --row-b: #28323f;          /* an even row: a REAL step, not a whisper */
  --fs-section: 20px;        /* a section heading INSIDE a surface */
  --fs-card: 16px;           /* the name of one card */
  --fs-label: 12px;          /* a field's label, above the field */
  --radius: 12px;
  --radius-sm: 8px;
  --shadow: 0 1px 2px rgba(0, 0, 0, .28), 0 6px 18px rgba(0, 0, 0, .24);
  --danger: #e36f5d;
  --danger-bg: #2a161a;
  --danger-line: #6e2330;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font: 15px/1.5 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
}

/* ---- top bar ---- */
.topbar {
  display: flex; align-items: center; gap: 16px;
  padding: 12px 20px; border-bottom: 1px solid var(--line); background: var(--surface);
}
.topbar__brand { font-weight: 700; letter-spacing: .02em; text-decoration: none; color: var(--ink); }
.topbar__right { margin-left: auto; display: flex; align-items: center; gap: 14px; color: var(--muted); font-size: 13px; }
.topbar__right a { color: var(--accent); text-decoration: none; }
/* The lane links in the bar — the way to any surface from any deck page. Quiet
 * by default so they read as navigation rather than as the page's own actions;
 * they wrap rather than overflow on a narrow window. */
.topbar__lanes { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.topbar__lane {
  color: var(--muted); text-decoration: none; font-size: 13px;
  padding: 5px 10px; border-radius: var(--radius-sm); border: 1px solid transparent;
}
.topbar__lane:hover { color: var(--ink); background: var(--surface-2); border-color: var(--line); }
/* The back control that sits at the top of a page rather than at its foot. */
.backlink { display: inline-block; color: var(--accent); text-decoration: none; font-size: 13px; margin-bottom: 10px; }
.backlink:hover { text-decoration: underline; }


/* ---- page shell ---- */
.page { max-width: 880px; margin: 0 auto; padding: 32px 20px; }
/* The admin page is a working surface rather than a reading one: it carries a
 * table of people and a twelve-column grid side by side with a section rail.
 * 880px is a measure chosen for prose, and it is the only page that wants more.
 * Nothing else changes — every other deck page keeps the column it had. */
.page--admin { max-width: 1200px; }

.flashes { max-width: 880px; margin: 12px auto 0; padding: 0 20px; }
.flash { padding: 10px 14px; border-radius: var(--radius-sm); margin-bottom: 8px; border: 1px solid var(--line); background: var(--surface); }
.flash--error { border-color: var(--danger-line); background: var(--danger-bg); }
.flash--info { border-color: #1f3a5f; background: #14202f; }

.footer { color: var(--muted); font-size: 12px; text-align: center; padding: 28px 0; }

/* ---- buttons ----
 * Was `a.btn, button.btn` inline; plain `.btn` here to match HALO's selector.
 * Every .btn in the deck's templates is an <a> or a <button>, so the rendered
 * result is unchanged. */
.btn {
  display: inline-block; padding: 10px 16px; border-radius: var(--radius-sm); border: 1px solid var(--line);
  background: var(--surface); color: var(--ink); text-decoration: none; cursor: pointer; font: inherit;
}
.btn:hover { background: var(--surface-2); }
.btn--primary { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); }
.btn--danger { background: transparent; border-color: var(--danger-line); color: var(--danger); }
.btn--danger:hover { background: var(--danger-bg); }

/* ---- cards and headings ---- */
.card { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius); padding: 20px; }

.eyebrow { font-size: 12px; font-weight: 600; letter-spacing: .12em; text-transform: uppercase; color: var(--accent); }
.headline { color: var(--ink); font-size: 26px; font-weight: 700; letter-spacing: -.02em; margin: 4px 0 0; }
.lede { color: var(--muted); margin: 8px 0 0; }
.hint { color: var(--faint); font-size: 13px; }
.section-title { color: var(--ink); font-size: 15px; font-weight: 600; margin: 0 0 14px; }

/* ============================================================================
   ADMIN READABILITY — the type scale, the zebra, and the air between things.

   Lin, 2026-09-07, on /admin/flight-data: "admin still looks like one
   continuous slop of data and confusion... you have not done the alternating
   color with the cards within the same sections... the titles of section are
   the same size as the body... use design skills and apply industry best
   practices to make this look readable."

   He was right on both counts, literally: .section-title was 15px against a
   14px body — one point of difference doing the work of a heading — and every
   card on a list sat on the same ground.

   ⚑ SCOPED TO .adminlayout ON PURPOSE. Nothing here escapes into LOOP or the
   deck's other pages; the admin is the surface he is looking at.

   ⚑ AND --muted / --faint ARE REDEFINED HERE, WHICH IS THE PRICE OF THE ZEBRA.
   The lighter row (--row-b) is lighter than anything the palette was tuned
   against, and the existing --muted #8b949e falls to 4.22:1 on it — under AA.
   A zebra that makes half the labels harder to read would be the opposite of
   what was asked for, so the two muted tiers are lifted INSIDE the admin only:
   --muted to #9aa4af (6.32:1 on row A, 5.13:1 on row B) and --faint to #929cab
   (5.76:1 / 4.68:1). Both clear AA on BOTH rows, which neither did before.
   ========================================================================== */
.adminlayout { --muted: #9aa4af; --faint: #929cab; }

/* ⚑ THE HEADINGS LIN MEANT ARE `.subhead`, AND THEY WERE 12px — SMALLER THAN
   THE 14px BODY THEY HEAD. "the titles of section are the same size as the
   body" was generous. Since the page became four surfaces the big `.headline`
   is the SURFACE's name; the headings INSIDE it — "Who can sign in", "Add
   someone", "What each role can reach" — are these, and they were doing a
   heading's job dressed as a caption.

   Now 20px, sentence case, with a rule under. The whole admin scale is
   26 / 20 / 16 / 14 / 12 — surface name, section heading, card name, body,
   label — which is close to the 1.2 ratio a type scale usually walks, and
   every step is a step you can see rather than one you have to measure. My
   first pass set 18/15, and my own test caught the section-to-card gap as too
   thin; I widened the scale rather than lowering the threshold. The uppercase-and-tracking device is
   not lost — it moves down a level to `.keygroup__title`, where a genuine
   small label belongs. */
.adminlayout .subhead {
  font-size: var(--fs-section); font-weight: 700; letter-spacing: -.01em;
  text-transform: none; color: var(--ink);
  margin: 34px 0 12px; padding-bottom: 8px; border-bottom: 1px solid var(--line);
}
.adminlayout .adminsec .subhead:first-of-type { margin-top: 22px; }

/* The landing's cards keep their own title style, one step under the surface
   name they lead to. */
.adminlayout .section-title { font-size: var(--fs-card); font-weight: 700; }

/* ONE CARD. More air BETWEEN cards than inside them — the grouping cue that
   makes a list read as a list (dense-admin practice: separate the units, then
   tighten within the unit). */
.adminlayout .personcard { margin-top: 18px; padding: 18px 18px 16px; }
.adminlayout .personcard__head { align-items: center; margin-bottom: 4px; }
.adminlayout .personcard__head strong {
  font-size: var(--fs-card); font-weight: 700; letter-spacing: -.01em; color: var(--ink);
}

/* ⚑ THE ZEBRA, WITHIN A SECTION. The class comes from Jinja's loop.cycle
   rather than :nth-child, because the cards are not always the only children of
   their container and a CSS-only stripe would fall in an arbitrary pattern —
   which is the trap the audio station's zebra was left out for. */
.adminlayout .personcard.row-a,
.adminlayout .dtable tbody tr.row-a td { background: var(--row-a); }
.adminlayout .personcard.row-b,
.adminlayout .dtable tbody tr.row-b td { background: var(--row-b); }
/* Hover still wins over the stripe, or the table stops answering the pointer. */
.adminlayout .dtable tbody tr:hover td { background: var(--surface-2); }

/* A LABEL BELONGS ABOVE ITS FIELD, smaller and quieter than the value it
   names — so a column of fields reads as pairs, not as a wall. */
.adminlayout .label {
  font-size: var(--fs-label); font-weight: 600; letter-spacing: .04em;
  text-transform: uppercase; color: var(--faint); margin: 12px 0 5px;
}

/* THE PRIMARY ACTION IS THE ONE THAT LOOKS LIKE AN ACTION. Archive and Remove
   are quiet neighbours; only what destroys reads red. */
.adminlayout .personcard__acts .btn { font-weight: 600; }
.adminlayout .personcard__acts .btn--primary { box-shadow: 0 1px 0 rgba(0,0,0,.25); }
.adminlayout .removerow .btn { color: var(--danger); border-color: var(--danger-line); }
.muted { color: var(--muted); }

/* ---- forms ---- */
.label { display: block; font-size: 13px; color: var(--muted); margin: 10px 0 4px; }
.field, select.field {
  width: 100%; padding: 9px 11px; border-radius: var(--radius-sm); border: 1px solid var(--line);
  background: var(--bg); color: var(--ink); font: inherit;
}
.field:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px rgba(59, 130, 246, .22); }

/* ---- data table ----
 * HALO's .dtable, in the deck's colours. Same class name, same shape, so a
 * table of people here reads as the same object as a table of flights there. */
.dtable { width: 100%; border-collapse: collapse; font-size: 14px; }
.dtable th { text-align: left; font-weight: 500; color: var(--faint); font-size: 12px; padding: 9px 12px; border-bottom: 1px solid var(--line); }
.dtable td { padding: 11px 12px; border-bottom: 1px solid var(--line); }
.dtable tr:last-child td { border-bottom: 0; }
.dtable tbody tr:hover { background: var(--surface-2); }

/* ---- admin page: the left rail and its sections ----
 * The shape the consolidation plan draws (docs/admin-consolidation/
 * CONSOLIDATION_PLAN.md §4): five sections down a left rail, in the order
 * who-can-get-in, what-they-did, then the catalogues, then the dangerous
 * operations last. Today every section is empty; each one fills in its own
 * move. */
.adminlayout { display: grid; grid-template-columns: 208px minmax(0, 1fr); gap: 28px; align-items: start; }
/* ⚑ minmax(0, 1fr), not 1fr. A grid track's default minimum is min-content, so
 * the content column refuses to shrink below the widest thing in it — and the
 * whole page grows sideways instead of the twelve-column matrix scrolling
 * inside its own .tablewrap. Move 1 found this the moment the first section had
 * real content in it; Move 0's empty sections never pushed on the track. */
.adminlayout > * { min-width: 0; }
.adminrail { display: flex; flex-direction: column; gap: 2px; position: sticky; top: 20px; }
.adminrail__item {
  padding: 9px 12px; border-radius: var(--radius-sm); border: 1px solid transparent;
  color: var(--muted); text-decoration: none; font-size: 12px; font-weight: 600;
  letter-spacing: .1em; text-transform: uppercase;
}
.adminrail__item:hover { background: var(--surface-2); color: var(--ink); }
.adminrail__item.active { background: var(--surface); border-color: var(--line); color: var(--ink); }
.adminrail__item--danger { color: var(--danger); }

.adminsec { scroll-margin-top: 20px; }
.adminsec + .adminsec { margin-top: 20px; }

/* ⚑ THE SHADE RANGE ACROSS THE FOUR ADMIN SURFACES (Lin, 2026-09-05: "make
   separate surfaces keep the main look, but maybe make one darker and lighter
   etc. get a range for 4 section then apply"; ruled by menu 2026-09-07).

   EVERY GROUND IS AN EXISTING TOKEN. Nothing new was invented: People and
   Flight data sit on --surface, the working ground the whole deck uses;
   Security steps one up to --surface-2, because you are reading a record
   rather than changing one; the Danger zone takes --danger-bg, which the
   palette already carried for exactly this.

   ⚑ MEASURED, AND THE NUMBERS SAY SOMETHING WORTH KNOWING. As WCAG contrast —
   which is LUMINANCE only — base against one-step-lighter is 1.082:1 and base
   against the danger ground is 1.012:1. The danger ground is not a lighter or
   darker step at all: it is a HUE step, red-brown against blue-grey, which the
   eye reads clearly and a luminance ratio cannot express. So the range is
   deliberately quiet, which is what "keep the main look" asks for; a bolder
   one would need a new token, and the order says to say so rather than invent
   one. --ink stays AAA on all three grounds (14.6, 13.5 and 14.5 to 1). */
.adminsec--security { background: var(--surface-2); }
.adminsec--danger { background: var(--danger-bg); border-color: var(--danger-line); }
.adminsec__empty { color: var(--faint); font-size: 13px; margin: 10px 0 0; }

@media (max-width: 760px) {
  .adminlayout { grid-template-columns: 1fr; }
  .adminrail { position: static; flex-direction: row; flex-wrap: wrap; }
}

/* ---- admin page: People & Access (Move 1) ----
 * The components LOOP's Users & Access page and Roles matrix used, in the
 * deck's own colours and under the deck's own class vocabulary. LOOP carried
 * these as a <style> block inside each admin template (roles.html:5-19,
 * access.html:4-8); moving them here is half of what "harmonize in style" meant
 * — the page that arrived from LOOP now dresses out of the same wardrobe as the
 * rest of the deck rather than bringing its own. */
.subhead {
  color: var(--ink); font-size: 12px; font-weight: 700; letter-spacing: .1em;
  text-transform: uppercase; margin: 26px 0 8px;
}
.adminsec .subhead:first-of-type { margin-top: 20px; }

/* Wide content scrolls inside its own box; the page itself never does. */
.tablewrap { overflow-x: auto; margin-top: 10px; border: 1px solid var(--line); border-radius: var(--radius-sm); }
.tablewrap .dtable th:first-child, .tablewrap .dtable td:first-child { padding-left: 14px; }

.pill {
  display: inline-block; font-size: 11px; font-weight: 700; letter-spacing: .04em;
  padding: 2px 9px; border-radius: 999px; background: var(--surface-2); color: var(--muted);
}
.pill--on { background: rgba(59, 130, 246, .16); color: var(--accent); }
.pill--you { background: rgba(59, 130, 246, .1); color: var(--muted); }

/* A form laid out as labelled fields that reflow rather than wrap awkwardly. */
.formgrid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: 12px; align-items: end; margin-top: 12px;
}
.formgrid__go { display: flex; align-items: end; }
.formgrid__go .btn { width: 100%; }

/* One person. Inset from the section so the list reads as a list of cards
 * rather than as a run of separate panels. */
.personcard {
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  background: var(--surface-2); padding: 16px; margin-top: 14px;
  scroll-margin-top: 20px;
}
.personcard__head { display: flex; justify-content: space-between; gap: 12px; flex-wrap: wrap; align-items: flex-start; }
.personcard__acts { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }
.personcard__acts form { display: inline; }

.keygroup { margin-top: 14px; }
.keygroup__title {
  font-size: 11px; font-weight: 700; letter-spacing: .1em; text-transform: uppercase;
  color: var(--faint); margin-bottom: 8px;
}
.keygrid { display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); gap: 8px; }
.keybox {
  display: flex; gap: 10px; align-items: flex-start; padding: 9px 11px;
  border: 1px solid var(--line); border-radius: var(--radius-sm); background: var(--bg);
}
.keybox input { margin-top: 3px; accent-color: var(--accent); }

/* The typed confirmation. Bordered in the danger family so it reads as the one
 * control on the card that does not come back. */
.removerow {
  margin-top: 16px; padding-top: 14px; border-top: 1px solid var(--line);
}
.removerow__go { display: flex; gap: 10px; margin-top: 8px; flex-wrap: wrap; }
.removerow__go .field { max-width: 320px; }

/* The role x permission matrix. Roles down, keys across, the two apps' key
 * groups separated by a rule so a tick's product is readable at a glance.
 *
 * ⚑ THE KEY LABELS ARE TURNED ON THEIR SIDE, and that is the whole reason all
 * twelve fit. Laid flat, seven labels alone fill the page and the ePOV half sits
 * off the right edge behind a scrollbar — a grid you have to scroll to read is
 * not a grid, and "what can this role reach" is a question you answer by
 * looking at one row at once. Upright headers cost ~34px a column instead of
 * ~110px, so the whole matrix lands inside the page. */
/* Shrink to its content rather than stretching the role column across the
 * page: the grid reads as one object with the labels beside it. */
.matrix { table-layout: auto; width: auto; min-width: 100%; }
.matrix .matrix__role { width: 1%; }
.matrix th, .matrix td { text-align: center; white-space: nowrap; }
.matrix .matrix__role { text-align: left; padding-right: 18px; }
.matrix__group {
  font-size: 11px; letter-spacing: .1em; text-transform: uppercase; color: var(--accent);
  border-bottom: 1px solid var(--line); padding-bottom: 6px;
}
.matrix__key {
  padding: 0 0 8px; vertical-align: bottom; width: 34px;
}
.matrix__key span {
  display: block; writing-mode: vertical-rl; transform: rotate(180deg);
  font-size: 11px; font-weight: 600; color: var(--muted); letter-spacing: .02em;
  height: 118px; margin: 0 auto; text-align: left;
}
.matrix__key--first { border-left: 1px solid var(--line-2); }
.matrix__cell { padding: 9px 0; }
.matrix__cell input { width: 16px; height: 16px; accent-color: var(--accent); cursor: pointer; }
.matrix tbody tr:hover { background: var(--surface-2); }

/* ---- the Security log and View as (Move 2) --------------------------------
 * The band, the launcher and the log. No new tokens: every colour below is one
 * the file already defines, so this section cannot drift from the deck's
 * palette (and nothing here touches LOOP's or the portal's stylesheets — the
 * band each of those draws is their own).
 */

/* The band saying whose view this is. It is deliberately the loudest thing on
 * the page: the failure it prevents is a master forgetting they are inside
 * somebody else's view, so it must be impossible to read past. Full-bleed and
 * above the flashes, on EVERY page. */
.viewband {
  display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
  padding: 10px 18px;
  background: var(--danger-bg);
  border-bottom: 1px solid var(--danger-line);
  color: var(--danger);
  font-size: 13px;
}
.viewband__text { flex: 1 1 auto; }
.viewband__btn {
  background: transparent; border: 1px solid var(--danger-line); color: var(--danger);
  border-radius: var(--radius-sm); padding: 5px 12px; font: inherit; cursor: pointer;
}
.viewband__btn:hover { background: var(--surface-2); }

/* The launcher's "you are currently viewing as" state, inside the section. */
.viewas--on {
  display: flex; align-items: center; justify-content: space-between; gap: 14px;
  flex-wrap: wrap;
  margin-top: 12px; padding: 14px 16px;
  border: 1px solid var(--danger-line); border-radius: var(--radius-sm);
  background: var(--danger-bg);
}
.viewas--on p { margin: 0; }

/* The log's two filters sit on one row and collapse rather than wrap oddly. */
.logfilter { margin-top: 14px; }

/* An archived row: still listed, visibly off the intake form. Dimmed rather
   than hidden, because "where did that pilot go" is the support call archiving
   is supposed to prevent. (Move 4 — Flight data.) */
.row--archived td { opacity: .55; }
.row--archived td strong { font-weight: 500; }
