/* GUI Design System — the canonical stylesheet.
   Tahoma 11px, steel-blue window chrome (#336699), gradient table headers,
   fieldset legends, yellow row selection. Brutally practical; density first.
   Saturated color appears only on exceptions. Reference data stays "mono"
   (deliberately-proportional Verdana). Spec: ../DESIGN.md.

   Origin: backlog-map's app.css. App-specific class names from that app are
   kept as aliases (table.backlog, .order-header, .order-title, …) so markup
   copies verbatim between apps; new apps should use the generic names
   (table.data-table, .window, .window-title, …). */

:root {
    --chrome: #e6e8ea;          /* app background behind panels */
    --panel: #ffffff;
    --panel-tint: #f4f6f8;      /* fieldset / toolbar fill */
    --grid: #d7dce1;            /* light row rules */
    --grid-strong: #c3cad1;     /* header cell separators */
    --border: #b9bfc6;          /* panel borders */
    --border-dark: #8f979f;     /* button / input borders */
    --ink: #1a1a1a;
    --ink-2: #444444;
    --ink-3: #616973;           /* darkened so 10px whispers clear 4.5:1 on the tint */

    --blue: #336699;            /* the 2009 Java blue */
    --bar-top: #4a7dae;         /* title-bar gradient */
    --bar-bot: #2f5d8c;
    --bar-text: #ffffff;
    --link: #235e9e;

    /* severity law: MISSING = red, LATE = yellow, GOOD = green.
       Missing trumps late trumps good. Do not reorder. */
    --late: #8a6d00;
    --late-bg: #fff6c8;
    --gap: #c00000;
    --gap-bg: #ffdede;
    --warn: #8a6d00;
    --warn-bg: #fff6c8;
    --good: #1e7a1e;
    --good-bg: #ddf0dd;
    --select: #ffffcc;          /* classic selected-row yellow */

    --sans: Tahoma, Verdana, "Segoe UI", Arial, sans-serif;
    /* the "data" font: forum-classic Verdana (MyBB-style), not actually monospace —
       column alignment of digits traded away for the period look, deliberately */
    --mono: Verdana, Arial, sans-serif;
}

* { box-sizing: border-box; }

body {
    margin: 0;
    background: var(--chrome);
    color: var(--ink);
    font: 11px/1.4 var(--sans);
}

a { color: var(--link); }
a:focus-visible, button:focus-visible { outline: 1px dotted var(--ink); outline-offset: 1px; }

.mono {
    font-family: var(--mono);
    font-variant-numeric: tabular-nums;
}

