/* awaresmith house kit — the CSS half of the front-end kit (2026-09-14).
   ONE file, shipped with the kit JS to every consumer by awaresmith-infra/kit-sync.sh
   (see js/awaresmith/kit.manifest). Change it in the GATEWAY, then sync — never
   edit a consumer's copy. App-specific rules stay in each app's site.css.

   Load order: kit.css BEFORE site.css, and before the _Layout white-label
   :root override, so a brand row still wins on the tokens below.

   Lists that can grow go in .as-scroll-card. */

/* ---- Brand palette (BRAND.md, approved 2026-08-26) ------------------------
   Two blues: darkblue #00008B for headings, #007EDE as the highlight (the
   logo blue). darkred is RETIRED. #007EDE is 4.17:1 on white — fine for the
   mark and large type, UNDER 4.5:1 for small text — so small text, links and
   button fills on light grounds use the ink stop #0063B1 (6.14:1), and dark
   grounds use #4FA8F0 (7.44:1). Every ratio below was computed, not assumed.
   WHITE LABEL: these five tokens are the ONLY place a colour is written; a
   white label recolours the site by overriding tokens, not CSS. Never write a
   brand hex below this block — read the token. */
:root {
    --as-heading: #00008B;   /* 15.30:1 on white */
    --as-hi: #007EDE;        /*  4.17:1 on white — large/marks/borders only */
    --as-ink: #0063B1;       /*  6.14:1 on white; white-on-it 6.14:1 */
    --as-ink-hover: #00518F; /*  white-on-it 8.16:1 */
    --as-hi-bright: #4FA8F0; /*  7.44:1 on near-black */
    /* Bootstrap's default link/primary #0d6efd is only 3.68:1 on white —
       FAILS 4.5:1 for the small text it is used on. Re-pointed to the ink. */
    --bs-link-color: var(--as-ink);
    --bs-link-hover-color: var(--as-ink-hover);
    --bs-link-color-rgb: 0, 99, 177;
    --bs-link-hover-color-rgb: 0, 81, 143;
}
.btn-primary {
    --bs-btn-bg: var(--as-ink); --bs-btn-border-color: var(--as-ink);
    --bs-btn-hover-bg: var(--as-ink-hover); --bs-btn-hover-border-color: var(--as-ink-hover);
    --bs-btn-active-bg: var(--as-ink-hover); --bs-btn-active-border-color: var(--as-ink-hover);
    --bs-btn-disabled-bg: var(--as-ink); --bs-btn-disabled-border-color: var(--as-ink);
}
.btn-outline-primary {
    --bs-btn-color: var(--as-ink); --bs-btn-border-color: var(--as-ink);
    --bs-btn-hover-bg: var(--as-ink); --bs-btn-hover-border-color: var(--as-ink);
    --bs-btn-active-bg: var(--as-ink-hover); --bs-btn-active-border-color: var(--as-ink-hover);
}
.text-bg-primary { background-color: var(--as-ink) !important; }

/* ---- Slide-out panel (js/awaresmith/panel.js) -----------------------------
   Standard (Rafe, 2026-08-13): EVERY offcanvas panel is 2/3 of the viewport on
   medium+ screens and full-width on small handhelds. !important beats
   Bootstrap's fixed --bs-offcanvas-width (400px). */
.offcanvas.as-panel { width: 100vw !important; }
@media (min-width: 768px) {
    .offcanvas.as-panel { width: 66.666vw !important; }
}
/* Grid tables scroll inside their own box; the page never scrolls sideways. */
.table-scroll { overflow-x: auto; }
.table-scroll > table { min-width: max-content; width: 100%; }

/* ---- Confirm / prompt / form toast (js/awaresmith/confirm-toast.js) ----------
   Middle-LEFT, because the drags it confirms end over the folder tree on the
   left. Site-wide rather than page-scoped: a second copy would drift. */
