/* Copyright (c) 2026, SCVGN / Michael
   Licensed under the BSD 2-Clause License. See LICENSE in the project root.

   TMDCS visual system — the whole of it. One stylesheet, no CDN, no
   JavaScript, dark only. Implements docs/design-language.md and
   docs/spec-v0.1-alpha.md section 4.

   Contents
     1  Tokens                    7  Flashes and banners
     2  Reset and base            8  Chips, badges, states
     3  Layout                    9  Tables — the report
     4  Nav and tabs             10  Toolbar and filters
     5  Cards                    11  Login card
     6  Buttons and forms        12  Styleguide helpers
*/

/* ------------------------------------------------------------------ 1 */
/* Palette taken verbatim from restrictions/static/css/app.css. Do not
   retune. The ramps below are the corrected v0.1 tokens: they were
   re-measured against the surfaces they actually sit on, not against
   --panel, on which no chip is ever drawn. */
:root {
  /* fleet palette — verbatim */
  --bg: #14161a;
  --panel: #1d2026;
  --text: #d6d9de;
  --accent: #4f9cf9;
  --danger: #e05555;

  /* fleet supporting values */
  --muted: #8b95a3;
  --border: #2a2e35;
  --input-border: #333;
  --on-accent: #0b0d10;
  --flash-error-bg: #3a1f22;
  --flash-info-bg: #1f2c3a;
  --code-bg: #26313f;

  /* TMDCS surfaces the fleet has no equivalent of */
  --rule-v: #23262c;        /* vertical column rule, quieter than --border */
  --head-bg: #242830;       /* sticky table head */
  --row-zebra: #20242b;
  --row-hover: #262b33;
  --row-ineffective: #1a1d22;
  --row-error: #2e2126;
  --row-error-hi: #3c282e;  /* error row, hovered — the lightest real ground */
  --row-total: #242830;
  --input-bg: #171a1f;

  /* ELEMENT TYPE (5). Colour is the THIRD cue: every chip carries its
     3-letter code AND a distinct glyph, so the ramp is never load-bearing
     on its own. --t-repetitive is deliberately near-neutral because most
     rows are Repetitive and colouring the majority is noise. */
  --t-repetitive: #aeb9c9;   /* circle   */
  --t-occasional: #19b6d8;   /* diamond  */
  --t-contingency: #efab3f;  /* square   */
  --t-foreign: #b281f0;      /* triangle */
  --t-ineffective: #8c95a0;  /* ring     */

  /* The chip grounds those five are measured on. #332e44 is the surface
     --t-foreign was solved against (4.51:1); do not lighten it. */
  --chip-repetitive-bg: #2a2e35;
  --chip-occasional-bg: #152f3a;
  --chip-contingency-bg: #38301f;
  --chip-foreign-bg: #332e44;
  --chip-ineffective-bg: #292d32;

  /* ELEMENT CATEGORY (3). VA/ENVA/NVA text is always present; the second
     cue is fill weight — boxed / bare / bordered — not hue. #3c2b34 is the
     surface --c-nva was solved against (4.53:1). */
  --c-va: #8ae8b0;
  --c-enva: #7fb0e8;
  --c-nva: #ec6f87;
  --chip-va-bg: #1d3529;
  --chip-enva-bg: #1f2c3a;
  --chip-nva-bg: #3c2b34;

  /* type */
  --mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  --radius: 8px;
  --shadow-card: 0 8px 30px rgba(0, 0, 0, 0.35);

  /* sticky column geometry — the left offset of .col-desc is the width of
     .col-el, so these two must stay in step */
  --col-el-w: 4.5rem;
  --col-desc-w: 14rem;
}

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

html {
  color-scheme: dark;
}

body {
  margin: 0;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  background: var(--bg);
  color: var(--text);
  font: 15px/1.5 system-ui, sans-serif;
}

a {
  color: var(--accent);
  text-decoration: none;
}

a:hover,
a:focus-visible {
  text-decoration: underline;
}

h1 {
  font-size: 1.5rem;
  margin: 0 0 1rem;
}