.trunc {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* old-school chunky scrollbars */
::-webkit-scrollbar { width: 14px; height: 14px; }
::-webkit-scrollbar-track { background: #eef0f2; border-left: 1px solid var(--grid); }
::-webkit-scrollbar-thumb {
    background: linear-gradient(90deg, #d9dde1, #bac1c8);
    border: 1px solid var(--border-dark);
}
::-webkit-scrollbar-thumb:hover { background: linear-gradient(90deg, #cdd2d7, #a9b1b9); }

/* ---------- title bar ---------- */

.topbar {
    display: flex;
    align-items: center;
    gap: 14px;
    padding: 4px 10px;
    background: linear-gradient(var(--bar-top), var(--bar-bot));
    border-bottom: 1px solid #22456a;
    height: 31px;
}

.brand {
    color: var(--bar-text);
    font: bold 12px var(--sans);
    letter-spacing: 0.1em;
    text-decoration: none;
    text-shadow: 0 1px 0 #1e3f61;
}

.global-search input {
    width: 260px;
    padding: 2px 5px;
    border: 1px solid #22456a;
    font: 11px var(--sans);
    background: var(--panel);
}

.presets {
    display: flex;
    gap: 3px;
}

.presets a {
    padding: 2px 9px;
    text-decoration: none;
    font-size: 11px;
    color: #d7e4f1;
    border: 1px solid transparent;
}

.presets a:hover { border-color: #9cb8d4; color: var(--bar-text); }

.presets a.active {
    color: var(--blue);
    background: var(--panel);
    border-color: #22456a;
    font-weight: bold;
}

.freshness {
    margin-left: auto;
    font-size: 10px;
    color: #cfe0f0;
}

/* ---------- split layout ---------- */

.split {
    display: grid;
    grid-template-columns: minmax(420px, 40%) 1fr;
    height: calc(100vh - 31px);
}

.pane { overflow-y: auto; }

.pane-list {
    border-right: 1px solid var(--border-dark);
    background: var(--panel);
    /* stat strip + filters stay put; only the table body scrolls */
    display: flex;
    flex-direction: column;
    overflow: hidden;
}

.table-scroll {
    flex: 1 1 auto;
    overflow-y: auto;
}

.pane-list .pager { flex-shrink: 0; }

.pane-map { padding: 8px 12px; }

@media (max-width: 900px) {
    .split { display: block; height: auto; }
    .topbar { height: auto; flex-wrap: wrap; }
    .pane-list { border-right: none; }
}

/* LOCAL ADDITION to the copied kit — port upstream to gui-design-system.
   Two-column detail window (doc detail page): tables in the left column, the
   viewed document filling the right. Title bar spans both; the embedded
   viewer runs viewport-tall. Collapses to the plain stacked window under
   900px, same breakpoint as .split. */
.window-cols {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0 8px;
    align-items: start;
}
.window-cols > .window-title { grid-column: 1 / -1; }
.window-cols > .window-body { grid-column: 2; grid-row: 2 / span 3; }
.window-cols > .window-body object { width: 100%; height: 83vh; }
.window-cols > .data-table { grid-column: 1; }
@media (max-width: 900px) {
    .window-cols { display: block; }
}

/* ---------- stat strip ---------- */

.stat-strip {
    display: flex;
    gap: 14px;
    padding: 4px 8px;
    border-bottom: 1px solid var(--border);
    background: var(--panel-tint);
    font-size: 11px;
    color: var(--ink-2);
}

.stat-strip a { text-decoration: none; }
.stat-strip a:hover { text-decoration: underline; }
.stat-strip strong { font-family: var(--mono); }
.stat-late strong { color: var(--late); }
.stat-gap strong { color: var(--gap); }

/* ---------- filters ---------- */

.filters {
    padding: 3px 6px 5px;
    border-bottom: 1px solid var(--border);
    background: var(--panel);
}

.filter-set {
    margin: 0;
    padding: 2px 8px 6px;
    border: 1px solid var(--border);
    background: var(--panel-tint);
}

.filter-set legend {
    font: bold 11px var(--sans);
    color: var(--blue);
    padding: 0 4px;
}

.filter-row {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    align-items: end;
    font-size: 10px;
    color: var(--ink-2);
}

/* LOCAL ADDITION to the copied kit — port upstream to gui-design-system.
   `gap` only spaces flex items WITHIN a row, so two stacked .filter-rows met at
   0px and their controls' borders touched (docs.html's Author button against the
   upload row, and the same in doc.html, admin/users.html, editor.html). 6px is
   the row's own gap, so stacked rows now breathe on the kit's existing rhythm. */
.filter-row + .filter-row { margin-top: 6px; }

.filter-row label { display: inline-flex; flex-direction: column; gap: 1px; }

.filter-row input,
.filter-row select {
    padding: 1px 4px;
    border: 1px solid var(--border-dark);
    font: 11px var(--sans);
    background: var(--panel);
}

.filter-row button {
    padding: 2px 12px;
    border: 1px solid var(--border-dark);
    background: linear-gradient(#ffffff, #dcdfe3);
    color: var(--ink);
    font: bold 11px var(--sans);
    cursor: pointer;
}

.filter-row button:hover { border-color: var(--blue); }
.filter-row button:active { background: linear-gradient(#d5d8dc, #e8ebee); }

/* ---------- data table (backlog-style grid) ---------- */

table.data-table,
table.backlog {
    width: 100%;
    border-collapse: collapse;
    font-size: 11px;
    table-layout: fixed;
}

.backlog .col-status, .data-table .col-status { width: 76px; }
.backlog .col-order, .data-table .col-order { width: 84px; }
.backlog .col-part, .data-table .col-part { width: 100px; }
.backlog .col-qty, .data-table .col-qty { width: 44px; }
.backlog .col-ship, .data-table .col-ship { width: 64px; }

.data-table th,
.backlog th {
    position: sticky;
    top: 0;
    background: linear-gradient(#fcfcfc, #e0e4e8);
    text-align: left;
    font: bold 10px var(--sans);
    letter-spacing: 0.04em;
    color: var(--ink-2);
    padding: 3px 6px;
    border-bottom: 1px solid var(--border-dark);
    white-space: nowrap;
}

.data-table th + th,
.backlog th + th { border-left: 1px solid var(--grid-strong); }

.data-table th a, .backlog th a { color: var(--ink-2); text-decoration: none; }
.data-table th a:hover, .backlog th a:hover { color: var(--blue); text-decoration: underline; }

.data-table td,
.backlog td {
    padding: 2px 6px;
    border-bottom: 1px solid var(--grid);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.data-table td + td,
.backlog td + td { border-left: 1px solid #edf0f3; }

.data-table td .chip,
.backlog td .chip { font-size: 9px; padding: 0 3px; }

.data-table td.num, .data-table th.num,
.backlog td.num, .backlog th.num { text-align: right; }

.data-table tbody tr:nth-child(even),
.backlog tbody tr:nth-child(even) { background: #f3f6f9; }

.data-table tbody tr:hover,
.backlog tbody tr:hover { background: #e6eef6; }

.data-table tbody tr.selected,
.backlog tbody tr.selected {
    background: var(--select);
    outline: 1px solid #c9b95e;
    outline-offset: -1px;
}

.data-table .order-link,
.backlog .order-link { font-weight: bold; text-decoration: none; }
.data-table .order-link:hover,
.backlog .order-link:hover { text-decoration: underline; }

.data-table td.empty,
.backlog td.empty {
    padding: 18px;
    text-align: center;
    color: var(--ink-3);
}

/* ---------- pager ---------- */

.pager {
    display: flex;
    gap: 12px;
    align-items: center;
    padding: 5px 8px;
    font-size: 11px;
    border-top: 1px solid var(--border);
    background: var(--panel-tint);
}

.pager .row-count { margin-left: auto; color: var(--ink-3); }

/* ---------- chips (status lozenges) ---------- */

.chip {
    display: inline-block;
    padding: 0 4px;
    border: 1px solid currentColor;
    font: bold 11px var(--mono);
    letter-spacing: 0.04em;
    text-transform: uppercase;
    white-space: nowrap;
    vertical-align: 1px;
}

.chip-late { color: var(--late); background: var(--late-bg); }
.chip-gap { color: var(--gap); background: var(--gap-bg); }
.chip-warn { color: var(--warn); background: var(--warn-bg); }
.chip-good { color: var(--good); background: var(--good-bg); }
.chip-ok { color: var(--ink-3); border-color: var(--border); background: none; }

/* signed schedule slack, left of a date pair: green +3d = early, amber -2d = late.
   The date pair itself takes the same color (sched-*) so the whole group reads at once. */
.slack { font-family: var(--mono); font-weight: bold; }
.slack-ok { color: var(--good); }
.slack-late { color: var(--late); }
.sched-ok { color: var(--good); }
.sched-late { color: var(--late); }

/* ---------- status lamps: [severity flag][supply-chain link][days late] ---------- */

.st-slots {
    display: inline-grid;
    grid-template-columns: 16px 16px 22px;
    gap: 0 4px;
    align-items: center;
    vertical-align: middle;
}

.st-days {
    font: bold 10px var(--mono);
    color: var(--late);
    text-align: right;
}

/* ---------- window panels (every panel is a little "window") ---------- */

.order-map { max-width: 860px; }

/* a window: blue title bar + white body */
.window,
.order-header {
    border: 1px solid var(--border-dark);
    background: var(--panel);
    padding: 0;
    margin-bottom: 4px;
}

.window-title,
.order-title {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
    padding: 3px 8px;
    background: linear-gradient(var(--bar-top), var(--bar-bot));
    color: var(--bar-text);
}

.window-title .ref,
.order-title .ref { font-size: 12px; font-weight: bold; }
.window-title .customer,
.order-title .customer { font-weight: bold; font-size: 11px; }

.window-title .type-tag,
.order-title .type-tag {
    background: var(--panel);
    color: var(--blue);
    border-color: #22456a;
}

.window-actions,
.order-actions { margin-left: auto; display: inline-flex; gap: 4px; align-items: center; }

/* generic body padding for windows that don't manage their own margins */
.window-body { padding: 5px 8px; }

/* raw <pre> content (e.g. plain-text doc viewer) keeps house density and wraps instead of scrolling */
.window-body pre { margin: 3px 0; white-space: pre-wrap; }

/* LOCAL ADDITION to the copied kit — port upstream to gui-design-system.
   A .window used as a native <dialog>. Two UA defaults need re-housing: .window's
   margin-bottom overrides the auto margins a modal centres itself with, and the top
   layer does not inherit body's colour. Three templates carried this as inline style
   with a comment saying the kit ought to own it — layout.html's agent-install dialog,
   machines.html's per-machine config modals, and the shared About box (footer
   fragment). Font and alignment are pinned for the same reason colour is: a dialog's
   markup rides inside whatever fragment carries it (the About box sits in the 10px
   centre-aligned .page-footer strip), and a modal speaks the body voice regardless
   of its host. */
dialog.window {
    margin: auto;
    color: var(--ink);
    font: 11px/1.4 var(--sans);
    text-align: left;
}

/* DESIGN.md §4 promises the backdrop "dims in steel"; neither kit copy ever carried
   the rule (origin app.css lacks it too — flag on the next upstream re-sync).
   --bar-bot at low alpha, not the UA's black. */
dialog.window::backdrop { background: rgba(47, 93, 140, 0.3); }

/* lamp cluster recessed into a title bar */
.lamp-panel {
    display: inline-flex;
    align-items: center;
    height: 20px;
    padding: 0 6px;
    background: linear-gradient(#d6dde4, #f5f8fa 45%, #ffffff);
    border: 1px solid #1c3a5a;
    box-shadow: inset 0 1px 2px rgba(9, 37, 64, 0.35);
}

/* toolbar groove between the lamps and the buttons (dark title-bar variant) */
.window-actions .sep,
.order-actions .sep {
    width: 2px;
    height: 18px;
    border-left: 1px solid #24476c;
    border-right: 1px solid #7ea3c6;
    margin: 0 3px;
}

.btn {
    display: inline-flex;
    align-items: center;
    height: 20px;
    padding: 0 8px;
    border: 1px solid var(--border-dark);
    background: linear-gradient(#ffffff, #dcdfe3);
    font: 11px var(--sans);
    color: var(--ink);
    text-decoration: none;
    cursor: pointer;
}

.btn:hover { border-color: var(--blue); color: var(--blue); }
.btn:active { background: linear-gradient(#d5d8dc, #e8ebee); }

/* LOCAL ADDITION to the copied kit — port upstream to gui-design-system.
   A dead button must read dead (§1.3 every element carries its information, §4
   raised chrome means live), but the UA's :disabled graying loses to .btn's
   explicit ink and gradient, so a disabled .btn rendered pixel-identical to a
   live one. program.html's Send shipped that gap; the strength-gated save
   buttons made it visible. Flat tint, receded ink, no hover invite. */
.btn:disabled, .btn:disabled:hover {
    background: var(--panel-tint);
    border-color: var(--border-dark);
    color: var(--ink-3);
    cursor: default;
}

/* toggle buttons: pressed-in when the feature is on */
.btn.active {
    background: linear-gradient(#cfdded, #e2ecf6);
    border-color: var(--blue);
    box-shadow: inset 0 1px 2px rgba(9, 37, 64, 0.25);
}

.window-sub,
.order-sub {
    margin: 4px 8px 0;
    font-size: 11px;
    color: var(--ink-2);
    display: flex;
    gap: 7px;
    align-items: baseline;
    flex-wrap: wrap;
}

.window-sub:last-child,
.order-sub:last-child { margin-bottom: 5px; }

.verdict {
    margin: 5px 8px 6px;
    padding-top: 5px;
    border-top: 1px dashed var(--border);
    font-size: 11px;
    font-weight: bold;
}

.verdict-ok { color: var(--good); }
.verdict-late { color: var(--late); }
.verdict-short { color: var(--gap); }

/* structured problem list under a window title */
.problem-set {
    position: relative;               /* anchors the top-right help button */
    margin: 6px 8px 7px;
    padding: 1px 8px 5px;
    border: 1px solid var(--border);
    background: var(--panel-tint);
}

/* click-to-open key for every problem condition — a small raised [?] tucked in
   the top-right corner; native <details>, no script */
.cond-help > summary {
    position: absolute;
    /* inside a fieldset, absolute top is measured below the legend band (~13px),
       so a negative top lifts the button onto the legend line, clear of row 1 */
    top: -14px;
    right: 5px;
    list-style: none;
    cursor: help;
    width: 15px;
    height: 15px;
    font: bold 11px var(--sans);
    line-height: 12px;
    text-align: center;
    color: var(--blue);
    background: var(--panel-tint);
    border: 1px solid;
    border-color: #fff var(--border-dark) var(--border-dark) #fff;  /* raised chrome */
    user-select: none;
}
.cond-help > summary::-webkit-details-marker { display: none; }
.cond-help > summary:hover { background: #eef1f4; }
.cond-help[open] > summary {                                        /* pressed when open */
    border-color: var(--border-dark) #fff #fff var(--border-dark);
    background: #dfe3e7;
}
.cond-help-body {
    margin: 2px 0 4px;
    padding: 3px 6px 5px;
    border: 1px solid var(--border);
    background: var(--panel);
}
.cond-help-body .ch-group {
    margin: 4px 0 1px;
    font: bold 10px var(--sans);
    letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--ink-3);
}
.cond-help-body .ch-group:first-child { margin-top: 1px; }
.cond-help-row {
    display: flex;
    align-items: baseline;
    gap: 6px;
    padding: 1px 0;
    font-size: 11px;
    line-height: 1.35;
}
.cond-help-row .p-slot { align-self: flex-start; }
.cond-help-row .ch-name { font-weight: bold; color: var(--ink); }
.cond-help-row .ch-text { color: var(--ink-2); min-width: 0; }
.cond-help-row em { font-style: normal; font-weight: bold; color: var(--gap); }

.problem-set legend {
    font: bold 11px var(--sans);
    padding: 0 4px;
}

.problems-short legend { color: var(--gap); }
.problems-late legend { color: var(--late); }

.problem-list {
    margin: 0;
    padding: 0;
    list-style: none;
}

.problem-list li {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 1px 0;
    font-size: 10.5px;
}

/* fixed-width icon slot so 13px orbs and 16px triangles share one left text edge */
.p-slot {
    flex: none;
    width: 16px;
    height: 16px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

.p-text { min-width: 0; }

.type-tag {
    font: bold 11px var(--mono);
    letter-spacing: 0.06em;
    border: 1px solid var(--blue);
    padding: 0 4px;
    background: var(--blue);
    color: var(--panel);
}

/* famfamfam Silk icons — the full sprite sheet, one request; offsets per icon
   (lookup for new icons: the famfamfam-silk sprite CSS).
   C-clamp = made, cart = bought, package = stock, flags = severity,
   broken link = supply gap, error = data issue */
.node-ico {
    flex: none;
    display: inline-block;
    width: 16px;
    height: 16px;
    margin: 0 2px;
    vertical-align: -4px;
    background: url("../img/silk/famfamfam-silk.png") no-repeat;
}

/* material line header: an asm-style layer cell — the demand is the skeleton,
   its supplies grouped beneath. Status icon + seq box lead it. */
.mtl-cell .ledger { white-space: nowrap; }

/* a line with a supply gap: the coverage fraction itself goes missing-red */
.qty-gap { color: var(--gap); font-weight: bold; }

.mtl-seq {
    flex: none;
    min-width: 16px;
    padding: 0 2px;
    text-align: center;
    font-size: 11px;
    line-height: 13px;
    background: #e9edf0;
    border: 1px solid var(--border);
    color: var(--ink-2);
}

/* compress (C-clamp) = manufactured/job */
.ico-job { background-position: -208px -240px; }
.ico-po { background-position: -208px -192px; }
.ico-stock { background-position: -16px -384px; }
/* bricks (multi-colored stack) = assembly. Material lines carry a STATUS icon
   instead of a structural one: tick = all good, error triangle = runs late,
   red exclamation = supply missing (worst state wins). */
.ico-asm { background-position: -32px -160px; }
.ico-line-ok { background-position: -16px 0; }
.ico-line-late { background-position: -128px -304px; }
.ico-line-miss { background-position: -192px -304px; }
.ico-gap { background-position: -288px -352px; }
.ico-cycle { background-position: -128px -304px; }
.ico-flag-green { background-position: -320px -256px; }
.ico-flag-yellow { background-position: -16px -320px; }
.ico-flag-red { background-position: 0 -320px; }
/* clock = runs late (all late conditions share it) */
.ico-clock { background-position: -224px -224px; }
/* clerical action needed (unfirm / unreleased) — provisional glyph: pencil.
   swap this one background-position to change the icon everywhere. */
.ico-clerical { background-position: -416px -240px; }
/* the truck (lorry) marks a sales order's need-by — it ships to a customer */
.ico-truck { background-position: -368px -112px; }
.ico-so { background-position: -288px -272px; }
/* intact chain link, dim — swaps to a lit broken link when a gap appears */
.ico-link-ok {
    background-position: -256px -352px;
    filter: grayscale(1);
    opacity: 0.3;
}
/* sampo: flex-column page shell so the attribution footer sits on the viewport
   floor on short pages (margin-top:auto on the footer) instead of mid-screen */
body { min-height: 100vh; display: flex; flex-direction: column; }

/* ---- sampo vocabulary (app-side additions, same sprite) ----
   cog = machine · page_gear = CNC program · page_white_text = work instruction
   user = portal user · transfer jobs reuse .ico-truck above (lorry — it delivers) */
.ico-machine { background-position: -240px -160px; }
.ico-program { background-position: -272px -384px; }
.ico-doc { background-position: -384px -400px; }
.ico-download { background-position: -288px -192px; }  /* disk — download the served file */
.ico-original { background-position: -96px -112px; }   /* attach (paperclip) — the uploaded source file */
.ico-current { background-position: -16px 0; }         /* accept — the chain's current revision (same cell as ico-line-ok, distinct
                                                          meaning). Owner's call 7/25/26: the green stays despite §8's always-on-color
                                                          rule — do not "fix" back to the gray chip without asking. */
.ico-user { background-position: -384px -480px; }
.ico-rollback { background-position: -32px -112px; }   /* arrow_undo — roll back (appends a new current rev) */

/* ---------- dependency tree (explorer-style: twisties + solid guides) ---------- */

/* one family for the whole tree: mixing Tahoma and Verdana at 11px reads as
   two different sizes (different glyph metrics) — everything in a cell is data */
.tree-root {
    padding: 4px 0 0 2px;
    font-family: var(--mono);
}

.tnode { position: relative; margin: 4px 0; }

.children { margin-left: 27px; }

/* sibling guide runs down the twisty column and stops at the last child's elbow.
   Solid, not dotted: per-node segments overlap at the seams, and solid lines
   merge invisibly where dotted ones double-print out of phase. */
.children > .tnode::before {
    content: "";
    position: absolute;
    left: 6px;
    top: -5px;
    bottom: -5px;
    border-left: 1px solid #a8b1ba;
}

.children > .tnode:last-child::before {
    bottom: auto;
    height: 14px;
}

/* elbow stub from the guide, through the expand box, to the peg tab */
.children > .tnode::after {
    content: "";
    position: absolute;
    left: 6px;
    top: 9px;
    width: 12px;
    border-top: 1px solid #a8b1ba;
}

.children > .tnode-late::after { border-color: var(--late); }
.children > .tnode-gap::after { border-color: var(--gap); }

/* gutter (twisty) + peg tab + sunken cubby cell */
.cell-row {
    display: flex;
    align-items: flex-start;
    gap: 5px;
}

.cell-stack {
    flex: 1;
    min-width: 0;
}

/* folder-label tab holding the peg (edge) info, attached to the cell's top edge */
.peg-tab {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    position: relative;
    z-index: 1;
    margin: 0 0 -1px;
    padding: 0 6px 1px;
    background: var(--panel-tint);
    border: 1px solid var(--border-dark);
    border-bottom: none;
    font-size: 11px;
    color: var(--ink-2);
}

.peg-tab .mono { font-size: 11px; }
.peg-tab-late { color: var(--late); }
.peg-tab-late .mono { color: var(--late); }
.peg-tab-gap { color: var(--gap); background: var(--gap-bg); }
.peg-tab-gap .mono { color: var(--gap); }

.cell {
    flex: 1;
    min-width: 0;
    background: var(--panel);
    border: 1px solid;
    /* bottom/right stay lighter than top/left for the recessed bevel,
       but dark enough to read as an outline on the white page */
    border-color: var(--border-dark) #aeb6be #aeb6be var(--border-dark);
    box-shadow: inset 1px 1px 2px rgba(20, 40, 60, 0.12);
    padding: 2px 6px 3px;
}

.cell-late { border-left: 3px solid var(--late); }
.cell-unfirm { border-style: dashed; }
.cell-compact { padding: 1px 6px; }

/* a gap keeps the cubby shape — the box where a supply cell should be, gap-tinted */
.cell-gap {
    background: var(--gap-bg);
    border-left: 3px solid var(--gap);
}

.cell-gap .ref,
.cell-gap .trunc,
.cell-gap .mono,
.cell-gap .side-dim,
.cell-gap .trow-detail .mono { color: var(--gap); }

/* pulled-in subassembly layer: part of the job itself, so it reads lighter
   than an independent supply cubby — tinted, flat, no sunken shadow */
.asm-cell {
    background: var(--panel-tint);
    border-color: var(--border);
    box-shadow: none;
    padding: 1px 6px 2px;
}

/* flat mode: assembly layers vanish; their materials sit directly under the job */
.order-map.flat-asm .asm-tnode,
.order-map.flat-asm .asm-tnode > .node-details,
.order-map.flat-asm .asm-children { display: contents; }
.order-map.flat-asm .asm-tnode::before,
.order-map.flat-asm .asm-tnode::after { display: none; }
.order-map.flat-asm .asm-tnode > .node-details > summary,
.order-map.flat-asm .asm-tnode > .cell-row { display: none; }

.trow {
    display: flex;
    align-items: center;
    gap: 6px;
    flex-wrap: wrap;
}

.trow .ref { font-weight: bold; }

.trow .ledger {
    font-family: var(--mono);
    font-size: 11px;
    color: var(--ink-2);
}

.trow-late .ledger { color: var(--late); }

/* windows-explorer expand box, sitting on the connector line at peg-tab level */
.twisty {
    flex: none;
    position: relative;
    z-index: 1;   /* always above the tree guides crossing beneath it */
    width: 11px;
    height: 11px;
    margin-top: 4px;
    border: 1px solid var(--border-dark);
    background: linear-gradient(#ffffff, #e4e7ea);
    font: bold 9px/9px var(--mono);
    text-align: center;
    color: var(--ink);
}

.twisty.spacer { visibility: hidden; }

details.node-details > summary .twisty::before { content: "−"; }
details.node-details:not([open]) > summary .twisty::before { content: "+"; }

.trow-detail {
    /* 2px icon margin + 16px icon + 2px margin + 6px flex gap = text starts at 26px */
    margin: 1px 0 0 26px;
    font-size: 11px;
    color: var(--ink-2);
    display: flex;
    gap: 7px;
    align-items: baseline;
    flex-wrap: wrap;
}

.trow-detail .mono { color: var(--ink); }
.trow-detail .ref { font-weight: bold; }

/* full-height groove divider between the part zone and the supply-source panel */
.vsep {
    flex: none;
    width: 0;
    align-self: stretch;
    border-left: 1px solid #c2c8ce;
    border-right: 1px solid #fff;
    margin: 1px 0;
}

/* two zones side by side: part + quantities left, supply source right */
.cell-cols {
    display: flex;
    align-items: stretch;
    gap: 8px;
}

/* fixed-width part zone so every side panel starts at the same offset,
   shrinkable when the cell itself is narrower (split view) */
.cell-main { flex: 0 1 440px; min-width: 0; }

.cell-side {
    flex: none;
    display: flex;
    gap: 14px;
    align-items: flex-start;
    padding: 1px 2px 1px 0;
}

.side-col { display: flex; flex-direction: column; line-height: 1.4; white-space: nowrap; }
.side-col .ref { font-weight: bold; }
.side-col .trunc { max-width: 150px; }
.side-lbl { color: var(--ink-3); }
.side-dim { color: var(--ink-2); }

.also-feeds { color: var(--ink-3); font-size: 11px; font-family: var(--mono); }

.jump { font-size: 11px; color: var(--ink-3); margin-left: auto; }

/* ---------- details / collapse ---------- */

details.node-details > summary {
    list-style: none;
    cursor: pointer;
}

details.node-details > summary::-webkit-details-marker { display: none; }

details.node-details > summary:focus-visible {
    outline: 1px dotted var(--ink);
    outline-offset: 1px;
}

.sub-summary {
    display: none;
    font-size: 11px;
    color: var(--ink-3);
    border: 1px solid var(--border);
    background: var(--panel-tint);
    padding: 0 5px;
    white-space: nowrap;
}

details.node-details:not([open]) .sub-summary { display: inline-block; }

/* ---------- placeholders ---------- */

.map-placeholder {
    color: var(--ink-3);
    padding: 24px 14px;
    max-width: 420px;
    font-size: 11px;
}

.map-placeholder .hint { font-size: 11px; }

.no-snapshot { padding: 24px; color: var(--ink-2); }

.detail-page { padding: 8px 14px; }

.backlink { font-size: 11px; margin: 2px 0 6px; }
.backlink a { color: var(--ink-3); text-decoration: none; }
.backlink a:hover { color: var(--blue); text-decoration: underline; }

/* ---------- part chain (value-stream strips) ---------- */

/* any part number is a door into its chain: quiet dotted underline, blue on hover */
.part-link {
    color: inherit;
    text-decoration: none;
    border-bottom: 1px dotted #9aa4ad;
}

.part-link:hover { color: var(--link); border-bottom: 1px solid currentColor; }

/* "you are here": the viewed part, classic selection yellow wherever it appears */
.part-here { background: var(--select); outline: 1px solid #c9b95e; padding: 0 2px; }

/* the focus card of a job/PO map takes the same selection yellow */
.card-focus { background: var(--select); }

.chain-board {
    overflow-x: auto;
    background: var(--panel);
    border: 1px solid var(--border-dark);
    padding: 0 6px 0 0;
}

/* every column has a hard width — feeder cards 194, arrows 172, cards 286 — so
   depth N starts at the same x in every row and the board reads as a table */

/* column header, styled like the data table's th strip */
.chain-cols {
    display: flex;
    width: max-content;
    min-width: 100%;
    background: linear-gradient(#fcfcfc, #e0e4e8);
    border-bottom: 1px solid var(--border-dark);
    font: bold 10px var(--sans);
    letter-spacing: 0.04em;
    color: var(--ink-2);
    padding: 3px 0;
}

.cc-supply { flex: none; width: 202px; padding-left: 8px; }
.cc-part { flex: none; width: 188px; text-align: center; }
.cc-rest { flex: 1; padding-left: 4px; white-space: nowrap; }

/* one demand line for the part: feeders → the part → its consumer tree */
.strip {
    display: flex;
    align-items: center;
    width: max-content;
    min-width: 100%;
    padding: 8px 2px 8px 8px;
    border-bottom: 1px solid var(--grid);
}

.strip:last-of-type { border-bottom: none; }

/* the feeder side mirrors the fork side: one full peg arrow per feeder,
   converging on a vertical bus at the rail's right edge; a single inlet
   stub (with the one arrowhead) enters the consumer */
.feeder-rail {
    flex: none;
    display: flex;
    flex-direction: column;
    gap: 10px;
    position: relative;
    margin-right: 16px;
}

/* inlet stub off the bus into the consumer's midline — ├── */
.feeder-rail::after {
    content: "";
    position: absolute;
    right: -16px;
    top: 50%;
    width: 16px;
    margin-top: -1px;
    border-top: 2px solid var(--border-dark);
}

/* the inlet's arrowhead, tip on the consumer card's edge */
.feeder-rail::before {
    content: "";
    position: absolute;
    right: -16px;
    top: 50%;
    margin-top: -4px;
    border: 4px solid transparent;
    border-left: 7px solid var(--border-dark);
    z-index: 1;
}

.frow {
    display: flex;
    align-items: center;
    position: relative;
}

/* convergence bus, same clipping rules as the fork bus */
.frow::after {
    content: "";
    position: absolute;
    right: 0;
    top: -6px;
    bottom: -6px;
    border-right: 2px solid var(--border-dark);
}

.frow:first-child::after { top: 50%; }
.frow:last-child::after { bottom: 50%; }
.frow:only-child::after { display: none; }

/* tributary arrows run headless into the bus; the inlet carries the head.
   A lone feeder skips all of that — its own arrow runs unbroken (one
   element, cannot kink) straight into the consumer */
.frow:not(:only-child) .flow-line::after { display: none; }
.feeder-rail:has(> .frow:only-child)::after,
.feeder-rail:has(> .frow:only-child)::before { display: none; }
.frow:only-child .flow-line { margin-right: -16px; }

/* consumer cards — jobs and SOs — share one width so the columns read as one */
.chain-card { flex: none; width: 286px; }
.chain-card .trow-detail { margin-left: 4px; }
.feeder-card { width: 194px; }

.so-card .so-cust { font-weight: bold; max-width: 268px; }

/* terminal markers: dead ends, cycles, depth cap — present but muted */
.card-end {
    width: auto;
    max-width: 200px;
    border-style: dashed;
    background: var(--panel-tint);
    box-shadow: none;
    color: var(--ink-3);
}

.card-end .ref { color: var(--ink-2); }

/* a node in the consumer tree: [arrow in][card][children]; children stack
   vertically and the card centers against them, so a one-to-many peg reads
   as a fork */
.cnode {
    display: flex;
    align-items: center;
    position: relative;
}

.ckids {
    flex: none;
    display: flex;
    flex-direction: column;
    gap: 10px;           /* vertical air between branch arrows' date labels */
    position: relative;
    margin-left: 16px;   /* breathing room between the card and its fan-out */
}

/* the joint off the card: a stub from the card's midline into the bus — ──┤ */
.ckids::before {
    content: "";
    position: absolute;
    left: -16px;
    top: 50%;
    width: 18px;         /* overlaps the bus so the corner never shows a seam */
    margin-top: -1px;
    border-top: 2px solid var(--border-dark);
}

/* fork bus: each child owns a vertical segment at the fork point; first and
   last clip to their own middles */
.ckids > .cnode::before {
    content: "";
    position: absolute;
    left: 0;
    top: -6px;
    bottom: -6px;
    border-left: 2px solid var(--border-dark);
}

.ckids > .cnode:first-child::before { top: 50%; }
.ckids > .cnode:last-child::before { bottom: 50%; }
.ckids > .cnode:only-child::before { display: none; }

/* single next hop: no bus, no stub — the arrow line itself runs the whole
   way from the card, one element, so the joint cannot kink */
.ckids:has(> .cnode:only-child)::before { display: none; }
.ckids > .cnode:only-child > .flow .flow-line { margin-left: -16px; }

/* the arrow between cards: what flows, how much, and its schedule.
   Wide enough that a worst-case date pair ("+10d 10/24/26 → 10/28/26")
   keeps air on both sides */
.flow {
    flex: none;
    display: flex;
    flex-direction: column;
    align-items: center;
    width: 172px;
    font-size: 11px;
}

.flow-lbl { white-space: nowrap; color: var(--ink); padding: 0 6px; }

.flow-line {
    align-self: stretch;
    height: 0;
    border-top: 2px solid var(--border-dark);
    position: relative;
    margin: 3px 0 2px;
}

.flow-line::after {
    content: "";
    position: absolute;
    right: -1px;
    /* abs offsets anchor to the padding box, which sits BELOW the 2px
       border-top that draws the line — so centering the 8px triangle on
       the line needs -5px, not -3px */
    top: -5px;
    border: 4px solid transparent;
    border-left: 7px solid var(--border-dark);
}

.flow-late .flow-line { border-color: var(--late); }
.flow-late .flow-line::after { border-left-color: var(--late); }
.flow-gap .flow-line { border-color: var(--gap); }
.flow-gap .flow-line::after { border-left-color: var(--gap); }
.flow-gap .flow-sub { color: var(--gap); }

.flow-sub { white-space: nowrap; color: var(--ink-3); padding: 0 6px; }

.chain-more { padding: 5px 8px 7px; color: var(--ink-3); }

/* ---------- page footer ---------- */

/* LOCAL ADDITION to the copied kit — port upstream to gui-design-system.
   Was a wall of inline style on layout.html's footer fragment, which the styling
   convention forbids outright and which no page could override (inline beats any
   selector). As a class it is themeable per page — see .signon-page below. */
.page-footer {
    margin-top: auto;
    text-align: center;
    padding: 3px 14px;
    border-top: 1px solid #c2c8ce;
    box-shadow: inset 0 1px 0 #fff;
    font: 10px var(--sans);
    color: var(--ink-3);
}

/* links and the About-box opener share the strip's quiet voice (the .backlink recipe:
   inherit the whisper, blue underline on hover). The button variant sheds .btn chrome —
   a raised 20px button would shout in a 10px strip — but keeps the pointer: it is a
   control (showModal), not a link. */
.page-footer a,
.page-footer button {
    font: inherit;
    color: inherit;
    text-decoration: none;
}

.page-footer button {
    padding: 0;
    border: none;
    background: none;
    cursor: pointer;
}

.page-footer a:hover,
.page-footer button:hover { color: var(--blue); text-decoration: underline; }

/* ---------- notice block ---------- */

/* LOCAL ADDITION to the copied kit — port upstream to gui-design-system.
   A centred, bordered message with a bold headline and an explanatory line, for
   pages that have no data yet and so no row to tint. .window-sub already covers
   an inline flash ON a populated page; this is the standalone one. Colour follows
   the kit's exception-only rule (DESIGN.md §3): the DEFAULT is neutral — news is
   not a state, and a screen where everything is fine is almost colorless — with
   warn-amber (soft caution) and gap-red (failure) as the exception variants. */
.notice {
    border: 1px solid var(--border);
    background: var(--panel-tint);
    padding: 6px 12px;
    text-align: center;
}

.notice strong {
    display: block;
    font-size: 12px;
    color: var(--ink);
}

.notice span {
    font-size: 11px;
    color: var(--ink-2);
}

.notice-warn {
    border-color: #c9b95e;
    background: var(--warn-bg);
}

.notice-warn strong { color: var(--warn); }

.notice-gap {
    border-color: #d59a9a;
    background: var(--gap-bg);
}

.notice-gap strong { color: var(--gap); }

/* ---------- property grid (right-aligned label : field) ---------- */

/* LOCAL ADDITION to the copied kit — port upstream to gui-design-system.
   The classic admin-console form: labels right-aligned against a single field
   column, so the colons line up and the eye tracks one edge. .filter-row is the
   kit's other form shape and stays the right one for a toolbar of controls laid
   out left-to-right; this is for a short stack of named fields.
   Fields are capped rather than 1fr so the grid stays a compact block on a wide
   page instead of flinging the inputs out to the far margin. */
.prop-grid {
    display: grid;
    grid-template-columns: auto minmax(0, 200px);
    gap: 5px 10px;
    align-items: center;
    justify-content: start;
}

.prop-grid > label {
    justify-self: end;
    font-weight: bold;
    color: var(--ink-2);
    white-space: nowrap;
}

.prop-grid input,
.prop-grid select {
    width: 100%;
    padding: 1px 4px;
    border: 1px solid var(--border-dark);
    font: 11px var(--sans);
    background: var(--panel);
}

/* a row that occupies the field column only — the submit button under the fields */
.prop-grid .prop-field { grid-column: 2; }

/* ---------- sign-on splash ---------- */

/* LOCAL ADDITION to the copied kit — port upstream to gui-design-system.
   The 2009 Java-console sign-on: chrome band, gradient brand panel, white form
   pane, chrome footer, the lot inside one framed window. Framed rather than
   full-bleed because a ~500px form pinned to the corners of a 1080p monitor
   makes the eye cross the whole screen to find it; the console this apes shipped
   in a window about this size, and the frame is what keeps the composition
   together. */
.signon-page {
    padding: 20px;
    /* the flat --chrome fill read as dead space behind a floating window. A shallow
       grey-to-grey-blue fall gives the frame something to sit on and pushes the white
       pane forward. Fixed attachment so the ramp is sized to the viewport rather than
       to the body box, which is only as tall as its content. */
    background: linear-gradient(#d7dde5, #94a3b7);
    background-attachment: fixed;
}

.signon-frame {
    /* width:100% is load-bearing, not decoration. margin:auto centres this in the
       column-flex body, and an auto margin in the cross axis CANCELS the stretch —
       so width:auto would shrink-wrap the frame to its widest line of text. That
       exact trap is what made the old login box render 187px wide; see
       docs/DECISIONS.md before touching either declaration. */
    width: 100%;
    max-width: 780px;
    margin: auto;
    min-height: 470px;
    max-height: calc(100vh - 40px);
    display: flex;
    flex-direction: column;
    border: 1px solid var(--border-dark);
    box-shadow: 0 2px 6px rgba(20, 50, 80, 0.18);
}

.signon {
    flex: 1 1 auto;
    display: grid;
    grid-template-columns: minmax(180px, 34%) 1fr;
    min-height: 0;
}

.signon-brand {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 6px;
    /* top padding deliberately equals .signon-pane's, so the wordmark and the form
       title start on the same line and read as one band. Centring the mark in the
       panel instead only looks right at the ~540px window the console shipped in —
       on a 16:9 monitor it sinks to the floor while the form stays at the top. */
    padding: 46px 16px 24px;
    text-align: center;
    border-right: 1px solid var(--border-dark);
    /* deep at the top fading to haze at the floor, like the console's logo panel */
    background: linear-gradient(var(--bar-top) 0%, #7e9dbe 45%, #c6d0da 100%);
}

.signon-mark {
    font: bold 32px var(--sans);
    letter-spacing: 0.16em;
    color: #fff;
    text-shadow: 0 2px 3px rgba(20, 50, 80, 0.5);
}

.signon-tag {
    font-size: 10px;
    letter-spacing: 0.08em;
    color: #eaf2fa;
    text-shadow: 0 1px 2px rgba(20, 50, 80, 0.45);
    /* the brand column is narrow enough that a tagline of any length wraps; balance
       splits it into even lines instead of stranding one word on the second */
    text-wrap: balance;
}

.signon-pane {
    background: var(--panel);
    padding: 46px 34px;
    overflow-y: auto;
}

/* the form column keeps a readable measure instead of running to the far edge */
.signon-form {
    width: 100%;
    max-width: 380px;
    display: flex;
    flex-direction: column;
    gap: 12px;
}

.signon-title {
    margin: 0;
    font: 19px var(--sans);
    color: var(--ink-2);
}

.signon-title strong { color: var(--ink); }

/* the sign-on page is bookended by chrome, so the attribution strip goes dark to
   match the topbar. Scoped to the page: every other page keeps the light strip. */
.signon-page .page-footer {
    background: linear-gradient(var(--bar-top), var(--bar-bot));
    border-top: 1px solid #22456a;
    box-shadow: none;
    color: #d3e2f1;
    text-align: left;
}

/* on the dark strip the quiet links inherit the pale chrome; hover lifts to white —
   kit blue is unreadable on the bar gradient */
.signon-page .page-footer a:hover,
.signon-page .page-footer button:hover { color: var(--bar-text); }

/* the sign-on page states the licence once (DESIGN.md §8, one owner per fact): its own
   no-JS credit (.signon-legal, in the form pane) owns it, so the shared footer's
   noscript fallback stays hidden here */
.signon-page .page-footer noscript { display: none; }

/* no-JS fallback line for the About box's icon credit — see login.html */
.signon-legal { font-size: 10px; color: var(--ink-3); }

@media (max-width: 700px) {
    /* a frame inside a frame is wasted space on a phone — drop back to full-bleed */
    .signon-page { padding: 0; }
    .signon-frame {
        max-width: none;
        max-height: none;
        min-height: 0;
        margin: 0;
        flex: 1 1 auto;
        border: none;
        box-shadow: none;
    }
    /* rows pinned auto/1fr on purpose: a single-column grid stretches its auto rows to
       share the leftover height, which handed the brand panel a third of a phone screen */
    .signon { grid-template-columns: 1fr; grid-template-rows: auto 1fr; }
    .signon-brand { padding: 14px; border-right: none; border-bottom: 1px solid var(--border-dark); }
    .signon-mark { font-size: 22px; }
    .signon-pane { padding: 20px 16px; }
}

/* ---------- print ---------- */

@media print {
    .topbar, .pane-list, .window-actions, .order-actions, .backlink { display: none !important; }
    .split { display: block; height: auto; }
    .pane-map { overflow: visible; padding: 0; }
    body { background: #fff; }
    .window-title, .order-title { color: #000; background: #fff; border-bottom: 2px solid #000; }
    .tnode, .window, .order-header { break-inside: avoid; }
    * { print-color-adjust: exact; -webkit-print-color-adjust: exact; }
}