.as-confirm-host {
    position: fixed; left: 0; top: 50%; transform: translateY(-50%);
    z-index: 1080; pointer-events: none;
}
.as-confirm {
    pointer-events: auto;
    margin-left: .75rem; max-width: 24rem;
    background: var(--bs-body-bg, #fff);
    border: 2px solid var(--as-heading); border-left-width: 6px; border-radius: 10px;
    box-shadow: 0 .75rem 2rem rgba(0, 0, 0, .22);
    padding: .75rem .9rem;
    opacity: 0; transform: translateX(-1rem);
    transition: opacity .12s ease, transform .12s ease;
}
.as-confirm.in { opacity: 1; transform: translateX(0); }
.as-confirm.danger { border-color: #dc3545; }
.as-confirm.warning { border-color: #ffc107; }   /* Bootstrap warning: the one sanctioned yellow */
.as-confirm-title { font-weight: 600; color: var(--as-heading); margin-bottom: .35rem; }
.as-confirm.danger .as-confirm-title { color: #B02A37; }   /* 6.50:1 on white */
.as-confirm-line { font-size: .85rem; margin-bottom: .2rem; }
.as-confirm-actions { display: flex; gap: .4rem; margin-top: .6rem; }
.as-confirm input { margin-top: .35rem; }
.as-confirm-error:empty { display: none; }
.as-confirm-error { color: #B02A37; font-size: .8rem; margin-top: .3rem; }   /* 6.50:1 */
/* formToast: several fields need more room than the one-line prompt, but the
   card must still fit a phone — cap at viewport width minus its margins. */
.as-confirm.wide { max-width: min(30rem, calc(100vw - 2rem)); }
.as-confirm.wide textarea, .as-confirm.wide select { margin-top: .35rem; }
.as-confirm-label { display: block; font-size: .8rem; font-weight: 600; margin: .5rem 0 0; }
@media (prefers-reduced-motion: reduce) {
    .as-confirm { transition: none; }
}

/* ---- Help toggles: [?] / [ISO?] on panel labels and section headers ---------
   (Rafe, 2026-08-27) The pill stays a glanceable clause reference; the words
   arrive on click. Same affordance as dom.js helpToggle, in the panel template. */
.as-iso-toggle { cursor: pointer; }
.as-iso-toggle .as-iso-mark { color: var(--as-ink); font-weight: 600; }
/* Registry intent paraphrase keeps its paragraph breaks in the help reveal. */
.as-clause-intent { white-space: pre-line; }

/* ---- Children widget (panel.js #renderChildren, children.js) ---------------
   The STANDARD list-view navigation (Rafe, 2026-09-11): every panel ends with
   the record's to-many lists — two columns, ≈5 rows tall then scroll, striped,
   each row a link that lands on the target grid filtered to that key. */
.as-children-group { border: 1px solid var(--bs-border-color); border-radius: .375rem; overflow: hidden; }
.as-children-title { display: flex; align-items: center; font-size: .8rem; font-weight: 600; color: var(--as-heading);
                     padding: .3rem .5rem; background: var(--bs-tertiary-bg); }
.as-children-list { max-height: 8.6rem; overflow-y: auto; }
.as-children-row { display: flex; gap: .5rem; align-items: baseline; padding: .25rem .5rem; font-size: .85rem;
                   line-height: 1.25rem; text-decoration: none; color: inherit; }
.as-children-row:nth-child(even) { background: #f3f4f6; }
.as-children-row:hover { background: #e7f1fc; color: var(--as-ink); }
.as-children-row code { color: var(--as-ink); flex: 0 0 auto; }
.as-children-text { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.as-children-empty { padding: .3rem .5rem; }
/* A grid filter that arrived by link (?filter=&value=) is boxed bright yellow —
   header and filter cell — until the person edits that filter themselves. */
th.as-grid-linked { outline: 3px solid #FFD600; outline-offset: -3px; }

/* ---- Scroll card: every list that can grow -----------------------------------
   House rule (Rafe, 2026-10-13): "Histories and items that list must be in a
   scrollable container (card) that has fixed height, otherwise the whole page
   gets infinitely long." The card holds the rows only. The section heading and
   the forms and buttons that add to the list stay outside it, so they never
   scroll away. Works in a panel column and on a full page: it is as wide as its
   parent and as tall as its rows, up to about 5 rows ("Set the max height for
   the list views to about 5 rows"). dom.js fitRows(card) measures the fifth
   row, since rows run one line or two (--as-scroll-rows); a card without it
   stops at --as-scroll-max.
   The shade at an edge says "more this way". Two covers in the card's own colour
   ride with the rows (local); two shades stay on the edges (scroll). At the
   top the top cover hides the top shade, at the end the bottom one, and a card
   that doesn't overflow hides both. Rows inside stay see-through so the shade
   shows. An empty card (nothing loaded) takes no room. */
.as-scroll-card {
    --as-scroll-bg: var(--bs-body-bg, #fff);
    --as-scroll-shade: rgba(0, 0, 0, .13);
    --as-scroll-rows: 5;        /* read by dom.js fitRows */
    --as-scroll-max: 16rem;     /* the cap without fitRows: about five two-line rows */
    max-height: var(--as-scroll-max); overflow-y: auto; min-width: 0;
    padding: .375rem .75rem;
    border: 1px solid var(--bs-border-color, #dee2e6); border-radius: var(--bs-border-radius, .375rem);
    overflow-wrap: break-word;
    background:
        linear-gradient(var(--as-scroll-bg) 30%, transparent) top / 100% 1.75rem no-repeat local,
        linear-gradient(transparent, var(--as-scroll-bg) 70%) bottom / 100% 1.75rem no-repeat local,
        radial-gradient(farthest-side at 50% 0, var(--as-scroll-shade), transparent) top / 100% .7rem no-repeat scroll,
        radial-gradient(farthest-side at 50% 100%, var(--as-scroll-shade), transparent) bottom / 100% .7rem no-repeat scroll,
        var(--as-scroll-bg);
}
.as-scroll-card:empty { display: none; }
/* Bootstrap sets the list-group colour on .list-group itself, so it is cleared there. */
.as-scroll-card .list-group { --bs-list-group-bg: transparent; }
/* The card's border closes the list: the last row needs no rule of its own. */
.as-scroll-card > .border-bottom:last-child { border-bottom: 0 !important; }
.as-scroll-card:focus-visible { outline: 2px solid var(--as-hi); outline-offset: 2px; }
/* "· Show all (73)" after a list's label (dom.js fitRows, past 50 rows): plain weight in a bold heading. */
.as-show-all { font-weight: 400; font-size: .875em; white-space: nowrap; }

/* ---- Collapsible card --------------------------------------------------------
   (Rafe, 2026-10-13: "Make the cards collapsible, but open by default.") A
   Bootstrap .card whose .card-header holds, in order: button.as-card-toggle
   (span.as-card-title, then an optional span.as-card-note, read-only words
   that belong to the heading), any .as-card-control (a copy button: it works
   on its own), and i.bi.as-card-chevron (bi-chevron-up open, bi-chevron-down
   folded) at the right. The button carries aria-expanded and aria-controls
   (the body's id), so Enter and Space work and a reader hears the state; its
   ::after covers the heading, so a click anywhere on it folds the card. Folding
   sets `hidden` on the .card-body and .collapsed on the card: the body's
   controls keep their values. Same header look as the form-page cards. First
   user: the documents panel (doc-settings.js docCard). */
.as-card-collapsible { border-color: var(--bs-border-color); }
.as-card-collapsible > .card-header {
    position: relative; display: flex; align-items: center; gap: .5rem;
    margin: 0; padding: .55rem 1rem; background: #f3f7fc; font-size: .95rem; line-height: 1.5;
}
.as-card-collapsible > .card-header:hover { background: #e7f1fc; }
.as-card-toggle {
    display: inline-block; min-width: 0;   /* inline text: "Settings for ID: 24" keeps its space */
    padding: 0; border: 0; background: none;
    color: var(--as-heading); font: inherit; font-weight: 600; text-align: left;
}
.as-card-toggle::after { content: ''; position: absolute; inset: 0; }
.as-card-toggle:focus-visible { outline: 0; }
.as-card-toggle:focus-visible::after { outline: 2px solid var(--as-ink); outline-offset: -2px; border-radius: inherit; }
.as-card-note { font-weight: 400; color: var(--as-ink); }
.as-card-note:empty { display: none; }
.as-card-collapsible > .card-header > .as-card-control { position: relative; z-index: 1; }
.as-card-chevron { margin-left: auto; padding-left: 1rem; color: var(--as-ink); }
.as-card-collapsible.collapsed > .card-header { border-bottom: 0; border-radius: var(--bs-card-inner-border-radius); }

/* ---- Form pages (server-rendered MVC forms) ---------------------------------
   Every MVC form is a stack of cards inside .as-form-page; fields sit on a
   Bootstrap .row g-3 with col-12 col-md-6 (col-lg-4 for short fields) so they
   reflow to one column on narrow screens. Actions live in .as-form-actions at
   the bottom. Same look as the panel. */
.as-form-page { max-width: 1100px; }
.as-form-page .card { border-color: var(--bs-border-color); margin-bottom: 1rem; }
.as-form-page .card-header { background: #f3f7fc; color: var(--as-heading); font-weight: 600; font-size: .95rem; }
.as-form-page .form-label { font-size: .85rem; font-weight: 600; margin-bottom: .2rem; }
.as-form-page .form-text { font-size: .78rem; }
.as-form-page legend { font-size: .85rem; font-weight: 600; color: var(--as-heading); }
.as-form-actions { display: flex; flex-wrap: wrap; gap: .5rem; align-items: center; padding: .75rem 0 0; }
.as-form-actions .btn-primary { font-weight: 600; }
.as-check-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(14rem, 1fr)); gap: .35rem .75rem; }
.as-check-grid .form-check { margin: 0; }
/* Status pills (enum name = class). Apps add their own enum members in site.css. */
.status { display: inline-block; font-size: .75rem; font-weight: 600; padding: .15rem .5rem; border-radius: 999px;
          background: #e9ecef; color: #333; vertical-align: middle; }
.status.Pending { background: #fff3cd; color: #664d03; }
.status.Purchasing, .status.Purchased, .status.Provisioned, .status.Deploying { background: #cfe2ff; color: var(--as-ink); }
.status.Deployed { background: #d1e7dd; color: #0f5132; }
.status.Failed { background: #f8d7da; color: #842029; }
/* Login card (Identity override pages). */
.as-login { max-width: 26rem; margin: 3rem auto; }
.as-login .card-header { background: var(--as-heading); color: #fff; font-weight: 600; }

/* ---- check-tree (js/awaresmith/check-tree.js) — lazy tree, three-state boxes ---- */
.ctree { font-size: .9rem; }
.ctree-row { display: flex; align-items: center; gap: .4rem; padding: .2rem .4rem;
    padding-left: calc(.4rem + var(--ctree-level, 0) * 1.25rem); border-radius: 6px; cursor: default; }
.ctree-row:hover { background: #f3f6fb; }
.ctree-row:focus-visible { outline: 2px solid var(--as-hi, #007EDE); outline-offset: -2px; }
.ctree-caret { border: 0; background: none; padding: 0; width: 1.1rem; line-height: 1; color: #6c757d; }
.ctree-caret:disabled { visibility: hidden; }
.ctree-box { margin: 0; flex: 0 0 auto; }
.ctree-row > .bi-folder, .ctree-row > .bi-folder2 { color: var(--as-ink, #0063B1); }
.ctree-name { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ctree-meta { margin-left: auto; color: #6c757d; font-size: .8rem; white-space: nowrap; padding-left: .75rem; }
.ctree-status { color: #6c757d; font-size: .85rem; padding: .25rem .4rem .25rem 2rem; }

/* ---- Typing combo (js/awaresmith/dom.js combo) -------------------------------
   It reads as a picker (board #147, 2026-10-05): form-select's caret on the box,
   and a list of every option when it opens. The list hangs off <body>
   (position: fixed, placed by the script), above panels, toasts and the form toast. */
.as-combo-input {
    padding-right: 2rem;
    background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e");
    background-repeat: no-repeat; background-position: right .6rem center; background-size: 14px 10px;
    cursor: pointer;
}
.as-combo-input:focus { cursor: text; }
.as-combo-input:disabled { cursor: default; }
.as-combo-list {
    position: fixed; z-index: 1095; margin: 0; padding: .25rem 0; list-style: none;
    max-height: 15rem; max-width: calc(100vw - 8px); overflow-y: auto;
    background: var(--bs-body-bg, #fff); border: 1px solid var(--bs-border-color, #dee2e6);
    border-radius: .375rem; box-shadow: 0 .5rem 1rem rgba(0, 0, 0, .15); font-size: .875rem;
}
.as-combo-option { padding: .3rem .75rem; cursor: pointer; }
.as-combo-option[aria-selected="true"] { font-weight: 600; }
.as-combo-option.active { background: #e7f1fc; color: var(--as-ink); }
.as-combo-empty { padding: .3rem .75rem; color: #6c757d; }

/* ---- A path inside a toast (js/awaresmith/alerts.js, { path } parts) ----------
   One piece, so "C:\RestoreTest" never breaks after "C:" (laptop #151,
   2026-10-05). A path longer than the whole line still wraps inside its box
   instead of running out of the toast. */
.as-path { display: inline-block; max-width: 100%; overflow-wrap: anywhere; }

/* "You don't have a seat" (seat-notice.js, _Layout): one banner per module per page, at the top,
   on Bootstrap's alert-warning. The link is where seats are assigned. */
.as-seat-notice { margin-bottom: 1rem; }
.as-seat-notice a { font-weight: 600; }

/* as-matrix */
/* ---- Matrix widget (js/awaresmith/matrix.js, plan QM-COMPETENCE-REGISTERS §3) -------
   People across the top with rotated names, competencies down the side, one small
   cell each; the header row and the first column stick. Status colours live in the
   --as-mx-* tokens below so a site can retune them; they are signal colours, not
   brand, so the brand block above stays the only place a brand hex is written.
   Every cell's title spells its state out in words: colour is never the only signal. */
:root {
    --as-mx-band: #eef1f8;      /* band rows: the filter-row tint */
    --as-mx-line: #c5ccd8;      /* outer frame, first-column rule, corner diagonal */
    --as-mx-grid: #d9dde4;      /* 1px cell borders */
    --as-mx-glyph: #0b1a3a;     /* cell glyphs on light fills */
    --as-mx-gap: #FFF200;       /* required, training not started */
    --as-mx-partial: #E0A100;   /* started, below the role's level (inner outline) */
    --as-mx-stop: #D11A1A;      /* may not do this work; white on it 5.42:1 */
    --as-mx-notreq: #f2f3f5;    /* no role needs it */
    --as-mx-v-review: #4A822B;  /* effectiveness checked at annual review; white on it 4.65:1 */
    --as-mx-v-super: #6B4A8E;   /* ... by a supervisor; 7.01:1 */
    --as-mx-v-test: #2F5F97;    /* ... by a test; 6.55:1 */
    --as-mx-v-third: #BA5809;   /* ... third-party certificate; 4.68:1 */
    --as-mx-due: #E0A100;       /* corner triangle: due within 30 days */
    --as-mx-expired: #C00000;   /* corner triangle: overdue; also the failed-save outline */
    --as-mx-linked: #FFD600;    /* arrived by link: the same yellow as th.as-grid-linked */
    --as-mx-cur: #e7f1fc;       /* crosshair on the active cell's two headers */
    --as-mx-h0: #F8696B; --as-mx-h1: #FDB57C; --as-mx-h2: #FFEB84; --as-mx-h3: #B6D7A8; --as-mx-h4: #63BE7B;
    --as-mx-r1: #eaf2fb; --as-mx-r2: #cfe2f6; --as-mx-r3: #9ec5ec;   /* required level 1-3; 4 is the ink */
    --as-mx-tot-bg: #f8f9fb;
    --as-mx-warn-ink: #9C5700; --as-mx-warn-bg: #FFF4D6;   /* amber total: 5.07:1 */
    --as-mx-bad-ink: #9C0006;  --as-mx-bad-bg: #FDE7E9;    /* red total: 7.36:1 */
}
.as-matrix {
    --as-mx-col: 30px; --as-mx-row: 20px; --as-mx-head: 150px; --as-mx-first: 290px;   /* 30px: two rotated lines (name + team) fit, 2026-10-18 */
    --as-mx-font: 11px; --as-mx-tot-w: 44px;
}
.as-matrix.as-mx-tight { --as-mx-col: 25px; --as-mx-row: 17px; --as-mx-font: 10px; }

.as-matrix .as-mx-status { color: var(--bs-secondary-color, #6c757d); min-width: 8rem; text-align: right; }
.as-matrix .as-mx-status.as-mx-busy { color: var(--as-ink); }
.as-matrix .as-mx-banner { color: #3a4250; }
.as-matrix .as-mx-banner.as-mx-banner-warn { color: var(--as-mx-warn-ink); }
.as-matrix .as-mx-filter-text { width: 16rem; max-width: 100%; }
.as-matrix .as-mx-filter .as-combo-input { width: 11rem; }
.as-matrix .as-mx-linkchip { display: inline-flex; align-items: center; gap: .35rem; font-size: .8rem;
    padding: .1rem .5rem; border-radius: 999px; outline: 3px solid var(--as-mx-linked); outline-offset: -3px; }

/* the scroller: both axes; scroll-padding keeps the active cell clear of the sticky parts */
.as-matrix .as-mx-scroll { overflow: auto; max-height: 70vh; border: 1px solid var(--as-mx-line); position: relative;
    outline: none; scroll-padding-top: calc(var(--as-mx-head) + 2px); scroll-padding-left: var(--as-mx-first); }
.as-matrix .as-mx-scroll:focus-visible { outline: 2px solid var(--as-ink); outline-offset: 1px; }
.as-matrix .as-mx-table { border-collapse: separate; border-spacing: 0; font-size: var(--as-mx-font); margin: 0; }
.as-matrix .as-mx-table th, .as-matrix .as-mx-table td {
    border-right: 1px solid var(--as-mx-grid); border-bottom: 1px solid var(--as-mx-grid); padding: 0; }
.as-matrix .as-mx-table thead th { position: sticky; top: 0; z-index: 3; background: var(--bs-body-bg, #fff);
    height: var(--as-mx-head); vertical-align: bottom; border-bottom: 2px solid var(--as-heading); font-weight: 400; }
/* corner: "People →" above a diagonal, "Competencies ↓" below it */
.as-matrix .as-mx-table thead th.as-mx-corner { left: 0; z-index: 5; width: var(--as-mx-first); min-width: var(--as-mx-first); max-width: var(--as-mx-first);
    background: linear-gradient(to top right, var(--bs-body-bg, #fff) calc(50% - .5px), var(--as-mx-line) calc(50% - .5px),
                var(--as-mx-line) calc(50% + .5px), var(--bs-body-bg, #fff) calc(50% + .5px)); }
.as-matrix .as-mx-c-top, .as-matrix .as-mx-c-bot { position: absolute; font-weight: 600; color: var(--as-heading); font-size: 12px; }
.as-matrix .as-mx-c-top { top: 10px; right: 12px; }
.as-matrix .as-mx-c-bot { bottom: 10px; left: 10px; }
/* rotated two-label headers: text reads bottom to top, like Excel's 90 degrees */
.as-matrix th.as-mx-col { width: var(--as-mx-col); min-width: var(--as-mx-col); max-width: var(--as-mx-col); }
.as-matrix .as-mx-table thead th.as-mx-tot { width: var(--as-mx-tot-w); min-width: var(--as-mx-tot-w); max-width: var(--as-mx-tot-w); background: var(--as-mx-tot-bg); }
.as-matrix .as-mx-rot { writing-mode: vertical-rl; transform: rotate(180deg); display: inline-block; margin-bottom: 6px; text-align: start; }
.as-matrix .as-mx-rot > span { display: block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
    max-inline-size: calc(var(--as-mx-head) - 12px); }
.as-matrix .as-mx-nm { font-weight: 600; font-size: 11.5px; color: var(--bs-body-color, #1b1f24); }
.as-matrix .as-mx-sub { font-size: 9.5px; color: var(--bs-secondary-color, #6c757d); }
.as-matrix th.as-mx-me .as-mx-nm::after { content: " (you)"; font-weight: 400; color: var(--as-ink); }
.as-matrix th.as-mx-inactive .as-mx-nm { color: #8a93a0; font-style: italic; }
.as-matrix .as-mx-badge-ok { color: var(--as-ink); }
.as-matrix .as-mx-badge-warn { color: var(--as-mx-warn-ink); }
.as-matrix .as-mx-badge-muted { color: #8a93a0; }
.as-matrix .as-mx-table thead th.as-mx-cur { background: var(--as-mx-cur); }
.as-matrix .as-mx-linked { outline: 3px solid var(--as-mx-linked); outline-offset: -3px; }
/* the sticky first column */
.as-matrix th.as-mx-name { position: sticky; left: 0; z-index: 2; background: var(--bs-body-bg, #fff);
    width: var(--as-mx-first); min-width: var(--as-mx-first); max-width: var(--as-mx-first); height: var(--as-mx-row);
    padding: 0 8px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-size: 12px; font-weight: 400;
    text-align: left; border-right: 2px solid var(--as-mx-line); }
.as-matrix th.as-mx-name.as-mx-cur { background: var(--as-mx-cur); }
.as-matrix .as-mx-code { color: #8a93a0; font-size: 10px; margin-left: 4px; }
/* bands: full-width category rows with a chevron and a count */
.as-matrix tr.as-mx-band td { background: var(--as-mx-band); height: calc(var(--as-mx-row) + 2px); cursor: pointer; }
.as-matrix .as-mx-band-lbl { position: sticky; left: 0; display: inline-flex; align-items: center; gap: 4px; padding: 0 8px;
    font-weight: 700; color: var(--as-heading); font-size: 11.5px; letter-spacing: .02em; }
.as-matrix .as-mx-chev { border: 0; background: none; padding: 0 2px; color: inherit; font: inherit; line-height: 1; }
.as-matrix .as-mx-band-lbl a { color: var(--as-ink); font-weight: 600; }
.as-matrix .as-mx-band-cnt { color: var(--bs-secondary-color, #6c757d); font-weight: 400; margin-left: 2px; }

/* cells. Diagonal, stop cross and the locked hatch are background layers set through
   two per-cell variables, so a hatched cell keeps its diagonal; ::before carries the
   save outline and ::after the due corner. Class-only selectors: legend swatches reuse them. */
.as-matrix .as-mx-cell { width: var(--as-mx-col); min-width: var(--as-mx-col); max-width: var(--as-mx-col); height: var(--as-mx-row);
    text-align: center; vertical-align: middle; font-weight: 700; color: var(--as-mx-glyph); position: relative;
    cursor: cell; user-select: none; background-image: var(--as-mx-mark, none), var(--as-mx-hatch, none); }
.as-matrix .as-mx-notreq { background-color: var(--as-mx-notreq); }
.as-matrix .as-mx-gap { background-color: var(--as-mx-gap); }
.as-matrix .as-mx-partial { box-shadow: inset 0 0 0 2px var(--as-mx-partial); }
.as-matrix .as-mx-v-review { background-color: var(--as-mx-v-review); color: #fff; }
.as-matrix .as-mx-v-super { background-color: var(--as-mx-v-super); color: #fff; }
.as-matrix .as-mx-v-test { background-color: var(--as-mx-v-test); color: #fff; }
.as-matrix .as-mx-v-third { background-color: var(--as-mx-v-third); color: #fff; }
.as-matrix .as-mx-diag { --as-mx-mark: linear-gradient(to top right, transparent calc(50% - 1px), currentColor calc(50% - 1px),
    currentColor calc(50% + 1px), transparent calc(50% + 1px)); }
.as-matrix .as-mx-stop { background-color: var(--as-mx-stop); color: #fff;
    --as-mx-mark: linear-gradient(to top right, transparent calc(50% - 1.2px), #fff calc(50% - 1.2px), #fff calc(50% + 1.2px), transparent calc(50% + 1.2px)),
                  linear-gradient(to top left, transparent calc(50% - 1.2px), #fff calc(50% - 1.2px), #fff calc(50% + 1.2px), transparent calc(50% + 1.2px)); }
.as-matrix .as-mx-due::after, .as-matrix .as-mx-expired::after { content: ""; position: absolute; top: 0; right: 0;
    border-top: 7px solid var(--as-mx-due); border-left: 7px solid transparent; }
.as-matrix .as-mx-expired::after { border-top-color: var(--as-mx-expired); }
.as-matrix .as-mx-heat { font-weight: 600; }
.as-matrix .as-mx-h0 { background-color: var(--as-mx-h0); }
.as-matrix .as-mx-h1 { background-color: var(--as-mx-h1); }
.as-matrix .as-mx-h2 { background-color: var(--as-mx-h2); }
.as-matrix .as-mx-h3 { background-color: var(--as-mx-h3); }
.as-matrix .as-mx-h4 { background-color: var(--as-mx-h4); }
.as-matrix .as-mx-req { font-weight: 600; }
.as-matrix .as-mx-r1 { background-color: var(--as-mx-r1); }
.as-matrix .as-mx-r2 { background-color: var(--as-mx-r2); }
.as-matrix .as-mx-r3 { background-color: var(--as-mx-r3); }
.as-matrix .as-mx-r4 { background-color: var(--as-ink); color: #fff; }
.as-matrix .as-mx-active { outline: 2px solid var(--as-heading); outline-offset: -2px; z-index: 1; }
.as-matrix .as-mx-pending::before, .as-matrix .as-mx-saving::before { content: ""; position: absolute; inset: 2px;
    border: 1px dotted currentColor; pointer-events: none; }
.as-matrix .as-mx-failed::before { content: ""; position: absolute; inset: 1px; border: 2px solid var(--as-mx-expired); pointer-events: none; }
.as-matrix .as-mx-locked { cursor: not-allowed;
    --as-mx-hatch: repeating-linear-gradient(135deg, rgba(20, 30, 50, .12) 0 2px, transparent 2px 6px); }
/* totals: right columns and footer rows */
.as-matrix td.as-mx-tot { text-align: center; font-weight: 600; color: #3a4250; background: var(--as-mx-tot-bg); font-size: 11px; }
.as-matrix .as-mx-table tfoot td, .as-matrix .as-mx-table tfoot th { background: var(--as-mx-tot-bg); font-weight: 600;
    text-align: center; font-size: 10.5px; height: var(--as-mx-row); }
.as-matrix .as-mx-table tfoot th.as-mx-name { color: var(--as-heading); text-align: left; }
.as-matrix td.as-mx-tot.as-mx-warn { color: var(--as-mx-warn-ink); background: var(--as-mx-warn-bg); }
.as-matrix td.as-mx-tot.as-mx-bad { color: var(--as-mx-bad-ink); background: var(--as-mx-bad-bg); }
.as-matrix td.as-mx-foot.as-mx-warn, .as-matrix td.as-mx-foot.as-mx-bad { color: var(--as-mx-bad-ink); }
.as-matrix .as-mx-empty { padding: 1rem; color: var(--bs-secondary-color, #6c757d); }
/* legend and the keys line */
.as-matrix .as-mx-legend { display: flex; flex-wrap: wrap; gap: 6px 18px; margin-top: 10px; font-size: .78rem; color: #3a4250; }
.as-matrix .as-mx-legend-item { display: inline-flex; align-items: center; gap: 6px; }
.as-matrix .as-mx-swatch { display: inline-block; line-height: var(--as-mx-row); border: 1px solid var(--as-mx-grid); font-size: var(--as-mx-font); }
.as-matrix .as-mx-keys { font-size: .75rem; color: var(--bs-secondary-color, #6c757d); margin-top: 6px; }
.as-matrix .as-mx-keys kbd { background: #f8f9fa; color: #212529; border: 1px solid #ced4da; border-bottom-width: 2px;
    border-radius: 3px; padding: 0 4px; font-size: .72rem; box-shadow: none; }
@media (max-width: 700px) {
    .as-matrix { --as-mx-first: 180px; --as-mx-head: 130px; }
}
/* Print (menu > Print): every filtered row, bands open, header repeated, legend last,
   landscape through a named page that only applies while the body carries the class. */
@media print {
    body.as-mx-printing { page: as-mx-landscape; }
    .as-matrix .as-mx-toolbar, .as-matrix .as-mx-bottom, .as-matrix .as-mx-keys, .as-matrix .as-mx-chev { display: none !important; }
    .as-matrix .as-mx-scroll { max-height: none; overflow: visible; border: 0; }
    .as-matrix .as-mx-table thead th, .as-matrix th.as-mx-name, .as-matrix .as-mx-band-lbl { position: static; }
    .as-matrix .as-mx-table thead { display: table-header-group; }
    .as-matrix .as-mx-table tr { break-inside: avoid; }
    .as-matrix .as-mx-cell, .as-matrix .as-mx-table th, .as-matrix .as-mx-table td, .as-matrix .as-mx-swatch {
        -webkit-print-color-adjust: exact; print-color-adjust: exact; }
    .as-matrix .as-mx-active { outline: 0; }
    .as-matrix .as-mx-locked { --as-mx-hatch: none; }
}
@page as-mx-landscape { size: landscape; margin: 10mm; }
/* end as-matrix */

/* ---- Page pulse (js/pages/Quality/pulse.js, 2026-10-18) --------------------------------------------------------
   A full-width card under a quality page's header: the key metric and a chart on the left, the notice on the right
   (green good, yellow alert, red danger). Two columns from 768px; stacked on a phone. */
.as-pulse { border: 1px solid var(--bs-border-color, #dee2e6); border-radius: 10px; overflow: hidden; }
.as-pulse-row { display: grid; grid-template-columns: 1fr; }
@media (min-width: 768px) { .as-pulse-row { grid-template-columns: 1fr 1fr; } }
.as-pulse-metric { display: grid; grid-template-columns: auto 1fr; grid-template-areas: "fig chart" "legend chart";
    gap: .25rem 1rem; align-items: center; padding: .9rem 1rem; }
.as-pulse-figure { grid-area: fig; }
.as-pulse-value { font-size: 2.1rem; font-weight: 700; line-height: 1; color: var(--as-heading, #00008B); }
.as-pulse-unit { margin-left: .35rem; color: var(--bs-secondary-color, #6c757d); font-size: .9rem; }
.as-pulse-caption { font-size: .8rem; color: var(--bs-secondary-color, #6c757d); margin-top: .2rem; }
.as-pulse-chart { grid-area: chart; position: relative; height: 110px; min-width: 0; }
.as-pulse-empty { height: 100%; display: flex; align-items: center; justify-content: center; font-size: .8rem;
    color: var(--bs-secondary-color, #6c757d); border: 2px dashed var(--bs-border-color, #dee2e6); border-radius: 50%;
    aspect-ratio: 1; margin: 0 auto; }
.as-pulse-legend { grid-area: legend; list-style: none; margin: 0; padding: 0; font-size: .78rem; }
.as-pulse-legend li { display: flex; align-items: center; gap: .35rem; white-space: nowrap; }
.as-pulse-dot { width: .7rem; height: .7rem; border-radius: 50%; flex: none; }
.as-pulse-notice { display: flex; gap: .75rem; align-items: flex-start; padding: .9rem 1rem; border-left: 6px solid; }
.as-pulse-icon { font-size: 1.6rem; line-height: 1; }
.as-pulse-level { font-size: .72rem; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; }
.as-pulse-title { font-weight: 600; }
.as-pulse-text { font-size: .9rem; }
.as-pulse-link { font-size: .85rem; }
.as-pulse-good { background: #e8f5ee; border-color: #198754; color: #0f5132; }
.as-pulse-good .as-pulse-icon { color: #198754; }
.as-pulse-alert { background: #fff8e1; border-color: #e0a800; color: #664d03; }
.as-pulse-alert .as-pulse-icon { color: #e0a800; }
.as-pulse-danger { background: #fdecee; border-color: #dc3545; color: #842029; }
.as-pulse-danger .as-pulse-icon { color: #dc3545; }
@media (max-width: 767.98px) { .as-pulse-notice { border-left: 0; border-top: 6px solid; } }
@media print { .as-pulse { display: none !important; } }   /* a printed record or report never carries the card */