h2 {
  font-size: 1.2rem;
  margin: 2rem 0 0.75rem;
}

h3 {
  font-size: 1rem;
  margin: 1.5rem 0 0.5rem;
}

p {
  margin: 0 0 0.75rem;
}

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

code,
kbd,
samp {
  font-family: var(--mono);
  font-size: 0.9em;
  background: var(--code-bg);
  border: 1px solid var(--accent);
  border-radius: 4px;
  padding: 0.05rem 0.35rem;
}

pre {
  font-family: var(--mono);
  background: var(--code-bg);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 0.75rem 1rem;
  overflow-x: auto;
}

pre code {
  background: none;
  border: 0;
  padding: 0;
}

.muted {
  color: var(--muted);
}

.small {
  font-size: 0.82rem;
}

.nowrap {
  white-space: nowrap;
}

.mono {
  font-family: var(--mono);
}

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

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* ------------------------------------------------------------------ 3 */
.site-header {
  background: var(--panel);
  border-bottom: 1px solid var(--border);
}

.site-header > .bar {
  max-width: 1280px;
  margin: 0 auto;
  padding: 0.6rem 1rem;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.75rem 1.25rem;
}

.brand {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  color: var(--text);
  font-weight: 600;
  letter-spacing: 0.02em;
}

.brand:hover {
  text-decoration: none;
}

.brand span {
  color: var(--accent);
}

.brand .logo {
  height: 28px;
  width: auto;
}

/* Ship the light rule even though v0.1 is dark-fixed: adding a theme
   toggle later then costs no CSS. */
.logo-light {
  display: none;
}

.logo-dark {
  display: inline;
}

[data-theme="light"] .logo-light {
  display: inline;
}

[data-theme="light"] .logo-dark {
  display: none;
}

main {
  width: 100%;
  max-width: 960px;
  margin: 0 auto;
  padding: 1.5rem 1rem;
  flex: 1 0 auto;
}

main.auth {
  max-width: 420px;
  padding-top: 3rem;
}

main.wide {
  max-width: 1280px;
}

main.full {
  max-width: none;
}

.site-footer {
  background: var(--panel);
  border-top: 1px solid var(--border);
  color: var(--muted);
  text-align: center;
  font-size: 0.82rem;
  padding: 0.9rem 1rem;
}

.page-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.5rem 1rem;
  margin-bottom: 1rem;
}

.page-head h1 {
  margin: 0;
}

.stack > * + * {
  margin-top: 1rem;
}

.row-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  align-items: center;
}

/* An action row that follows a card is separated from it.
 *
 * `.row-actions` carries `gap` — which is spacing BETWEEN the buttons — and no
 * margin whatsoever, so the row itself hugs the button and whatever sits above
 * it touches that. Michael, 2026-08-17, diagnosing it from the rendered page
 * better than my first fix did:
 *
 *     I can see that the padding and border are applied on the text field of
 *     the button, not the button filed it self, that why both containers from
 *     top and buttom are touching the button container.
 *
 * Exactly so: the visible padding belongs to `.btn`, not to its container. My
 * earlier `form + .card` gave the section BELOW a submit row its 1.5rem and
 * did nothing at all about the card ABOVE, so "Save the template" was still
 * welded to the bottom of Contingency — half a fix, which on screen reads as
 * no fix.
 *
 * 1.5rem to match `form + .card`, so a submit row sits evenly between what it
 * saves and what comes next rather than drifting toward one of them. Scoped to
 * `.card +` because a `.row-actions` used as a card's own footer relies on
 * having no margin, and several screens open with one directly under the page
 * heading where a gap would be wrong. */
.card + .row-actions {
  margin-top: 1.5rem;
}

/* ------------------------------------------------------------------ 4 */
.nav {
  margin-left: auto;
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem 1rem;
  align-items: center;
}

.nav a {
  color: var(--text);
  padding: 0.15rem 0;
  border-bottom: 2px solid transparent;
}

.nav a:hover {
  color: var(--accent);
  text-decoration: none;
}

.nav a.active {
  color: var(--accent);
  border-bottom-color: var(--accent);
}

.nav .who {
  color: var(--muted);
  font-size: 0.82rem;
}

/* Project tabs. Every tab is a real <a href> to a real route — there is no
   JavaScript anywhere in TMDCS, so tabs are bookmarkable and Back works. */
.tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem;
  border-bottom: 1px solid var(--border);
  margin-bottom: 1.25rem;
}

.tabs a {
  color: var(--muted);
  padding: 0.45rem 0.8rem;
  border: 1px solid transparent;
  border-bottom: 0;
  border-radius: var(--radius) var(--radius) 0 0;
  margin-bottom: -1px;
}

.tabs a:hover {
  color: var(--text);
  text-decoration: none;
}

.tabs a.active {
  color: var(--text);
  background: var(--panel);
  border-color: var(--border);
}

/* ------------------------------------------------------------------ 5 */
.card {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 1.5rem;
  /* Named container so the lists inside can respond to the CARD's width
   * rather than the viewport's — the same list is used in a full-width panel
   * and in a third of a row, and only the card knows which. */
  container: card / inline-size;
  /* Cards in a row are stretched to the tallest by the grid; make their own
   * contents lay out top-to-bottom so a trailing hint sits at the bottom
   * instead of floating in the middle of the extra height. */
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
  min-width: 0;
}

/* The card's own children must not force it wider than its grid track. */
.card > * {
  min-width: 0;
}

/* A trailing explanatory note is pushed to the bottom, so cards of unequal
 * content still line their footers up with each other. */
.card > .field-hint:last-child {
  margin-top: auto;
  margin-bottom: 0;
}

.card + .card {
  margin-top: 1rem;
}

/* A card that follows a form gets the same break a card following a card does.
 *
 * Michael, 2026-08-17, on the allowance guide:
 *
 *     The button SAVE THE TEMPLATE no margin and padding between the
 *     'contingency' and 'assest on element at a time' Could you add some
 *     space please.
 *
 * `.card + .card` above spaces the cards INSIDE a form, so the rhythm looks
 * right all the way down — and then stops dead at `</form>`, because the next
 * card's previous sibling is the form, not a card. The submit button therefore
 * sat flush against the heading of the next section, which reads as though the
 * button belongs to it.
 *
 * 1.5rem rather than the 1rem between cards: this is a bigger seam than one
 * card to the next — it is the end of everything the button will save — and
 * 1.5rem is already this sheet's larger break (`.card-grid`), so it is the
 * existing rhythm rather than a new number. */
form + .card {
  margin-top: 1.5rem;
}

.card-title {
  margin: 0 0 0.75rem;
  font-size: 1.05rem;
}

/* 260px was too narrow for the content that actually goes in these cards: a
 * label column plus a value inside 1.5rem of padding left about 210px, which is
 * where the vertical-fragment wrapping came from. 22rem is the width at which
 * every label in the app fits on one or two sensible lines; below that the
 * container query on `.kv` stacks them instead.
 *
 * `auto-fit` rather than `auto-fill` so a lone card in the last row grows to
 * fill the space instead of sitting at minimum width beside empty tracks —
 * that is the "blocks are different sizes" effect. */
.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(22rem, 1fr));
  gap: 1rem;
  align-items: stretch;
  /* A grid has no bottom margin of its own, so whatever followed it — the
   * cycle-quantity toolbar on the study summary — sat flush against the cards
   * with no gap at all. Match the gap between the cards so the rhythm is even
   * in both directions. */
  margin-block-end: 1.5rem;
}

.card-grid:last-child {
  margin-block-end: 0;
}

/* Definition lists: a label column that sizes to its content, but CAN shrink.
 *
 * `max-content` alone was wrong twice over. A grid item's default `min-width`
 * is `auto`, which refuses to go below the content's intrinsic size, so the
 * label column took whatever its longest label wanted and the whole grid
 * overflowed its card. The longest label in the app is "Time elapsed after the
 * last breakpoint" at 38 characters, and it sits in the study summary's Clock
 * card — so TEBS, the last breakpoint, the finish and TEAF were pushed outside
 * their card, with the neighbouring card painted over the top of them. Four
 * values that were rendered, correct, and invisible.
 *
 * `minmax(0, max-content)` keeps the sizing behaviour and restores the ability
 * to shrink; the wrap rules let a long label use two lines instead of forcing
 * the page sideways, which is what it did at 768px. */
.kv {
  display: grid;
  grid-template-columns: minmax(0, max-content) minmax(0, 1fr);
  gap: 0.35rem 1rem;
  margin: 0;
}

/* Wrap at spaces, NOT mid-word.
 *
 * `overflow-wrap: anywhere` was added here to stop a long label overflowing its
 * card, and it over-corrected: in a card only 260px wide the label column is
 * about 210px, and `anywhere` breaks inside words rather than between them, so
 * "Ignored count towards a cycle" came out as a vertical column of fragments.
 * The `minmax(0, …)` above is what actually fixes the overflow; this only has
 * to let a long label take a second line. `break-word` breaks a word only when
 * that word alone cannot fit, which is the behaviour wanted. */
.kv dt {
  color: var(--muted);
  overflow-wrap: break-word;
}

.kv dd {
  margin: 0;
  overflow-wrap: break-word;
}

/* Below this the label column has no room to be a column at all, so stop
 * pretending: stack the label over its value and let both use the full width.
 * A container query rather than a media query, because what matters is how wide
 * the CARD is, not the viewport — the same list appears in a full-width panel
 * and in a third of a row. */
@container card (max-width: 22rem) {
  .kv {
    grid-template-columns: 1fr;
    gap: 0 0;
  }

  .kv dt {
    margin-top: 0.5rem;
  }

  .kv dt:first-child {
    margin-top: 0;
  }
}

.empty {
  border: 1px dashed var(--border);
  border-radius: var(--radius);
  padding: 2rem 1rem;
  text-align: center;
  color: var(--muted);
}

/* ------------------------------------------------------------------ 6 */
.btn {
  display: inline-block;
  font: inherit;
  line-height: 1.2;
  padding: 0.45rem 0.85rem;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--panel);
  color: var(--text);
  cursor: pointer;
  text-decoration: none;
}

.btn:hover {
  border-color: var(--accent);
  color: var(--text);
  text-decoration: none;
}

.btn-primary {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--on-accent);
  font-weight: 600;
}

.btn-primary:hover {
  filter: brightness(1.08);
  color: var(--on-accent);
}

.btn-danger {
  background: var(--danger);
  border-color: var(--danger);
  color: var(--on-accent);
  font-weight: 600;
}

.btn-danger:hover {
  filter: brightness(1.08);
  color: var(--on-accent);
}

.btn-ghost {
  background: transparent;
}

.btn-sm {
  padding: 0.2rem 0.5rem;
  font-size: 0.85rem;
}

.btn-lg {
  padding: 0.6rem 0.9rem;
  font-size: 1.05rem;
}

.btn-block {
  display: block;
  width: 100%;
}

.btn:disabled,
.btn[aria-disabled="true"] {
  opacity: 0.5;
  cursor: not-allowed;
}

.field {
  margin-bottom: 1rem;
}

.field > label,
.form-label {
  display: block;
  margin-bottom: 0.3rem;
  color: var(--muted);
}

input[type="text"],
input[type="password"],
input[type="email"],
input[type="number"],
input[type="date"],
input[type="search"],
input[type="file"],
select,
textarea {
  width: 100%;
  font: inherit;
  color: var(--text);
  background: var(--input-bg);
  border: 1px solid var(--input-border);
  border-radius: 6px;
  padding: 0.4rem 0.6rem;
}

input[type="number"] {
  font-family: var(--mono);
  text-align: right;
}

/* A read-only field must LOOK read-only. The element code on an existing
   element is drawn rather than omitted — its absence read as an oversight and
   sent people hunting for the setting that had hidden it — but a box painted
   like every other box is an invitation to type in it, and the explanation sits
   below the field where it is read second. Muted, dimmed and cursor: default,
   so the answer arrives before the click. The hint text still says why. */
input[readonly],
textarea[readonly] {
  color: var(--muted);
  background: var(--panel);
  border-style: dashed;
  cursor: default;
}

select {
  width: auto;
}

textarea {
  min-height: 6rem;
}

.field-error {
  color: var(--text);
  font-size: 0.85rem;
  margin-top: 0.3rem;
}

.field-error::before {
  content: "\26A0\FE0E";
  color: var(--danger);
  margin-right: 0.35rem;
}

.field-hint {
  color: var(--muted);
  font-size: 0.85rem;
  margin-top: 0.3rem;
}

input.is-invalid,
select.is-invalid,
textarea.is-invalid {
  border-color: var(--danger);
}

.form-inline {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 0.75rem;
}

.form-inline .field {
  margin-bottom: 0;
}

/* ------------------------------------------------------------------ 7 */
.flash {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 0.7rem 1rem;
  margin-bottom: 1rem;
}

.flash-error,
.flash-danger {
  background: var(--flash-error-bg);
  border-color: var(--danger);
}

.flash-info,
.flash-message,
.flash-success {
  background: var(--flash-info-bg);
  border-color: var(--accent);
}

/* The blocking banner: validation errors, and the contingency
   double-count guard. It is loud on purpose — a report you cannot see the
   warning on is a report you cannot defend. */
.banner {
  border: 1px solid var(--accent);
  border-left-width: 4px;
  border-radius: var(--radius);
  background: var(--flash-info-bg);
  padding: 0.85rem 1rem;
  margin-bottom: 1rem;
}

.banner-blocking {
  border-color: var(--danger);
  background: var(--flash-error-bg);
}

.banner-title {
  font-weight: 600;
  margin: 0 0 0.25rem;
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
}

.banner-blocking .banner-title::before {
  content: "\26A0\FE0E";
  color: var(--danger);
}

.banner-title + p {
  margin-bottom: 0;
}

.banner p:last-child {
  margin-bottom: 0;
}

/* One validation finding: the rule's own wording, then the fix. */
.finding {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1rem;
  align-items: flex-start;
  justify-content: space-between;
  background: var(--panel);
  border: 1px solid var(--border);
  border-left: 4px solid var(--danger);
  border-radius: var(--radius);
  padding: 0.75rem 1rem;
  margin-bottom: 0.75rem;
}

.finding-warn {
  border-left-color: var(--t-contingency);
}

.finding-body {
  flex: 1 1 24rem;
  min-width: 0;
}

.finding-where {
  font-family: var(--mono);
  font-size: 0.85rem;
  color: var(--muted);
  margin-bottom: 0.2rem;
}

.finding-rule {
  margin: 0;
}

.finding-detail {
  color: var(--muted);
  margin: 0.15rem 0 0;
}

/* ------------------------------------------------------------------ 8 */
.chip {
  display: inline-flex;
  align-items: baseline;
  gap: 0.35rem;
  font-size: 0.8rem;
  line-height: 1.4;
  padding: 0.1rem 0.45rem;
  border-radius: 4px;
  border: 1px solid transparent;
  white-space: nowrap;
}

.chip .glyph {
  font-size: 0.9em;
}

/* Element type — code + glyph + colour, in that order of importance. */
.chip-repetitive {
  color: var(--t-repetitive);
  background: var(--chip-repetitive-bg);
}

.chip-occasional {
  color: var(--t-occasional);
  background: var(--chip-occasional-bg);
}

.chip-contingency {
  color: var(--t-contingency);
  background: var(--chip-contingency-bg);
}

.chip-foreign {
  color: var(--t-foreign);
  background: var(--chip-foreign-bg);
}

.chip-ineffective {
  color: var(--t-ineffective);
  background: var(--chip-ineffective-bg);
}

/* Element category — the second cue is fill weight, never hue:
   VA boxed, ENVA bare, NVA bordered. */
.cat {
  display: inline-block;
  font-size: 0.78rem;
  letter-spacing: 0.04em;
  padding: 0.05rem 0.4rem;
  border-radius: 4px;
  border: 1px solid transparent;
  white-space: nowrap;
}

.cat-value_added {
  color: var(--c-va);
  background: var(--chip-va-bg);
}

.cat-essential_non_value_added {
  color: var(--c-enva);
  background: transparent;
  padding-left: 0;
  padding-right: 0;
}

.cat-non_value_added {
  color: var(--c-nva);
  background: var(--chip-nva-bg);
  border-color: var(--c-nva);
}

.badge {
  display: inline-block;
  font-size: 0.78rem;
  padding: 0.05rem 0.45rem;
  border: 1px solid var(--border);
  border-radius: 999px;
  color: var(--muted);
}

.badge-ok {
  color: var(--c-va);
  border-color: var(--c-va);
}

.badge-blocked {
  color: var(--text);
  background: var(--flash-error-bg);
  border-color: var(--danger);
}

.badge-edited {
  color: var(--t-contingency);
  border-color: var(--t-contingency);
}

.badge-accent {
  color: var(--accent);
  border-color: var(--accent);
}

/* ------------------------------------------------------------------ 9 */
/* Tables are the point of this application.

   border-collapse: separate is not a style choice. Collapsed borders do
   not travel with position: sticky cells — the rules stay behind while the
   cell scrolls over them — so the report table draws its own borders per
   cell and sets border-spacing to 0. */
.table-wrap {
  overflow-x: auto;
  border: 1px solid var(--border);
  border-radius: var(--radius);
}

.table-scroll {
  overflow: auto;
  max-height: 70vh;
  border: 1px solid var(--border);
  border-radius: var(--radius);
}

table {
  width: 100%;
  border-collapse: separate;
  border-spacing: 0;
}

th,
td {
  text-align: left;
  padding: 0.5rem 0.75rem;
  border-bottom: 1px solid var(--border);
  border-right: 1px solid var(--rule-v);
  background: var(--row-bg, var(--panel));
  vertical-align: top;
}

th:last-child,
td:last-child {
  border-right: 0;
}

tbody tr:last-child > * {
  border-bottom: 0;
}

thead th {
  --row-bg: var(--head-bg);
  color: var(--muted);
  font-weight: 600;
  white-space: nowrap;
  border-bottom-color: var(--border);
}

/* Row state is published once, on the <tr>, as --row-bg. Every cell reads
   it — including the sticky ones, which is why zebra, hover, error and
   ineffective each need exactly one rule and not one rule per column. */
tbody tr {
  --row-bg: var(--panel);
}

tbody tr:nth-child(even) {
  --row-bg: var(--row-zebra);
}

tbody tr:hover {
  --row-bg: var(--row-hover);
}

tbody tr.is-ineffective {
  --row-bg: var(--row-ineffective);
}

tbody tr.is-error {
  --row-bg: var(--row-error);
}

tbody tr.is-error:hover {
  --row-bg: var(--row-error-hi);
}

/* Numeric columns: right-aligned, monospace, fixed 4 dp, so decimals line
   up down the page. tabular-nums keeps the digit advance constant even
   where the system font would not. */
.num {
  text-align: right;
  font-family: var(--mono);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* A blank cell is a statement, not missing data. The dash is drawn with
   ::after so copying the table into a spreadsheet yields a genuinely
   empty cell rather than a stray character. */
.blank::after {
  content: "\2014";
  color: var(--muted);
}

/* The frequency is ONE cell showing the PAIR — "263 occurrences per 227
   cycles" — never two columns both headed Freq. */
.freq {
  text-align: right;
  font-family: var(--mono);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.freq .den {
  color: var(--muted);
}

/* Ineffective elements render 0(+N): observed time and a count, nothing
   else. */
.ineffective-count .plus {
  color: var(--t-ineffective);
}

.report-table {
  min-width: 62rem;
}

.report-table .col-el {
  width: var(--col-el-w);
  min-width: var(--col-el-w);
  max-width: var(--col-el-w);
  position: sticky;
  left: 0;
  z-index: 2;
  font-family: var(--mono);
}

.report-table .col-desc {
  width: var(--col-desc-w);
  min-width: var(--col-desc-w);
  max-width: var(--col-desc-w);
  position: sticky;
  left: var(--col-el-w);
  z-index: 2;
  border-right: 1px solid var(--border);
  box-shadow: 6px 0 8px -8px rgba(0, 0, 0, 0.75);
}

.report-table thead .col-el,
.report-table thead .col-desc,
.report-table tfoot .col-el,
.report-table tfoot .col-desc {
  z-index: 4;
}

.report-table thead th {
  position: sticky;
  top: 0;
  z-index: 3;
}

/* The totals row sticks to the bottom of the scroll pane, as iePro's does.
   Its cells carry their own --row-bg so the sticky left columns match. */
.report-table tfoot th,
.report-table tfoot td {
  --row-bg: var(--row-total);
  position: sticky;
  bottom: 0;
  z-index: 3;
  border-top: 2px solid var(--border);
  border-bottom: 0;
  font-weight: 600;
}

/* ----------------------------------------------------------------- 10 */
.toolbar {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 0.75rem 1rem;
  margin-bottom: 1rem;
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1.5rem;
  align-items: center;
}

.toolbar-line {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem 1rem;
  align-items: center;
  width: 100%;
}

.toolbar-label {
  color: var(--muted);
  font-size: 0.85rem;
  min-width: 6rem;
}

.toolbar .spacer {
  margin-left: auto;
}

.legend {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem 0.9rem;
  align-items: center;
  color: var(--muted);
  font-size: 0.85rem;
}

/* ----------------------------------------------------------------- 11 */
/* The fleet login page, non-Bootstrap variant: 56px logo centred INSIDE
   the card, muted subtitle, no page heading, large inputs, full-width
   submit. Everything is scoped to .login-card so no other card shifts. */
.login-card {
  box-shadow: var(--shadow-card);
}

.login-card .login-brand {
  text-align: center;
  margin-bottom: 1.5rem;
}

.login-card .login-logo {
  max-height: 56px;
  width: auto;
  max-width: 100%;
}

.login-card .login-subtitle {
  text-align: center;
  color: var(--muted);
  margin-bottom: 1.5rem;
}

.login-card .field {
  margin-bottom: 1rem;
}

.login-card input[type="text"],
.login-card input[type="password"] {
  padding: 0.6rem 0.9rem;
  font-size: 1.05rem;
}

.login-card .btn {
  margin-top: 0.5rem;
}

/* Pre-auth pages other than login show the brand block above the card;
   login.html overrides the block empty so the brand sits inside the card. */
.auth-brand {
  text-align: center;
  margin-bottom: 1.5rem;
}

.auth-brand img {
  max-height: 72px;
  width: auto;
}

.auth-brand .tagline {
  display: block;
  margin-top: 0.5rem;
  color: var(--accent);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-size: 0.82rem;
}

/* ----------------------------------------------------------------- 12 */
.sg-section {
  margin-bottom: 2.5rem;
}

.sg-note {
  color: var(--muted);
  font-size: 0.85rem;
  margin: 0 0 0.75rem;
}

.sg-swatches {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
  gap: 0.75rem;
}

.sg-swatch {
  border: 1px solid var(--border);
  border-radius: 6px;
  overflow: hidden;
}

.sg-swatch .chipface {
  height: 3rem;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--mono);
  font-size: 0.85rem;
}

.sg-swatch .meta {
  padding: 0.35rem 0.5rem;
  font-size: 0.8rem;
  color: var(--muted);
  border-top: 1px solid var(--border);
  font-family: var(--mono);
}

.sg-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 0.75rem;
  align-items: center;
  margin-bottom: 0.75rem;
}

.sg-toc {
  columns: 2 12rem;
  gap: 1.5rem;
  margin: 0 0 2rem;
  padding-left: 1.1rem;
}

/* ------------------------------------------------------------------ */
@media (max-width: 640px) {
  main {
    padding: 1rem 0.75rem;
  }

  .card {
    padding: 1rem;
  }

  .site-header > .bar {
    padding: 0.5rem 0.75rem;
  }

  .nav {
    width: 100%;
    margin-left: 0;
  }

  :root {
    --col-desc-w: 10rem;
  }
}
