/* cosmo_portal — hand-written CSS. No preprocessor, no framework.
   Per spec §7.3: one CSS file, CSS variables for the palette, modern
   grid + flexbox. Modern dark theme (per Mal 2026-08-22 20:04). */

/* ============== Palette ============== */

:root {
    /* Surfaces (dark navy, layered for visual hierarchy) */
    --color-bg:           #0a1020;  /* page bg, deepest */
    --color-bg-elev:      #131b30;  /* navbar, header */
    --color-card:         #18233d;  /* cards, widgets, inputs */
    --color-card-hover:   #1f2c4a;  /* hover state */
    --color-input-bg:     #1e2942;  /* form input bg */

    /* Borders */
    --color-border:       #1f2a44;
    --color-border-strong:#2a3a5f;

    /* Text */
    --color-fg:           #e5e7eb;  /* primary text */
    --color-fg-muted:     #94a3b8;  /* secondary text */
    --color-fg-dim:       #64748b;  /* tertiary text */

    /* Brand accent (matches polymedtech logo blue) */
    --color-accent:       #38bdf8;
    --color-accent-hover: #0ea5e9;
    --color-accent-dim:   rgba(56, 189, 248, 0.15);

    /* Semantic */
    --color-error:        #f87171;
    --color-error-bg:     rgba(239, 68, 68, 0.12);
    --color-error-border: rgba(239, 68, 68, 0.4);
    --color-success:      #34d399;
    --color-success-bg:   rgba(52, 211, 153, 0.12);

    /* Layout */
    --space-xs: 0.25rem;
    --space-sm: 0.5rem;
    --space-md: 1rem;
    --space-lg: 2rem;
    --space-xl: 3rem;
    --radius-sm: 4px;
    --radius:    8px;
    --radius-lg: 12px;

    /* Elevation */
    --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.2);
    --shadow:    0 4px 12px rgba(0, 0, 0, 0.25);
    --shadow-lg: 0 12px 32px rgba(0, 0, 0, 0.35);

    /* Type */
    --font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
                  "Helvetica Neue", Arial, sans-serif;
    --font-mono: "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;
}

/* ============== Reset ============== */

* { box-sizing: border-box; }

html {
    /* Smoother font rendering on dark backgrounds */
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}

body {
    font-family: var(--font-sans);
    margin: 0;
    padding: 0;
    background: var(--color-bg);
    color: var(--color-fg);
    line-height: 1.5;
    font-size: 15px;
}

a {
    color: var(--color-accent);
    text-decoration: none;
    transition: color 120ms ease;
}
a:hover {
    color: var(--color-accent-hover);
    text-decoration: underline;
}

main {
    max-width: 1200px;
    margin: 0 auto;
    padding: var(--space-xl) var(--space-lg);
}

h1, h2, h3, h4 {
    margin: 0 0 var(--space-md);
    color: var(--color-fg);
    font-weight: 600;
    line-height: 1.25;
}
h1 { font-size: 1.75rem; }
h2 { font-size: 1.4rem; }
h3 { font-size: 1.15rem; }

p { margin: 0 0 var(--space-md); }

/* ============== Header / Navbar ============== */

.site-header {
    background: rgba(10, 16, 32, 0.85);
    backdrop-filter: saturate(180%) blur(12px);
    -webkit-backdrop-filter: saturate(180%) blur(12px);
    border-bottom: 1px solid var(--color-border);
    position: sticky;
    top: 0;
    z-index: 100;
}

.header-inner {
    max-width: 1200px;
    margin: 0 auto;
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: var(--space-md) var(--space-lg);
    gap: var(--space-lg);
}

.brand {
    display: inline-flex;
    align-items: center;
    text-decoration: none;
    color: var(--color-fg);
    transition: opacity 120ms ease;
}
.brand:hover {
    opacity: 0.85;
    text-decoration: none;
}
.brand img {
    height: 44px;
    width: auto;
    display: block;
    border-radius: var(--radius-sm);
}

.main-nav {
    display: flex;
    align-items: center;
    gap: var(--space-xs);
    flex-wrap: wrap;
    list-style: none;
    margin: 0;
    padding: 0;
}

.main-nav > .nav-direct,
.main-nav > .nav-dropdown > .nav-summary {
    color: var(--color-fg-muted);
    text-decoration: none;
    padding: var(--space-sm) var(--space-md);
    font-size: 0.95rem;
    font-weight: 500;
    border-radius: var(--radius-sm);
    cursor: pointer;
    display: inline-block;
    user-select: none;
    transition: color 120ms ease, background 120ms ease;
}
.main-nav > .nav-direct:hover,
.main-nav > .nav-dropdown[open] > .nav-summary {
    color: var(--color-fg);
    background: var(--color-accent-dim);
    text-decoration: none;
}
.main-nav > .nav-dropdown > .nav-summary::after {
    content: " \25BE";  /* small down-caret */
    color: var(--color-fg-dim);
    font-size: 0.75em;
    margin-left: 0.25em;
}

.nav-dropdown {
    position: relative;
}
.nav-dropdown > .nav-summary {
    list-style: none;
}
.nav-dropdown > .nav-summary::-webkit-details-marker { display: none; }

/* Nested dropdown summary padding — 2026-08-25.
   Per Mal: Resources first-level items ("Manual contour upload forms",
   "Product handling instructions", "Safety data sheets") rendered
   flush against the rounded panel left edge. They're nested
   <details>'s <summary> elements inside a .nav-menu, NOT the
   top-level nav triggers that `.main-nav > .nav-dropdown > .nav-summary`
   matches. So they fell back to browser default padding (≈0).

   The selector below catches nested-summary case specifically
   (specificity 0,3,2 — beats `.main-nav > .nav-dropdown > .nav-summary`
   at 0,3,0 only when the element is not a DIRECT child of
   `.main-nav > .nav-dropdown`). It applies the same vertical
   padding as `.nav-menu a` but bumps left to 1.5rem (24px) for
   breathing room. Vertical kept at `var(--space-sm)` to match
   the rest of the dropdown items.

   This also affects Submit Order's nested "Attenuflex eCutout"
   sub-trigger — same pattern, same fix. */
.main-nav .nav-menu li > details > .nav-summary {
    padding: var(--space-sm) var(--space-md) var(--space-sm) 1.5rem;
    cursor: pointer;
    color: var(--color-fg-muted);
}
.main-nav .nav-menu li > details > .nav-summary:hover {
    background: var(--color-accent-dim);
    color: var(--color-accent);
}

.nav-dropdown > .nav-menu {
    position: absolute;
    top: calc(100% + 6px);
    left: 0;
    background: var(--color-bg-elev);
    border: 1px solid var(--color-border-strong);
    border-radius: var(--radius);
    box-shadow: var(--shadow-lg);
    list-style: none;
    margin: 0;
    padding: var(--space-sm) 0;
    min-width: 220px;
    z-index: 10;
}
.nav-dropdown--right > .nav-menu { left: auto; right: 0; }
.nav-dropdown--footer > .nav-menu { top: auto; bottom: calc(100% + 6px); }

.nav-menu li { margin: 0; padding: 0; }
.nav-menu a,
.nav-menu .nav-logout-btn {
    display: block;
    padding: var(--space-sm) var(--space-md);
    color: var(--color-fg);
    text-decoration: none;
    font-size: 0.9rem;
    white-space: nowrap;
    transition: background 120ms ease, color 120ms ease;
}
.nav-menu a:hover,
.nav-menu .nav-logout-btn:hover {
    background: var(--color-accent-dim);
    color: var(--color-accent);
}

.nav-dropdown--nested > .nav-menu {
    top: 0;
    left: 100%;
}

.nav-logout {
    border-top: 1px solid var(--color-border);
    margin-top: var(--space-sm);
    padding-top: var(--space-sm);
}
.nav-logout form { margin: 0; }
.nav-logout-btn {
    background: none;
    border: none;
    color: var(--color-fg);
    font: inherit;
    text-align: left;
    cursor: pointer;
    width: 100%;
}

.main-nav > .nav-cta {
    background: var(--color-accent);
    color: #0a1020;
    font-weight: 600;
    padding: 0.5rem 1rem;
}
.main-nav > .nav-cta:hover {
    background: var(--color-accent-hover);
    color: #0a1020;
}

.user-chip {
    padding: var(--space-xs) var(--space-md);
    background: var(--color-accent-dim);
    border: 1px solid var(--color-border);
    border-radius: 999px;
    color: var(--color-fg);
    font-size: 0.85rem;
    font-weight: 500;
}

/* ============== Forms ============== */

label {
    display: block;
    margin-bottom: var(--space-sm);
    font-weight: 500;
    color: var(--color-fg);
}

input[type="text"],
input[type="email"],
input[type="password"],
input[type="date"],
input[type="number"],
select,
textarea {
    width: 100%;
    padding: 0.625rem 0.875rem;
    background: var(--color-input-bg);
    color: var(--color-fg);
    border: 1px solid var(--color-border-strong);
    border-radius: var(--radius-sm);
    font-size: 0.95rem;
    font-family: inherit;
    line-height: 1.5;
    transition: border-color 120ms ease, box-shadow 120ms ease;
    -webkit-appearance: none;
    appearance: none;
}

input:focus,
select:focus,
textarea:focus {
    outline: none;
    border-color: var(--color-accent);
    box-shadow: 0 0 0 3px var(--color-accent-dim);
}

input::placeholder,
textarea::placeholder {
    color: var(--color-fg-dim);
}

select {
    background-image: url("data:image/svg+xml;charset=US-ASCII,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%2394a3b8' d='M0 2l4 4 4-4z'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 0.75rem center;
    background-size: 10px;
    padding-right: 2rem;
}

button,
.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: var(--color-accent);
    color: #0a1020;
    border: none;
    padding: 0.625rem 1.25rem;
    border-radius: var(--radius-sm);
    cursor: pointer;
    font-size: 0.95rem;
    font-weight: 600;
    font-family: inherit;
    transition: background 120ms ease, transform 60ms ease;
    text-decoration: none;
}
button:hover,
.btn:hover {
    background: var(--color-accent-hover);
    text-decoration: none;
}
button:active,
.btn:active {
    transform: translateY(1px);
}

.btn-secondary {
    background: transparent;
    color: var(--color-fg);
    border: 1px solid var(--color-border-strong);
}
.btn-secondary:hover {
    background: var(--color-card-hover);
    border-color: var(--color-accent);
}

.error {
    color: var(--color-error);
    background: var(--color-error-bg);
    border: 1px solid var(--color-error-border);
    border-radius: var(--radius-sm);
    padding: var(--space-sm) var(--space-md);
    margin-bottom: var(--space-md);
    font-size: 0.9rem;
}
.success {
    color: var(--color-success);
    background: var(--color-success-bg);
    border: 1px solid rgba(52, 211, 153, 0.4);
    border-radius: var(--radius-sm);
    padding: var(--space-sm) var(--space-md);
    margin-bottom: var(--space-md);
    font-size: 0.9rem;
}

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

/* Plan §3d + Mal D2 2026-09-09: form-level PHI warning rendered
   above upload fields where CSV / PDF / XLSX / ZIP are accepted
   but NOT scanned for patient-name removal. The amber border +
   light fill grabs the operator's attention without screaming. */
.phi-warning {
    background: rgba(245, 158, 11, 0.08);  /* amber-500 at 8% */
    border-left: 3px solid var(--color-warn, #f59e0b);
    border-radius: var(--radius-sm);
    padding: var(--space-sm) var(--space-md);
    margin: var(--space-xs) 0 var(--space-md) 0;
    font-size: 0.85rem;
    line-height: 1.45;
    color: var(--color-fg);
}

.phi-warning strong {
    color: var(--color-warn-strong, #b45309);
}

.phi-warning code {
    background: var(--color-input-bg);
    padding: 0.05rem 0.35rem;
    border-radius: 3px;
    font-size: 0.85em;
}

/* Plan §4: dashboard "urgent attention" panel for open
   phi_strip_failures. Red left-border + light fill so it stands
   out from the standard dashboard content above it. */
.urgent-attention-panel {
    background: rgba(239, 68, 68, 0.06);  /* red-500 at 6% */
    border-left: 3px solid var(--color-error, #ef4444);
    border-radius: var(--radius-md);
    padding: var(--space-md) var(--space-lg);
    margin-bottom: var(--space-lg);
}

.urgent-attention-panel[data-accent="red"] .urgent-attention-title {
    color: var(--color-error, #ef4444);
}

.urgent-attention-header {
    display: flex;
    align-items: center;
    gap: var(--space-sm);
    margin-bottom: var(--space-sm);
}

.urgent-attention-icon {
    font-size: 1.3rem;
}

.urgent-attention-title {
    margin: 0;
    flex: 1;
    font-size: 1.05rem;
    font-weight: 600;
}

.urgent-attention-link {
    font-size: 0.9rem;
    color: var(--color-link, #38bdf8);
    text-decoration: none;
}

.urgent-attention-link:hover {
    text-decoration: underline;
}

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

.urgent-attention-item {
    display: grid;
    grid-template-columns: minmax(140px, auto) 1fr auto;
    grid-template-areas:
        "order  meta   time"
        "error  error  error";
    column-gap: var(--space-md);
    row-gap: 2px;
    padding: var(--space-sm) 0;
    border-bottom: 1px solid var(--color-border, rgba(255,255,255,0.08));
    font-size: 0.85rem;
    line-height: 1.45;
}

.urgent-attention-item:last-child {
    border-bottom: none;
}

.urgent-attention-item[data-open="false"] {
    opacity: 0.7;
}

.urgent-attention-order {
    grid-area: order;
}

.urgent-attention-order a {
    color: var(--color-link, #38bdf8);
    text-decoration: none;
    font-weight: 500;
}

.urgent-attention-slot {
    color: var(--color-fg-muted);
    font-size: 0.8rem;
    margin-left: 0.4rem;
}

.urgent-attention-source {
    grid-area: meta;
    color: var(--color-fg-muted);
    font-family: var(--font-mono, monospace);
    font-size: 0.78rem;
    align-self: center;
}

.urgent-attention-error {
    grid-area: error;
    color: var(--color-fg);
    font-size: 0.82rem;
    word-break: break-word;
}

.urgent-attention-error code {
    color: var(--color-error, #ef4444);
    font-weight: 600;
}

.urgent-attention-time {
    grid-area: time;
    color: var(--color-fg-muted);
    font-size: 0.78rem;
    align-self: center;
    text-align: right;
    white-space: nowrap;
}

.badge-resolved {
    display: inline-block;
    background: rgba(34, 197, 94, 0.25);
    color: rgb(134, 239, 172);
    border: 1px solid rgba(34, 197, 94, 0.5);
    padding: 0.1rem 0.5rem;
    border-radius: 999px;
    font-size: 0.72rem;
    font-weight: 600;
    margin-left: var(--space-xs);
}

/* ============== Login page ============== */

body:has(.login-page) {
    background: linear-gradient(180deg, #0a1020 0%, #131b30 100%);
}

.login-page {
    max-width: 420px;
    margin: 5vh auto 0;
    padding: 0;
    text-align: center;
}

.login-heading {
    font-size: 1.75rem;
    font-weight: 600;
    margin: 0 0 var(--space-sm);
    color: var(--color-fg);
}

.login-subheading {
    color: var(--color-fg-muted);
    margin: 0 0 var(--space-xl);
    font-size: 0.95rem;
}

.login-form {
    background: var(--color-card);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
    padding: var(--space-xl);
    box-shadow: var(--shadow-lg);
    text-align: left;
}

.login-form img.brand-logo {
    display: block;
    margin: 0 auto var(--space-lg);
    height: 56px;
    width: auto;
    border-radius: var(--radius-sm);
}

.login-fields .form-group {
    margin-bottom: var(--space-md);
}

.login-fields button {
    width: 100%;
    margin-top: var(--space-sm);
    padding: 0.75rem;
    font-size: 1rem;
}

/* ============== Footer ============== */

.site-footer {
    background: var(--color-bg-elev);
    color: var(--color-fg-muted);
    border-top: 1px solid var(--color-border);
    padding: var(--space-lg);
    margin-top: var(--space-xl);
}

.footer-nav {
    display: flex;
    justify-content: center;
    align-items: center;
    gap: var(--space-md);
    padding: 0 0 var(--space-md);
    flex-wrap: wrap;
}
.footer-direct {
    color: var(--color-fg-muted);
    text-decoration: none;
    font-size: 0.9rem;
    transition: color 120ms ease;
}
.footer-direct:hover {
    color: var(--color-accent);
    text-decoration: none;
}
.footer-copyright {
    text-align: center;
    color: var(--color-fg-dim);
    font-size: 0.85rem;
    padding: 0;
    margin: 0;
    line-height: 1.5;
}

/* ============== Account / Multi-section form ============== */

/* ============== Account / Multi-section form ============== */

/* Form section cards on /users/account — 2026-08-25 (per Mal).
   Previous approach used <fieldset>+<legend>. The legend text
   sat ON the fieldset's top border (browser draws the border
   through the legend's bounding box) — the border remained visible
   cutting through the title text even with a background-color
   trick. Computed styles confirmed: legend bg matched card bg
   pixel-for-pixel, but the border was still drawn ON TOP of the
   legend background.

   Replaced in template with <section role="group"
   aria-labelledby="account-section-N"> + <h3 class="account-section-title">
   — semantic grouping preserved (role="group" + aria-labelledby is
   the WAI-ARIA equivalent of fieldset+legend), but visual hierarchy
   is now controlled by plain CSS without browser quirks. */

.account-page section.account-section {
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    padding: var(--space-lg);
    margin-bottom: var(--space-lg);
    background: var(--color-card);
}
.account-page .account-section-title {
    font-weight: 600;
    font-size: 1.15rem;
    color: var(--color-fg);
    margin: 0 0 var(--space-md) 0;
    /* Sits clearly INSIDE the card border (no border-cutting — the
       section element doesn't trigger the fieldset/legend
       rendering quirk). */
}
.account-page section.account-section legend {
    /* Hide the original <legend> when it appears (some templates
       still use it). The visual h3 above carries the heading. */
    display: none;
}
.account-page legend {
    /* Legacy CSS for any fieldset/legend not yet converted.
       Kept for the orders/{rnd,supportingEquipment,brachytherapy},
       pages/patientMatch, and accounts templates that still use
       fieldset+legend. Same background-matching trick as before —
       in those cases the legend visually breaks the border, which
       is acceptable for those contexts. (Account page no longer
       uses fieldset.) */
    font-weight: 600;
    padding: 0 var(--space-sm);
    color: var(--color-fg);
}
.account-page legend small {
    font-weight: 400;
    color: var(--color-fg-muted);
    font-size: 0.85rem;
    margin-left: var(--space-xs);
}
.account-page .account-info {
    display: grid;
    grid-template-columns: max-content 1fr;
    gap: var(--space-sm) var(--space-md);
    margin: 0 0 var(--space-md) 0;
}
.account-page .account-info dt {
    font-weight: 600;
    color: var(--color-fg-muted);
}
.account-page .account-info dd { margin: 0; }

.checkbox-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
    gap: var(--space-sm);
}
.checkbox-label {
    display: flex;
    align-items: center;
    gap: var(--space-sm);
    padding: var(--space-sm);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    background: var(--color-bg-elev);
    cursor: pointer;
    transition: border-color 120ms ease, background 120ms ease;
}
.checkbox-label:hover {
    border-color: var(--color-accent);
    background: var(--color-card-hover);
}
.checkbox-label.disabled {
    color: var(--color-fg-dim);
    cursor: not-allowed;
    opacity: 0.6;
}
.checkbox-label input[type="checkbox"] { margin: 0; }
.checkbox-label small {
    color: var(--color-fg-muted);
    font-size: 0.8rem;
    margin-left: var(--space-xs);
}

/* ============== Client survey ============== */

.client-survey-page { max-width: 900px; margin: 0 auto; }
.client-survey-grid {
    display: grid;
    grid-template-columns: 1fr auto 1fr;
    gap: var(--space-md);
    align-items: start;
    margin: var(--space-md) 0;
}
.client-survey-grid .or-divider {
    align-self: center;
    font-weight: bold;
    color: var(--color-fg-muted);
    padding: 0 var(--space-md);
}
.client-survey-grid fieldset {
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    padding: var(--space-lg);
    margin: 0;
    background: var(--color-card);
}
.client-survey-grid legend {
    font-weight: 600;
    padding: 0 var(--space-sm);
}
.client-survey-page .info {
    background: var(--color-accent-dim);
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    padding: var(--space-sm) var(--space-md);
    color: var(--color-fg);
}
.client-list {
    margin-top: var(--space-md);
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    padding: var(--space-sm);
    background: var(--color-card);
}
.client-list summary {
    cursor: pointer;
    font-weight: 500;
    padding: var(--space-sm);
}
.client-list ul {
    margin: var(--space-sm) 0 0 var(--space-md);
    padding: 0;
    max-height: 200px;
    overflow-y: auto;
}
.client-list li {
    padding: 0.25rem 0;
    font-family: var(--font-mono);
    font-size: 0.9rem;
}
@media (max-width: 700px) {
    .client-survey-grid { grid-template-columns: 1fr; }
    .client-survey-grid .or-divider { padding: var(--space-sm) 0; }
}

/* ============== Patient match / eCutout preview ============== */

.patient-match-page { max-width: 720px; margin: 0 auto; }
.patient-match-form fieldset {
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    padding: var(--space-lg);
    margin-bottom: var(--space-lg);
    background: var(--color-card);
}
.patient-match-form legend {
    font-weight: 600;
    padding: 0 var(--space-sm);
}
.patient-match-form label small {
    color: var(--color-fg-muted);
    font-size: 0.85rem;
    margin-left: var(--space-sm);
}
.patient-match-form .form-actions {
    display: flex;
    justify-content: flex-end;
    margin-top: var(--space-md);
}
.preview-summary {
    background: var(--color-card);
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    padding: var(--space-lg);
    margin-top: var(--space-lg);
}
.preview-fields {
    display: grid;
    grid-template-columns: max-content 1fr;
    gap: var(--space-sm) var(--space-md);
    margin: var(--space-md) 0;
}
.preview-fields dt {
    font-weight: 600;
    color: var(--color-fg-muted);
}
.preview-fields dd { margin: 0; }

/* ============== Order registry / admin ============== */

.order-registry-page { max-width: 1100px; margin: 0 auto; }
.registry-actions {
    margin: var(--space-md) 0;
    display: flex;
    gap: var(--space-sm);
}
.order-registry-page .empty {
    color: var(--color-fg-muted);
    font-style: italic;
    padding: var(--space-md) 0;
}

.client-group-matrix {
    width: 100%;
    border-collapse: collapse;
    margin: var(--space-md) 0;
    background: var(--color-card);
    border-radius: var(--radius);
    overflow: hidden;
}
.client-group-matrix th,
.client-group-matrix td {
    padding: var(--space-sm);
    border: 1px solid var(--color-border);
    text-align: center;
}
.client-group-matrix th {
    background: var(--color-bg-elev);
    font-weight: 600;
}
.client-group-matrix .product-label {
    text-align: left;
    font-family: var(--font-mono);
    background: var(--color-bg-elev);
}

/* Settings page read-only sections — 2026-08-25.
   Per Mal: the `<dl>`/definition-list rendering read as raw text on
   the admin settings page. Convert each section (app, server, session,
   uploads, dashboard, search, ecutout) into a 2-column table —
   "Setting" / "Value" — matching the existing `.client-group-matrix`
   styling. Reuses `--color-card`, `--color-border`, `--radius` etc.
   so the look matches the matrix table below it. */
.settings-table {
    width: 100%;
    border-collapse: collapse;
    margin: var(--space-md) 0;
    background: var(--color-card);
    border-radius: var(--radius);
    overflow: hidden;
}
.settings-table th,
.settings-table td {
    padding: var(--space-sm) var(--space-md);
    border: 1px solid var(--color-border);
    text-align: left;
}
.settings-table th {
    background: var(--color-bg-elev);
    font-weight: 600;
    width: 40%;
}
.settings-table td:first-child {
    font-family: var(--font-mono);
    color: var(--color-fg-muted);
}
.settings-table td:last-child {
    font-family: var(--font-mono);
    color: var(--color-fg);
}

/* Generic orders/results table — used by 4 templates:
     - admin/editEcutoutLibrary.html (library entries)
     - admin/wcMassLookup.html (mass lookup results)
     - statement/view.html (statement rows)
     - search/results.html (search results)

   Per Mal 2026-08-25 20:19 — eCutout library section showed raw
   browser-default table styling. Apply the same dark-card
   background + rounded corners + mono cells as .settings-table
   and .client-group-matrix. Same approach: define once, reuse.

   Per-column widths use CSS grid-style "nth-child" fractional widths
   to balance readability of long paths / hashes / numbers against
   short labels like Name. No table-layout:fixed — cells size to
   their content. */
.orders {
    width: 100%;
    border-collapse: collapse;
    margin: var(--space-md) 0;
    background: var(--color-card);
    border-radius: var(--radius);
    overflow: hidden;
}
.orders th,
.orders td {
    padding: var(--space-sm) var(--space-md);
    border: 1px solid var(--color-border);
    text-align: left;
    vertical-align: top;
}
.orders th {
    background: var(--color-bg-elev);
    font-weight: 600;
    color: var(--color-fg);
    font-family: var(--font-mono);
    font-size: 0.85rem;
    letter-spacing: 0.04em;
    text-transform: uppercase;
}
.orders td {
    color: var(--color-fg);
}
.orders td code {
    font-family: var(--font-mono);
    font-size: 0.85rem;
    word-break: break-all;  /* long paths + hashes should wrap, not overflow */
    color: var(--color-fg-muted);
}
.orders tbody tr:nth-child(even) {
    /* Subtle stripe for readability on long lists. Same
       alpha-overlay technique as .dashboard-widget. */
    background: rgba(255, 255, 255, 0.02);
}
/* Responsive: on narrow viewports the table can scroll horizontally
   rather than wrapping cells into a card pile (the columns are
   path-heavy, so wrapping is uglier than scrolling). */
.orders {
    display: block;
    overflow-x: auto;
    white-space: nowrap;
}
@media (min-width: 900px) {
    .orders {
        display: table;
        overflow-x: visible;
        white-space: normal;
    }
}

/* Summary tables on /statement/current and /statement/previous
   — 2026-08-25 (per Mal). The template uses <table class="summary">
   for the "By product type" and "By status" sub-tables at the
   top of the statement page, but no .summary CSS rule existed,
   so the tables rendered as raw browser-default text (no card
   chrome, no header row styling, no row separators). Mal wanted
   them to look like the .orders table below them — same dark card
   chrome, mono header row, even-row stripe, rounded corners.

   Two-column "key facts" tables are different from .orders:
   - No long path/hash content that needs word-break.
   - Numeric column should right-align (.summary .num class).
   - Status column shows a status pill (.status badge).
   - Tables sit side-by-side at ≥900px (.summary-tables grid). */

.summary-tables {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--space-md);
    margin: var(--space-md) 0;
}
@media (min-width: 900px) {
    .summary-tables {
        /* Side-by-side on desktop for the two key-facts tables. */
        grid-template-columns: 1fr 1fr;
    }
}

.summary {
    width: 100%;
    border-collapse: collapse;
    background: var(--color-card);
    border-radius: var(--radius);
    overflow: hidden;
}
.summary th,
.summary td {
    padding: var(--space-sm) var(--space-md);
    border: 1px solid var(--color-border);
    text-align: left;
    vertical-align: top;
}
.summary th {
    background: var(--color-bg-elev);
    font-weight: 600;
    color: var(--color-fg);
    font-family: var(--font-mono);
    font-size: 0.8rem;
    letter-spacing: 0.04em;
    text-transform: uppercase;
}
.summary td {
    color: var(--color-fg);
}
.summary .num {
    /* Right-align numeric columns + tabular-nums for column
       stability. Used by .summary thead.num + .summary td.num. */
    text-align: right;
    font-variant-numeric: tabular-nums;
    font-family: var(--font-mono);
    width: 1%;  /* shrink-wrap trick so the text column expands and the num column is tight */
}
.summary tbody tr:nth-child(even) {
    background: rgba(255, 255, 255, 0.02);
}

/* Status badge — used in the dashboard table, the order
   registry, the statement, the search results, and the order
   confirm / update_status / return_request pages.
   The tone is set on the element via ``data-status-tone`` and
   picked up from the STATUS_TONES map in app/util/text.py
   (the single source of truth for the status → colour mapping).
   Six tones — cyan / amber / purple / green / red / grey — match
   the KPI accent strip palette (step 5) so a status badge and
   the KPI it tracks read as the same operational state. */
.status {
    display: inline-block;
    padding: 2px 8px;
    border-radius: 999px;
    font-size: 0.8rem;
    font-weight: 500;
    background: var(--color-bg-elev);
    color: var(--color-fg);
    border: 1px solid var(--color-border);
    white-space: nowrap;
}
.status[data-status-tone="cyan"] {
    background: rgba(56, 189, 248, 0.15);
    color: #38bdf8;
    border-color: rgba(56, 189, 248, 0.3);
}
.status[data-status-tone="amber"] {
    background: rgba(251, 191, 36, 0.15);
    color: #fbbf24;
    border-color: rgba(251, 191, 36, 0.3);
}
.status[data-status-tone="purple"] {
    background: rgba(167, 139, 250, 0.15);
    color: #a78bfa;
    border-color: rgba(167, 139, 250, 0.3);
}
.status[data-status-tone="green"] {
    background: rgba(52, 211, 153, 0.15);
    color: #34d399;
    border-color: rgba(52, 211, 153, 0.3);
}
.status[data-status-tone="red"] {
    background: rgba(248, 113, 113, 0.15);
    color: #f87171;
    border-color: rgba(248, 113, 113, 0.3);
}
.status[data-status-tone="grey"] {
    background: rgba(160, 160, 160, 0.15);
    color: var(--color-fg-muted);
    border-color: var(--color-border);
}
.matrix-checkbox {
    display: flex;
    justify-content: center;
    align-items: center;
    padding: var(--space-sm);
    cursor: pointer;
    min-height: 32px;
}
.matrix-form .form-actions {
    display: flex;
    justify-content: flex-end;
    margin-top: var(--space-md);
}

/* ============== Search form (named filter highlight) ============== */

.search-form label.active-filter {
    border-left: 3px solid var(--color-accent);
    padding-left: var(--space-sm);
    background: var(--color-accent-dim);
    border-radius: var(--radius-sm);
}

/* ============== Dashboard ============== */

.dashboard-page { padding-top: var(--space-md); }
.dashboard-greeting {
    font-size: 1.75rem;
    font-weight: 600;
    margin: 0 0 4px;
    color: var(--color-fg);
}
.dashboard-intro {
    margin: 0 0 var(--space-xl);
    color: var(--color-fg-muted);
    font-size: 1rem;
}

.widgets {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--space-lg);
}
@media (max-width: 720px) {
    .widgets { grid-template-columns: 1fr; }
}

/* Dashboard mobile polish — 2026-08-25. The widget-table has
   per-column fixed widths (config.yaml) summing to ~530px; at
   narrow widths that means each row wastes 24px on the empty
   action column + 24px on the action <col>. Hide both on mobile
   to claw back ~50px per row of horizontal real estate. The
   action <th>/<td> are intentionally empty in product_tile.html
   (legacy parity — the column was reserved for row actions that
   were never wired up), so hiding them is cosmetic only. */
@media (max-width: 720px) {
    .dashboard-widget .widget-table .widget-action,
    .dashboard-widget .widget-table .widget-action-col {
        display: none;
    }
}

/* Card layout at mobile widths — 2026-08-25 (Mal feedback: tables
   overflow clipped on his actual iPhone). At <=720px the widget
   is narrower than the table (~326px widget vs ~510px table for
   the Bolus widget), so even with overflow-x:auto the visible
   state shows columns clipped at the right edge — looks like
   collision. Transform the table into a stack of cards: hide
   the thead, each <tr> becomes its own card, each <td> becomes a
   row inside the card with the column label prepended via the
   data-label="…" attribute (added in product_tile.html).

   Only the display model changes; the row_class (yellow/pink
   priority), the wrap-cell clamp, and the .widget-thumb all
   still work because they're scoped to descendants, not the
   outer table layout. */
@media (max-width: 720px) {
    .dashboard-widget .widget-table thead {
        /* Labels now come from each td's data-label attr, so the
           thead is pure noise on mobile. */
        display: none;
    }
    .dashboard-widget .widget-table,
    .dashboard-widget .widget-table tbody,
    .dashboard-widget .widget-table tr,
    .dashboard-widget .widget-table td {
        display: block;
        width: auto;
    }
    .dashboard-widget .widget-table tbody {
        /* No gap between cards (tr has its own margin-bottom). */
    }
    .dashboard-widget .widget-table tr {
        /* Each row becomes a card. Keep the row_class bg colours
           (yellow / pink / transparent) for in-flight priority. */
        margin-bottom: var(--space-sm);
        border: 1px solid var(--color-border);
        border-radius: var(--radius-sm);
        padding: var(--space-sm) var(--space-md);
    }
    .dashboard-widget .widget-table tr:last-child {
        margin-bottom: 0;
    }
    .dashboard-widget .widget-table td {
        /* Each cell is a label + value pair. Border between cells
           is unnecessary inside a card. */
        padding: 4px 0;
        border: none;
    }
    .dashboard-widget .widget-table td::before {
        /* Prepend the column label. ``attr(data-label)`` reads the
           attribute set by product_tile.html. */
        content: attr(data-label);
        display: block;
        font-size: 0.7rem;
        font-weight: 600;
        letter-spacing: 0.04em;
        text-transform: uppercase;
        color: var(--color-fg-muted);
        margin-bottom: 2px;
    }
    /* The wrap-cell clamp only makes sense in tabular layout — the
       card layout already gives each cell its own row, so the
       2-line clamp becomes a regular wrap. Reset it. */
    .dashboard-widget .widget-table td.wrap-cell > .cell-content {
        display: block;
        -webkit-line-clamp: unset;
        line-clamp: unset;
        -webkit-box-orient: unset;
        overflow: visible;
    }
    /* The order-number cell hosts the ecutout_thumbnail inline in
       the table view. In card view it sits below the order-number
       label / value — keep it but make sure it doesn't push the
       card width. */
    .dashboard-widget .widget-table td[data-label="Order Number"] .widget-thumb {
        display: block;
        margin: 6px 0 0;
    }
}

/* Header stack on narrow screens — 2026-08-25. Below 800px the
   brand logo + 5 nav items + user chip don't fit in one flex
   row, so the items wrap and the user-chip ends up on its own
   line with awkward spacing. Stack the header into two rows
   (brand on top, nav below) so each gets full width and the
   nav can wrap its own items cleanly. The nav's flex-wrap:wrap
   rule (already in place) handles item-level wrapping. */
@media (max-width: 800px) {
    .header-inner {
        flex-direction: column;
        align-items: stretch;
        gap: var(--space-sm);
        padding: var(--space-sm) var(--space-md);
    }
    .main-nav {
        justify-content: flex-start;
    }
}

.dashboard-widget {
    background: var(--color-card);
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    margin-bottom: var(--space-md);
    overflow: hidden;
    min-width: 0;
    box-shadow: var(--shadow-sm);
}

.dashboard-widget > h3 {
    line-height: 60px;
    position: relative;
    margin: 0;
    padding: 0 var(--space-lg);
    font-size: 1.05rem;
    font-weight: 600;
    border-bottom: 1px solid var(--color-border);
    background: var(--color-bg-elev);
    color: var(--color-fg);
}

.dashboard-widget > h3 > .widget-icon {
    position: absolute;
    top: 0;
    right: 0;
    height: 60px;
    width: auto;
    border-left: 1px solid var(--color-border);
}

.dashboard-widget > .table-wrap {
    max-height: 530px;
    overflow-y: auto;
    /* 2026-08-31 (Cosmo feedback): "all columns should show all rows
       without needing to grab a horizontal scroll bar". Previous
       behaviour was overflow-x: auto so users could horizontally
       scroll inside the widget when it was narrower than the
       fixed-pixel table. Now that .widget-table uses table-layout:
       auto + per-cell overflow-wrap: anywhere, the table shrinks
       to fit the widget — no horizontal scroll needed, on mobile OR
       desktop. overflow-x: hidden is the safety net for any future
       regression that pushes content past the widget edge (better
       to clip than to add a horizontal scroll that hides the
       later columns).
       -webkit-overflow-scrolling: touch retained so iOS still gets
       momentum scrolling for the (vertical) overflow-y case. */
    overflow-x: hidden;
    -webkit-overflow-scrolling: touch;
}

.dashboard-widget .widget-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.9rem;
    /* table-layout: auto (was 'fixed') so column widths size to the
       widget's available space instead of honouring the per-column
       pixel widths from config.yaml. Long content (DCM paths, full
       filenames) wraps inside its cell via overflow-wrap below
       instead of pushing the column wider than the widget. The
       <col> widths are now applied as min-width hints (see
       product_tile.html) so a column never gets narrower than its
       hint — e.g. the eCutout Order Number column needs 110px to
       host its thumbnail. */
    table-layout: auto;
}

.dashboard-widget .widget-table th,
.dashboard-widget .widget-table td {
    padding: 0.625rem 0.75rem;
    border: 1px solid var(--color-border);
    text-align: left;
    vertical-align: top;
    color: var(--color-fg);
    /* Long content (file paths, session IDs, free-text descriptions)
       wraps inside the cell rather than forcing the column wider.
       'anywhere' breaks at any character if needed (vs 'break-word'
       which only breaks at whitespace). The wrap-cell clamp (Status
       + Client File, 2-line ellipsis) still works on top of this
       because the .cell-content span inside those cells is
       display:-webkit-box, which honours the cell width. */
    overflow-wrap: anywhere;
    word-break: normal;
}

.dashboard-widget .widget-table thead th {
    background: var(--color-bg-elev);
    font-weight: 600;
    color: var(--color-fg);
}

.dashboard-widget .widget-table tbody tr:nth-child(even) {
    background: rgba(255, 255, 255, 0.02);
}

.dashboard-widget .widget-table .widget-action { width: 24px; }

.dashboard-widget .widget-table .widget-thumb {
    /* eCutout contour thumbnail — rendered under the order
       number (legacy behaviour). Native PNG is 517x532; cap
       at 90px wide so it fits in the 110px order-number column
       with margin. Block-level + margin auto centers it.

       Background: ``transparent`` (was ``var(--color-bg)``).
       Removing the dark navy background lets the cell's priority
       background (priority-warning yellow / priority-danger
       pink) show through transparent pixels of the thumbnail
       PNG. Solid-color thumbnails (no transparency) are
       unaffected. Bug fix 2026-08-24 — Mal flagged the
       conditional formatting only covered part of the order
       number cell, the dark thumbnail background was hiding
       the yellow/pink under the image. */
    display: block;
    width: 90px;
    max-width: 100%;
    height: auto;
    margin: 6px auto 0;
    border-radius: var(--radius-sm);
    border: 1px solid var(--color-border);
    background: transparent;
}
.dashboard-widget .widget-table .widget-action-col { width: 24px; }

.dashboard-widget .widget-table td.wrap-cell {
    /* display:-webkit-box + -webkit-line-clamp: 2 truncates a cell to
       at most 2 lines with ellipsis. The -webkit-box display model
       breaks background-color propagation in priority rows (only
       the content box gets the yellow / pink; padding stays dark).

       Fix: wrap the cell's text content in a <span class="cell-content">;
       apply display: -webkit-box to the SPAN, not the <td>. The <td>
       stays display: table-cell, so its background fills normally.
       See product_tile.html + cell-content CSS rule below. */
    overflow-wrap: anywhere;
    word-break: normal;
}
.dashboard-widget .widget-table td.wrap-cell > .cell-content {
    /* -webkit-line-clamp: 2 truncation lives on a child span so
       the <td> keeps table-cell display (and background propagation
       works for priority-warning / priority-danger rows). */
    display: -webkit-box;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 100%;
}

.dashboard-widget .widget-table th.wrap-cell {
    /* Header cells with the wrap-cell class just wrap naturally on
       whitespace; no -webkit-box (see td rule above for the why). */
    overflow-wrap: anywhere;
    word-break: normal;
}

/* Dashboard row-highlighting — legacy parity.
   See ordercontrol/public/stylesheets/app.css:
       .priority-warning td { background-color: #fff3cd; color: #856404; }
       .priority-danger  td { background-color: #f8d7da; color: #721c24; }
   Applied to <td> rather than <tr> so the colour doesn't bleed to
   the empty action column or the table border. Class is set by
   the dashboard template via the Jinja global ``order_priority``
   (defined in app/orders.py). */
.dashboard-widget .widget-table tbody tr.priority-warning td {
    background-color: #fff3cd;
    color: #856404;
}
.dashboard-widget .widget-table tbody tr.priority-danger td {
    background-color: #f8d7da;
    color: #721c24;
}

/* ============== Bolus form / slot rows ============== */

.bolus-type-row {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: var(--space-md);
    align-items: end;
    margin-bottom: var(--space-md);
}
.bolus-type-select label,
.bolus-type-file label {
    display: block;
    margin-bottom: var(--space-sm);
    font-weight: 500;
}
.bolus-type-file .optional {
    font-weight: normal;
    color: var(--color-fg-muted);
    font-size: 0.85rem;
    margin-left: 0.4em;
}
@media (max-width: 720px) {
    .bolus-type-row { grid-template-columns: 1fr; }
}

.slot-row {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: var(--space-md);
    align-items: start;
    margin-bottom: var(--space-md);
    padding: var(--space-md);
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    background: var(--color-card);
}
.slot-row > .slot-select,
.slot-row > .slot-file,
.slot-row > .slot-type,
.slot-row > .slot-part {
    display: flex;
    flex-direction: column;
    gap: var(--space-sm);
    min-width: 0;
}
.silicon-bolus-slots .slot-row {
    grid-template-columns: 1fr 1fr 1fr;
}
@media (max-width: 720px) {
    .slot-row,
    .silicon-bolus-slots .slot-row { grid-template-columns: 1fr; }
}

/* ============== Sortable table headers (User List 2026-08-31) ==============
   Click-to-sort column headers on .orders tables with [data-sortable].
   Server-default order is restored on the third click of the same
   column (server ORDER BY username ASC). Sort state persists in
   location.hash so refresh + share-link preserve the view.
   ----------------------------------------------------------------------- */

.orders th.sortable,
.orders-table th.sortable {
    padding: 0;  /* the button inside provides the visual padding */
}
.orders th .sort-header-btn,
.orders-table th .sort-header-btn {
    /* The header TH is now a click target — but we keep a real <button>
       inside for keyboard accessibility (focusable, Enter/Space activate)
       and ARIA semantics (aria-sort on the button). */
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 6px;
    width: 100%;
    padding: var(--space-sm) var(--space-md);
    background: transparent;
    border: 0;
    color: inherit;
    font: inherit;
    letter-spacing: inherit;
    text-transform: inherit;
    text-align: left;
    cursor: pointer;
    user-select: none;
}
.orders th .sort-header-btn:hover,
.orders-table th .sort-header-btn:hover {
    background: rgba(255, 255, 255, 0.04);
}
.orders th .sort-header-btn:focus-visible,
.orders-table th .sort-header-btn:focus-visible {
    /* Keyboard focus indicator — clearer than the browser default on
       transparent backgrounds. */
    outline: 2px solid var(--color-accent, #6ea8fe);
    outline-offset: -2px;
}
.orders th .sort-header-btn:active,
.orders-table th .sort-header-btn:active {
    background: rgba(255, 255, 255, 0.06);
}
.orders th.sortable .sort-arrow,
.orders-table th.sortable .sort-arrow {
    /* Reserve space so the column width doesn't jump when the arrow
       appears on click. Glyph is ▲ (asc) or ▼ (desc); empty when unsorted. */
    display: inline-block;
    width: 0.9em;
    text-align: center;
    font-size: 0.75em;
    opacity: 0.85;
}
.orders th.sorted,
.orders-table th.sorted {
    /* Visual emphasis on the active sort column — same hue as the
       login-page heading accent so it reads as "you've focused here". */
    background: rgba(110, 168, 254, 0.10);
}

/* ============== Dashboard action buttons (2026-08-31) ==============
   Per-row action buttons on the dashboard widgets (currently eCutout
   and AttenuFlex). Each button is a <form> with a hidden CSRF token
   posting to /admin/orders/<order_number>/<action_key>. The button
   label comes from config.yaml -> dashboard.widgets[].actions[].
   --------------------------------------------------------------- */

.dashboard-widget .widget-table .widget-actions-head,
.dashboard-widget .widget-table .widget-actions-cell {
    /* Action column needs a min-width to comfortably hold the two
       button labels ("Return Request", "Update Order Status") on
       stacked rows. Without this, the column collapses and the
       buttons wrap awkwardly. */
    min-width: 140px;
    vertical-align: middle;
}

.dashboard-widget .widget-table .widget-action-form {
    /* Each form is a single button on its own row. Stack them
       vertically with a small gap. */
    display: block;
    margin: 2px 0;
}
.dashboard-widget .widget-table .widget-action-form:first-child {
    margin-top: 0;
}
.dashboard-widget .widget-table .widget-action-form:last-child {
    margin-bottom: 0;
}

.dashboard-widget .widget-table .widget-action-btn {
    /* Match the existing ``.btn`` + ``.btn-sm`` look but with a
       fixed width so the two buttons in the same cell align. */
    width: 100%;
    font-size: 0.78rem;
    padding: 4px 8px;
    line-height: 1.2;
    text-align: center;
    white-space: normal;  /* allow the label to wrap if it's long */
}

/* Distinct colours for the two action types so admins can scan
   the widget quickly. Return Request is amber (caution), Update
   Status is the default brand color. Using the existing --color-*-
   tokens so a future theme tweak propagates automatically. */
.dashboard-widget .widget-action-btn.widget-action-return_request {
    background: rgba(245, 158, 11, 0.15);  /* amber-500 @ 15% */
    border-color: rgba(245, 158, 11, 0.45);
    color: rgb(245, 158, 11);
}
.dashboard-widget .widget-action-btn.widget-action-return_request:hover {
    background: rgba(245, 158, 11, 0.25);
}
.dashboard-widget .widget-action-btn.widget-action-update_status {
    /* Inherits .btn .btn-primary look — no overrides needed. */
}

/* Mobile card view: the action forms become full-width buttons
   inside each card. Stacks below the data-label/value rows because
   the template renders them last. */
@media (max-width: 720px) {
    .dashboard-widget .widget-table td.widget-actions-cell {
        /* Same borderless treatment as the other cells in card view. */
        padding-top: 6px;
    }
    .dashboard-widget .widget-table .widget-action-btn {
        /* Touch-friendly tap target on mobile. */
        padding: 6px 10px;
        font-size: 0.85rem;
    }
    .dashboard-widget .widget-table th.widget-actions-head {
        /* The mobile card layout hides <thead>; the Action label
           still gets the "card subheading" treatment via td::before
           because the cell carries a data-label via the macro. */
        display: none;
    }
}

/* Update Status form (dashboard/update_status.html) — narrow
   single-column form, similar to /admin/editEcutoutLibrary. */
.update-status-form label {
    display: block;
    margin-bottom: var(--space-md);
}
.update-status-form select {
    width: 100%;
    padding: 0.5rem 0.75rem;
    background: var(--color-bg);
    color: var(--color-fg);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    font: inherit;
}
.update-status-form .form-actions {
    display: flex;
    gap: var(--space-sm);
    margin-top: var(--space-lg);
}
.update-status-form .info,
.update-status-form p.info {
    /* Used on return_request.html as a neutral info banner. */
    background: rgba(110, 168, 254, 0.10);
    border: 1px solid rgba(110, 168, 254, 0.30);
    border-radius: var(--radius-sm);
    padding: var(--space-sm) var(--space-md);
    color: var(--color-fg);
}

/* ============== Dashboard v2 (2026-08-31 redesign) ==============
   Replaces the 14-widget tile grid with a single vertical flow:
   greeting → 4 KPI cards → pill bar → unified orders table.
   Reference image: dark theme, cyan accent (#22d3ee-ish), generous
   padding, rounded corners. Reference design had a slight gradient
   on KPI cards — keep that for visual punch.
   --------------------------------------------------------------- */

.dashboard-page {
    padding: var(--space-md) 0 var(--space-xl);
}

.dashboard-greeting-block {
    margin-bottom: var(--space-xl);
}
.dashboard-greeting-block .dashboard-greeting {
    font-size: 1.75rem;
    font-weight: 600;
    margin: 0 0 4px;
    color: var(--color-fg);
}
.dashboard-greeting-block .dashboard-intro {
    margin: 0;
    color: var(--color-fg-muted);
    font-size: 0.95rem;
}

/* --- KPI cards row ---------------------------------------------- */
.kpi-row {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: var(--space-md);
    margin-bottom: var(--space-xl);
}
@media (max-width: 720px) {
    .kpi-row { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

.kpi-card {
    /* Slight gradient for visual punch (matches the reference image).
       Falls back to the same dark-card background if gradient isn't
       supported. */
    background:
        linear-gradient(135deg, rgba(34, 211, 238, 0.06) 0%, transparent 60%),
        var(--color-card);
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    padding: var(--space-md) var(--space-lg);
    display: flex;
    flex-direction: column;
    gap: 4px;
    position: relative;
    /* Subtle hover lift so the cards feel clickable / interactive
       (the v1 widget tiles had no hover effect at all). */
    transition: transform 120ms ease, border-color 120ms ease;
}
.kpi-card:hover {
    transform: translateY(-1px);
    border-color: rgba(34, 211, 238, 0.30);
}
.kpi-card::before {
    /* 3px left accent strip — color driven by data-accent on the
       card. Lets the user scan the four KPIs and read the
       operational state at a glance (in-progress = amber, ready =
       green, etc). Falls through to a neutral cyan if the config
       doesn't specify an accent. */
    content: "";
    position: absolute;
    left: 0; top: 0; bottom: 0;
    width: 3px;
    background: var(--color-accent);
    border-radius: var(--radius) 0 0 var(--radius);
}
.kpi-card[data-accent="cyan"]::before   { background: var(--color-accent); }
.kpi-card[data-accent="amber"]::before  { background: #fbbf24; }
.kpi-card[data-accent="green"]::before  { background: #34d399; }
.kpi-card[data-accent="red"]::before    { background: #f87171; }
.kpi-card[data-accent="purple"]::before { background: #a78bfa; }
.kpi-card[data-accent="grey"]::before   { background: #64748b; }
.kpi-card .kpi-label {
    font-size: 0.75rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--color-fg-muted);
}
.kpi-card .kpi-value {
    font-size: 2rem;
    font-weight: 700;
    color: var(--color-fg);
    /* Tabular nums so the digits don't jump width as values change
       (e.g. 9 → 10 → 100). */
    font-variant-numeric: tabular-nums;
    line-height: 1.1;
}
.kpi-card .kpi-delta {
    /* Small secondary line under the value. Set by the route when
       the KPI config opts in via ``delta: new_this_week`` (or
       another supported delta kind in future). */
    font-size: 0.72rem;
    color: var(--color-fg-dim);
    margin-top: 2px;
    font-weight: 500;
}

/* --- Pill bar ---------------------------------------------------- */
.pill-bar {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-sm);
    margin-bottom: var(--space-md);
    padding-bottom: var(--space-md);
    border-bottom: 1px solid var(--color-border);
}

.pill {
    /* Inactive pill: dark, rounded, subtle border, mono-caps label.
       Active pill: cyan accent fill (matches the reference image). */
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 6px 14px;
    background: var(--color-card);
    border: 1px solid var(--color-border);
    border-radius: 999px;
    color: var(--color-fg);
    text-decoration: none;
    font-size: 0.85rem;
    font-weight: 500;
    /* Subtle transition so hover/click feels responsive. */
    transition: background 120ms ease, color 120ms ease, border-color 120ms ease;
}
.pill:hover {
    background: var(--color-bg-elev);
    border-color: rgba(34, 211, 238, 0.30);
}
.pill--active {
    /* Dim cyan fill (alpha 0.15) with cyan text — refined over the
       prior full-cyan "screaming" fill. Still clearly "active" but
       no longer competes with row content for visual weight. */
    background: var(--color-accent-dim, rgba(56, 189, 248, 0.15));
    color: var(--color-accent, #38bdf8);
    border-color: var(--color-accent, #38bdf8);
    font-weight: 600;
}
.pill-count {
    /* Pill count badge — slightly different style in active vs
       inactive states so the contrast stays high. */
    font-size: 0.75rem;
    font-weight: 600;
    padding: 0 6px;
    min-width: 18px;
    text-align: center;
    border-radius: 999px;
    background: rgba(0, 0, 0, 0.20);
    color: inherit;
}
.pill:not(.pill--active) .pill-count {
    background: rgba(255, 255, 255, 0.08);
    color: var(--color-fg-muted);
}
.pill--active .pill-count {
    /* Match the dim cyan tint so the count badge sits inside the
       active pill cleanly. The default dark overlay (set on
       .pill-count) would fight the cyan background — replace it
       with a subtle cyan overlay. */
    background: rgba(56, 189, 248, 0.18);
    color: var(--color-accent, #38bdf8);
}

/* --- Orders section header + table ----------------------------- */
.orders-section {
    /* The orders table is large; give it its own background + border
       to make it feel like the "main content" of the page. */
    background: var(--color-card);
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    padding: var(--space-md) var(--space-lg);
}
.orders-section-header {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: var(--space-sm);
    margin-bottom: var(--space-md);
}
.orders-section-title {
    margin: 0;
    font-size: 1.05rem;
    font-weight: 600;
    color: var(--color-fg);
    display: inline-flex;
    align-items: center;
    gap: var(--space-sm);
}
.orders-section-title code {
    font-family: var(--font-mono);
    font-size: 0.85rem;
    padding: 2px 6px;
    background: rgba(34, 211, 238, 0.10);
    border: 1px solid rgba(34, 211, 238, 0.30);
    border-radius: var(--radius-sm);
    color: var(--color-accent, #22d3ee);
}
.orders-section-count {
    color: var(--color-fg-muted);
    font-size: 0.85rem;
    font-weight: 400;
}
.orders-section-meta {
    margin: 0;
    color: var(--color-fg-muted);
    font-size: 0.8rem;
}

.orders-empty {
    color: var(--color-fg-muted);
    text-align: center;
    padding: var(--space-xl) 0;
    font-size: 0.95rem;
}

/* Orders table reuses the .orders-table / .sortable-table /
   .sort-header-btn / .sort-arrow styles from the User List commit
   (admin/users.html). Just add a few refinements specific to the
   dashboard table: an inline eCutout thumbnail, a product-type
   pill, and the per-row action column. */

.orders-table .ecutout-thumb {
    /* Inline under the order number for evaMed rows. Capped at 60px
       so it doesn't push the row height (the v1 widget tile had 90px,
       too large for the unified table at 100 rows per page). */
    display: block;
    margin-top: 4px;
    max-width: 60px;
    height: auto;
    border-radius: var(--radius-sm);
    border: 1px solid var(--color-border);
    background: var(--color-bg);
}

.orders-table .product-pill {
    /* Small product-type label inside its cell — same visual idiom
       as the .status pill in app/util/text.py. */
    display: inline-block;
    padding: 1px 8px;
    border-radius: 999px;
    font-size: 0.75rem;
    font-weight: 500;
    background: var(--color-bg-elev);
    color: var(--color-fg-muted);
    border: 1px solid var(--color-border);
    white-space: nowrap;
}

.orders-table .orders-actions-cell {
    min-width: 140px;
    vertical-align: middle;
}
.orders-table .orders-action-form {
    display: block;
    margin: 2px 0;
}
.orders-table .orders-action-btn {
    width: 100%;
    font-size: 0.78rem;
    padding: 4px 8px;
    line-height: 1.2;
    text-align: center;
    white-space: normal;
}
.orders-table .orders-action-btn.orders-action-update_status {
    /* Cyan tone — matches the status-badge cyan + the Total Orders
       KPI accent strip. One palette across the whole dashboard. */
    background: rgba(56, 189, 248, 0.10);
    border-color: rgba(56, 189, 248, 0.35);
    color: #38bdf8;
}
.orders-table .orders-action-btn.orders-action-update_status:hover {
    background: rgba(56, 189, 248, 0.18);
}
.orders-table .orders-action-btn.orders-action-return_request {
    /* Amber tone — same as the status-badge amber. Return request
       is a "this needs attention" action, not an error. */
    background: rgba(251, 191, 36, 0.10);
    border-color: rgba(251, 191, 36, 0.35);
    color: #fbbf24;
}
.orders-table .orders-action-btn.orders-action-return_request:hover {
    background: rgba(251, 191, 36, 0.18);
}

/* On narrow viewports the table can scroll horizontally inside its
   wrapper rather than reflowing to a card pile (we have action
   buttons + 6 data columns; reflowing would hide the action). */
.orders-wrap {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
}

/* Row visibility refinements: subtle alpha-overlay stripe on even
   rows for scannability, plus a cyan-tinted hover state. Matches
   the .orders / .widget-table alpha-overlay technique so the
   long orders list (up to 100 rows/page) stays easy to scan. */
.orders-table tbody tr {
    transition: background-color 120ms ease;
}
.orders-table tbody tr:nth-child(even) {
    /* 0.04 alpha — visible enough to alternate at a glance while
       still subtle enough that the cyan hover (0.06) wins visually
       when the cursor is over a row. The prior 0.02 was technically
       correct but too faint to actually help differentiate rows
       (Cosmo 2026-09-01 feedback: 'should alternate background
       shading to help with visual differentiation'). */
    background: rgba(255, 255, 255, 0.04);
}
.orders-table tbody tr:hover {
    /* Cyan tint on hover — subtle but makes the click target
       discoverable and the table feel responsive. Stronger than
       the stripe (0.06 vs 0.04 alpha) so hover wins visually. */
    background: rgba(56, 189, 248, 0.06);
}

/* Column header sizing — explicit font-size + line-height so every
   column header (sortable buttons + the plain 'Action' th) lands on
   the same metrics regardless of inherited body font. The .orders
   (user-list) table has its own .orders th rule with font-size
   0.8rem + mono + uppercase; the dashboard uses sans-serif title
   case at 0.85rem so it's slightly more readable. */
.orders-table th {
    font-size: 0.85rem;
    line-height: 1.3;
    font-weight: 600;
}

/* ============== Test certificate (Phase 2 + Phase 3, 2026-09-02) ==============
   The edit form (Phase 2) and read-only view + history (Phase 3) use a
   shared set of CSS classes (.cert-block, .cert-info, .cert-spec, etc.)
   modelled on the existing .form-page / .orders-table patterns. The
   visual language matches the status-badge palette (cyan/amber/purple/
   green/red/grey) so a cert's status banner reads consistently with
   order-status badges elsewhere in the portal. */

/* ----- Status banner (top of edit + view pages) -----
   Left-border accent + tinted background, one variant per cert status.
   Draft: amber (work in progress). Published: green (locked for client).
   Void: red (kept for audit, excluded from client view). Empty: grey
   (no revisions yet — staff get a link to the editor). */
.cert-status-banner {
    display: block;
    padding: 0.75rem 1rem;
    margin: 1rem 0 1.5rem 0;
    border-radius: 6px;
    border-left: 4px solid var(--color-border);
    background: rgba(255, 255, 255, 0.03);
    color: var(--color-fg);
    font-size: 0.9rem;
    line-height: 1.4;
}
.cert-status-banner.status-draft {
    background: rgba(251, 191, 36, 0.08);
    border-left-color: #fbbf24;
    color: #fde68a;
}
.cert-status-banner.status-published {
    background: rgba(52, 211, 153, 0.08);
    border-left-color: #34d399;
    color: #a7f3d0;
}
.cert-status-banner.status-void {
    background: rgba(248, 113, 113, 0.08);
    border-left-color: #f87171;
    color: #fecaca;
}
.cert-status-banner.status-empty {
    background: rgba(160, 160, 160, 0.06);
    border-left-color: var(--color-fg-muted);
    color: var(--color-fg-muted);
}

/* ----- Cert block (one of the five sections) -----
   Sits below the status banner, separated by 1.5rem of vertical
   breathing room. h2 is the block heading; the contents are a mix
   of <dl> + <table> + freeform text depending on the block. */
.cert-block {
    margin: 1.5rem 0;
}
.cert-block > h2 {
    font-size: 1rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--color-fg-muted);
    margin-bottom: 0.75rem;
    padding-bottom: 0.25rem;
    border-bottom: 1px solid var(--color-border);
}
.cert-block > h3 {
    font-size: 0.85rem;
    font-weight: 600;
    color: var(--color-fg);
    margin: 1rem 0 0.5rem 0;
}

/* ----- Definition-list fields (label above, value below) -----
   Used for the dense block content (bolus type, STL file, printer,
   etc.). dt is uppercase muted; dd is the value. On narrow widths
   the dd still has a small left indent so the value reads as a
   child of the label without floating too far left. */
.cert-info {
    display: grid;
    grid-template-columns: max-content 1fr;
    column-gap: 1.5rem;
    row-gap: 0.5rem;
    margin: 0;
}
.cert-info dt {
    font-size: 0.75rem;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--color-fg-muted);
    padding-top: 0.15rem;  /* optical alignment with dd baseline */
}
.cert-info dd {
    margin: 0;
    color: var(--color-fg);
    font-size: 0.9rem;
    word-break: break-word;  /* long values wrap rather than overflow */
}
.cert-info ul.cert-batches {
    margin: 0;
    padding-left: 1.25rem;
}
.cert-info ul.cert-batches li {
    font-size: 0.9rem;
}

/* ----- Spec tables (Dimensions, Silicone Mass) -----
   Tabular layout for the four columns (Expected / Min / Max / Actual).
   Header row gets a muted background so the column labels read even
   when the values are uniform; the first column (X/Y/Z or empty) is
   slightly narrower so the numbers have room. */
.cert-spec {
    width: 100%;
    border-collapse: collapse;
    margin: 0.5rem 0 1rem 0;
    font-size: 0.9rem;
}
.cert-spec th,
.cert-spec td {
    padding: 0.4rem 0.75rem;
    border-bottom: 1px solid var(--color-border);
    text-align: right;
}
.cert-spec thead th {
    background: rgba(255, 255, 255, 0.04);
    color: var(--color-fg-muted);
    font-size: 0.75rem;
    font-weight: 500;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    border-bottom: 1px solid var(--color-border-strong);
}
.cert-spec tbody th {
    text-align: left;
    font-weight: 500;
    color: var(--color-fg);
    background: transparent;
    width: 6rem;  /* narrow first column so numbers have room */
}

/* ----- Unpacking / Handling block (boilerplate from config.yaml) -----
   Sans-serif body (NOT monospaced — the legacy PDF's Courier-style
   font was a print-only artefact; on screen a monospace block looks
   like an error). Soft background + left border to distinguish the
   read-only block from editable sections above. */
.cert-boilerplate pre.boilerplate {
    background: rgba(255, 255, 255, 0.02);
    border: 1px solid var(--color-border);
    border-left: 3px solid var(--color-border-strong);
    border-radius: 4px;
    padding: 0.75rem 1rem;
    margin: 0;
    color: var(--color-fg);
    font-family: inherit;  /* override cosmo's default monospace for pre */
    font-size: 0.9rem;
    line-height: 1.5;
    white-space: pre-wrap;
}

/* ----- Empty-state panel (no published cert yet) ----- */
.cert-empty-state {
    margin: 2rem 0;
    padding: 1.5rem;
    border: 1px dashed var(--color-border);
    border-radius: 6px;
    background: rgba(255, 255, 255, 0.02);
    color: var(--color-fg-muted);
}
.cert-empty-state h2 {
    font-size: 1rem;
    font-weight: 600;
    color: var(--color-fg);
    margin: 0 0 0.5rem 0;
}
.cert-empty-state p {
    margin: 0.5rem 0;
    color: var(--color-fg);
}

/* ----- Footer nav (shared across view + history + edit) ----- */
.cert-footer-nav {
    margin: 2rem 0 1rem 0;
    padding-top: 1rem;
    border-top: 1px solid var(--color-border);
    color: var(--color-fg-muted);
    font-size: 0.85rem;
}

/* ----- Edit form-specific (Phase 2; .cert-form + .cert-actions) -----
   The form-grid mirrors the bolus form's responsive grid so the
   23 cert fields land on a sensible column count at each viewport.
   Save-draft is the secondary action; save-and-publish is primary. */
.cert-form .form-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
    gap: 0.75rem 1rem;
    margin: 0.5rem 0 1rem 0;
}
.cert-form .form-grid label {
    display: flex;
    flex-direction: column;
    font-size: 0.75rem;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--color-fg-muted);
    gap: 0.25rem;
}
.cert-form .form-grid input,
.cert-form .form-grid select {
    font-size: 0.9rem;
    color: var(--color-fg);
    background: var(--color-bg-elev);
    border: 1px solid var(--color-border);
    border-radius: 4px;
    padding: 0.4rem 0.6rem;
}
.cert-actions {
    margin: 1.5rem 0 0.5rem 0;
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    align-items: center;
}
.cert-void-reason {
    margin: 0.5rem 0;
    color: var(--color-fg-muted);
    font-size: 0.85rem;
}
.cert-void-reason summary {
    cursor: pointer;
    padding: 0.25rem 0;
}
.cert-void-reason input {
    margin-top: 0.25rem;
    width: min(400px, 100%);
    background: var(--color-bg-elev);
    border: 1px solid var(--color-border);
    border-radius: 4px;
    padding: 0.4rem 0.6rem;
    color: var(--color-fg);
}

/* ----- History table + status pill (Phase 3 admin page) ----- */
.cert-history-table {
    width: 100%;
    border-collapse: collapse;
    margin: 1rem 0;
    font-size: 0.85rem;
}
.cert-history-table th,
.cert-history-table td {
    padding: 0.5rem 0.75rem;
    border-bottom: 1px solid var(--color-border);
    text-align: left;
    vertical-align: top;
}
.cert-history-table thead th {
    background: rgba(255, 255, 255, 0.04);
    color: var(--color-fg-muted);
    font-size: 0.7rem;
    font-weight: 500;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    border-bottom: 1px solid var(--color-border-strong);
}
.cert-history-table tbody tr:hover {
    background: rgba(56, 189, 248, 0.04);
}

/* Cert status pill — modelled on the .status badge in the dashboard.
   Three variants (draft / published / void) matching the status banner
   palette so a status banner and a status pill on the history page
   read as the same operational state. */
.cert-status-pill {
    display: inline-block;
    padding: 1px 8px;
    border-radius: 999px;
    font-size: 0.75rem;
    font-weight: 500;
    border: 1px solid var(--color-border);
    white-space: nowrap;
}
.cert-status-pill.status-draft {
    background: rgba(251, 191, 36, 0.10);
    border-color: rgba(251, 191, 36, 0.30);
    color: #fde68a;
}
.cert-status-pill.status-published {
    background: rgba(52, 211, 153, 0.10);
    border-color: rgba(52, 211, 153, 0.30);
    color: #a7f3d0;
}
.cert-status-pill.status-void {
    background: rgba(248, 113, 113, 0.10);
    border-color: rgba(248, 113, 113, 0.30);
    color: #fecaca;
}

/* ----- Mobile responsive (lesson 77 follow-up) -----
   At narrow viewports the spec tables and history table are wrapped
   in a horizontally-scrollable container rather than reflowing to
   cards. The cert-info grid also drops to a single column. */
@media (max-width: 720px) {
    .cert-info {
        grid-template-columns: 1fr;
        row-gap: 0.25rem;
    }
    .cert-info dd {
        padding-bottom: 0.25rem;
        border-bottom: 1px solid var(--color-border);
    }
    .cert-history-table {
        font-size: 0.8rem;
    }
    .cert-history-table th,
    .cert-history-table td {
        padding: 0.4rem 0.5rem;
    }
}

/* ============== Order summary page (Phase 4, Cosmo 2026-09-02) ==============
   Page 1 of the 5-page plan. Reuses the existing .kpi-card style (already
   used by the dashboard's KPI strip — same left-accent + gradient + hover
   lift). The .cert-panel is a thin wrapper that adds the in-card banner,
   subtitle line, and CTA button row. All tone variants ride the same
   6-tone palette so a cert-published-green panel reads as the same
   operational state as a green order-status badge elsewhere in the portal. */

.order-summary .order-summary-header {
    margin: 0 0 var(--space-md) 0;
}
.order-summary .order-summary-header h1 {
    font-size: 1.4rem;
    font-weight: 600;
    margin: 0 0 0.5rem 0;
    color: var(--color-fg);
}
.order-summary .order-summary-header h1 code {
    color: var(--color-cyan);
    font-size: 1.1rem;
}

/* kpi-card heading — uppercase muted label inside each card. */
.kpi-card-heading {
    font-size: 0.75rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--color-fg-muted);
    margin: 0 0 0.75rem 0;
    padding: 0;
    border: 0;
}

/* Cert panel — extends .kpi-card with banner + CTA. */
.cert-panel {
    /* Reuse the .kpi-card gradient + border. The data-accent on
       the article drives the left accent strip via .kpi-card::before. */
    margin-top: var(--space-md);
}
.cert-panel-banner {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.5rem;
    padding: 0.5rem 0.75rem;
    border-radius: 6px;
    margin-bottom: 0.75rem;
    font-size: 0.95rem;
    line-height: 1.4;
}
.cert-panel-banner strong {
    font-weight: 600;
}
.cert-panel-subtitle {
    color: var(--color-fg-muted);
    font-size: 0.85rem;
}
.cert-panel-banner.status-published {
    background: rgba(52, 211, 153, 0.10);
    color: #a7f3d0;
}
.cert-panel-banner.status-published strong {
    color: #6ee7b7;
}
.cert-panel-banner.status-draft {
    background: rgba(251, 191, 36, 0.10);
    color: #fde68a;
}
.cert-panel-banner.status-draft strong {
    color: #fcd34d;
}
.cert-panel-banner.status-empty {
    background: rgba(160, 160, 160, 0.06);
    color: var(--color-fg-muted);
}
.cert-panel-banner.status-empty strong {
    color: var(--color-fg);
}

/* CTA button row inside the cert panel. */
.cert-panel-cta {
    /* The .kpi-card parent is display:flex flex-direction:column.
       Without align-self, the CTA gets blockified to a flex item and
       stretches to fill the cross-axis (full card width). flex-start
       pins it to its content width on the left edge of the card. */
    display: inline-block;
    align-self: flex-start;
    margin-top: 0.5rem;
}

/* ----- Order link (dashboard / search results order_number anchor) -----
   Phase 4 (Cosmo 2026-09-02). The order_number cell on the dashboard
   and the search results now wraps in <a class="order-link">. Keep the
   <code> rendering as-is (cyan accent fits the brand); just neutralise
   the default underline + set the cursor so the cell reads as clickable.
   The dashboard's row hover already tints the background, so the anchor
   doesn't need its own hover treatment. */
.order-link {
    color: inherit;
    text-decoration: none;
}
.order-link:hover code {
    color: var(--color-cyan);
    text-decoration: underline;
}

/* ============== Pagination component (Cosmo 2026-09-02 23:37) ==============
   The components/pagination.html macro renders:
     <nav class="pagination">
       <a class="page-link page-link--prev">Previous</a>
       <ol class="page-numbers">
         <li><a class="page-link">1</a></li>
         ...
         <li><span class="page-link page-link--current">5</span></li>
       </ol>
       <a class="page-link page-link--next">Next</a>
     </nav>

   Modern UX best practices baked in:
   - Horizontal layout (the macro renders an <ol> which defaults to
     vertical block; .page-numbers + .page-link flexbox + gap fix it)
   - Current page: filled brand-cyan background, white text, no link
   - Hover: subtle tint on every non-current page link
   - Focus-visible: cyan ring so keyboard users can see where they are
   - First/last page collapse to a single "1" / "N" when window fits
   - Prev/Next hidden via [hidden] on edges (no render at all)
   - Touch targets: 36x36 minimum hit area, larger tap-friendly padding
   - Ellipsis: muted color, no hover, doesn't break flow
   - Responsive: at <=480px the prev/next collapse to icon-only
   - Reduced-motion friendly (no transitions on transform/opacity) */

nav.pagination {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    margin: 2rem 0 1rem;
    padding: 0.75rem 0;
    flex-wrap: wrap;
}

ol.page-numbers {
    display: flex;
    align-items: center;
    gap: 0.25rem;
    margin: 0;
    padding: 0;
    list-style: none;
    flex-wrap: wrap;
}

ol.page-numbers li {
    display: flex;
    align-items: center;
    justify-content: center;
    margin: 0;
}

/* Page number link (and prev/next) — base style. */
.page-link {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 36px;
    min-height: 36px;
    padding: 0.4rem 0.75rem;
    border-radius: 6px;
    border: 1px solid var(--color-border);
    background: var(--color-card);
    color: var(--color-fg);
    font-size: 0.9rem;
    font-weight: 500;
    text-decoration: none;
    cursor: pointer;
    transition: background-color 120ms ease, border-color 120ms ease, color 120ms ease;
    /* Honor user motion preference */
}

.page-link:hover {
    background: rgba(56, 189, 248, 0.10);
    border-color: rgba(56, 189, 248, 0.45);
    color: #38bdf8;
    text-decoration: none;
}

.page-link:focus-visible {
    outline: 2px solid var(--color-cyan);
    outline-offset: 2px;
}

/* Current page — non-interactive <span>. */
.page-link--current {
    background: var(--color-accent);
    border-color: var(--color-accent);
    color: #0a1020;
    font-weight: 600;
    cursor: default;
}

.page-link--current:hover {
    /* Explicitly no hover on current page — it doesn't navigate */
    background: var(--color-accent);
    border-color: var(--color-accent);
    color: #0a1020;
}

/* Ellipsis — non-interactive, muted. */
.page-ellipsis {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 36px;
    min-height: 36px;
    color: var(--color-fg-muted);
    user-select: none;
}

/* Prev/Next — slightly wider hit target than page numbers. */
.page-link--prev,
.page-link--next {
    padding: 0.4rem 0.9rem;
    font-weight: 500;
    /* Use the same colour as regular page-link but with subtle
       emphasis via a left/right chevron-like inset border on the
       edge variants. (Could use ::before for an arrow, but keeping
       text-only keeps it accessible without font icons.) */
}

.page-link--prev {
    margin-right: 0.25rem;
}

.page-link--next {
    margin-left: 0.25rem;
}

/* Mobile — collapse Prev/Next to icon-like text, hide ellipsis if
   window is short. At <=480px the chevrons still read because we
   keep the word — better a11y than collapsing to <. */
@media (max-width: 480px) {
    .page-link--prev,
    .page-link--next {
        padding: 0.4rem 0.5rem;
        font-size: 0.8rem;
    }
    ol.page-numbers {
        gap: 0.15rem;
    }
}

/* ============== Print stylesheet (Cosmo 2026-09-02 23:46) ==============
   The cert document prints cleanly from the browser (white background,
   black text, controlled-document feel). Applies to:
     - /orders/{pt}/{n}/test-certificate             (client view)
     - /orders/{pt}/{n}/test-certificate/edit        (operator form)
     - /orders/{pt}/{n}/test-certificate/revision/{n} (per-revision)
     - /orders/{pt}/{n}/test-certificate/history     (admin audit page)
     - /orders/{pt}/{n}/manufacturing                 (mfg form)

   Print rules are scoped under the page's section so other pages
   (dashboard, search, order summary) keep their on-screen styles.

   Browser print conventions baked in:
     - Force white bg + dark text (Chrome saves backgrounds by
       default but not always; -webkit-print-color-adjust:exact
       is required to ensure colour fidelity on the page itself)
     - Hide nav, footer, action buttons, back-to-summary links
     - Avoid page-break-inside on each block so dimensions table
       doesn't split across pages
     - Show URLs in parentheses after links so printed copies are
       traceable (the cert is a controlled document)
     - Page-break-before: always on the .cert-block h2 so each
       block lands on its own page when printing multi-block docs.
   Limit the rule scope to .test-cert-view / .manufacturing-edit
   so other pages don't accidentally inherit. */

@media print {
    @page {
        size: A4;
        margin: 12mm 14mm;
    }

    /* Scope to the cert + manufacturing pages only. */
    .test-cert-view,
    .test-cert-edit,
    .manufacturing-edit {
        background: #fff !important;
        color: #000 !important;
        font-size: 10pt;
        line-height: 1.4;
    }

    /* Force colours to render in print (Chromium defaults to
       "economy" which strips background colours and greys out
       dark-on-dark; this overrides it). */
    .test-cert-view *,
    .test-cert-edit *,
    .manufacturing-edit * {
        -webkit-print-color-adjust: exact !important;
        print-color-adjust: exact !important;
        color-adjust: exact !important;
        box-shadow: none !important;
        text-shadow: none !important;
    }

    /* Hide site chrome and action controls. Note: the .site-header /
       .site-footer live OUTSIDE the page's <section> wrapper, so
       we can't scope the hide under .test-cert-view. Use the
       page-specific body class instead. We set .printing-cert on
       <body> in the page templates via a small Jinja hook. */
    body.printing-cert .site-header,
    body.printing-cert .site-footer,
    body.printing-cert .cert-footer-nav,
    body.printing-cert .cert-actions,
    body.printing-cert .cert-void-reason,
    body.printing-cert .test-cert-history,
    body.printing-cert .breadcrumb,
    body.printing-cert nav.pagination,
    body.printing-cert .pill-bar,
    body.printing-cert .kpi-row,
    body.printing-cert .navbar {
        display: none !important;
    }
    /* Backwards compat: also hide on the page-scoped selectors in
       case the body class isn't set. */
    body.printing-cert .site-header,
    body.printing-cert .site-footer,
    .test-cert-edit .site-header,
    .test-cert-edit .site-footer,
    .manufacturing-edit .site-header,
    .manufacturing-edit .site-footer {
        display: none !important;
    }
    body.printing-cert .cert-footer-nav,
    body.printing-cert .cert-actions,
    body.printing-cert .cert-void-reason,
    .test-cert-edit .cert-footer-nav,
    .test-cert-edit .cert-actions,
    .test-cert-edit .cert-void-reason,
    .manufacturing-edit .cert-footer-nav,
    .manufacturing-edit .cert-actions,
    .manufacturing-edit .cert-void-reason,
    .test-cert-history,
    .breadcrumb,
    nav.pagination,
    .pill-bar,
    .kpi-row,
    .navbar,
    .footer {
        display: none !important;
    }

    /* Headings — print size + black. */
    .test-cert-view h1,
    .test-cert-view h2,
    .test-cert-view h3,
    .test-cert-edit h1,
    .test-cert-edit h2,
    .test-cert-edit h3,
    .manufacturing-edit h1,
    .manufacturing-edit h2,
    .manufacturing-edit h3 {
        color: #000 !important;
        page-break-after: avoid;
        page-break-inside: avoid;
        border-bottom: 1px solid #000 !important;
    }

    .test-cert-view h1,
    .test-cert-edit h1,
    .manufacturing-edit h1 {
        font-size: 14pt;
        margin-top: 0;
    }

    /* Status banners — keep the colour as a printed border so the
       reader sees draft/published/void at a glance without needing
       the screen view. */
    .cert-status-banner {
        background: #fff !important;
        border: 1px solid #000 !important;
        border-left-width: 4px !important;
        color: #000 !important;
        padding: 4pt 6pt !important;
        margin: 6pt 0 !important;
        page-break-inside: avoid;
    }
    .cert-status-banner.status-published { border-left-color: #047857 !important; }
    .cert-status-banner.status-draft     { border-left-color: #b45309 !important; }
    .cert-status-banner.status-void      { border-left-color: #b91c1c !important; }
    .cert-status-banner.status-empty     { border-left-color: #525252 !important; }
    .cert-status-banner.status-info      { border-left-color: #1d4ed8 !important; }

    /* kpi-card → plain bordered block, no gradient/border-radius
       (border-radius wastes ink on real printers). */
    .kpi-card {
        background: #fff !important;
        color: #000 !important;
        border: 1px solid #000 !important;
        border-radius: 0 !important;
        box-shadow: none !important;
        padding: 6pt 8pt !important;
        margin: 6pt 0 !important;
        page-break-inside: avoid;
    }

    /* Definition list — label/value readable in B&W. */
    .cert-info dt {
        color: #000 !important;
        font-weight: 600;
        text-transform: none;
        letter-spacing: 0;
    }
    .cert-info dd {
        color: #000 !important;
    }

    /* Tables — black borders, no zebra stripes. */
    .cert-spec {
        background: #fff !important;
        color: #000 !important;
        border-collapse: collapse;
        page-break-inside: avoid;
    }
    .cert-spec thead th {
        background: #e5e5e5 !important;
        color: #000 !important;
        border-bottom: 2px solid #000 !important;
    }
    .cert-spec th,
    .cert-spec td {
        border: 1px solid #000 !important;
        color: #000 !important;
        padding: 2pt 4pt;
    }

    /* Boilerplate block — printed text legibility. */
    .cert-boilerplate pre.boilerplate {
        background: #fff !important;
        color: #000 !important;
        border: 1px solid #000 !important;
        padding: 4pt 6pt;
        font-family: inherit;
        font-size: 9pt;
        white-space: pre-wrap;
    }

    /* Form inputs — render as text, not boxes. The cert form has
       inputs the operator types into; printing should look like a
       filled-in form, not a stack of empty rectangles. We override
       the screen-side .cert-form .form-grid input rule (which has
       HIGHER specificity than .cert-form input alone) by matching
       it here too. !important wins regardless, but matching both
       keeps the rule readable. */
    .cert-form input,
    .cert-form select,
    .cert-form textarea,
    .cert-form .form-grid input,
    .cert-form .form-grid select,
    .cert-form .form-grid textarea,
    .cert-form .cert-spec input {
        border: none !important;
        background: transparent !important;
        color: #000 !important;
        padding: 0 !important;
        font-size: 10pt;
        box-shadow: none !important;
        outline: none !important;
        -webkit-appearance: none !important;
        appearance: none !important;
    }
    /* Native select arrow — hide it for print so the dropdown
       doesn't render as a tiny box. */
    .cert-form select {
        background-image: none !important;
    }
    .cert-form input[type="text"]:empty::before,
    .cert-form input[value=""]::before {
        content: "\00a0";  /* nbsp placeholder so empty rows render height */
    }
    /* Labels stay as labels (small caps + lighter weight is screen
       style; print needs them readable). */
    .cert-form label {
        color: #000 !important;
        text-transform: none;
        font-size: 9pt;
        letter-spacing: 0;
    }

    /* Pre-fill attribution banner — keep visible (audit-trail
       matters on paper). */
    .cert-status-banner.status-info {
        border-left-width: 4px !important;
        border-left-color: #1d4ed8 !important;
    }

    /* URLs after links — the cert is a controlled document so
       recipients should be able to trace the source. */
    a[href]:not(.btn)::after {
        content: " (" attr(href) ")";
        font-size: 8pt;
        color: #525252;
    }
}

/* ============== Test certificate document layout (Cosmo 2026-09-03) ==============
   Matches the legacy 3d1 portal's Silicone_310_TC PDF shape:
   - Single outer-bordered document table
   - Header row: logo (left) + title (centre) + product order # + date (right)
   - Two-column body: left = Order/Manufacturing/Notes/Instructions; right = Delivery/QC
   - Section headers: gray bar with centred bold text
   - Values in blue (data-dense clinical feel); product order # in red
   - Sans-serif throughout
   Applied to: .test-cert-view (the cert view page body). */

body.printing-cert .cert-document {
    border: 1px solid #000;
    border-collapse: collapse;
    width: 100%;
    margin: 0;
    background: #fff;
    color: #000;
    font-size: 0.85rem;
    line-height: 1.35;
}

/* ---- Header row ---- */
body.printing-cert .cert-header {
    display: grid;
    grid-template-columns: 1fr 2fr 1fr;
    align-items: center;
    border-bottom: 1px solid #000;
    padding: 0.5rem 0.75rem;
}
body.printing-cert .cert-header-logo img {
    max-height: 36px;
    max-width: 100%;
}
body.printing-cert .cert-header-title {
    text-align: center;
}
body.printing-cert .cert-header-title h1 {
    font-size: 1.4rem;
    font-weight: 700;
    margin: 0;
    color: #000;
    letter-spacing: 0.02em;
}
body.printing-cert .cert-header-meta {
    text-align: right;
    font-size: 0.8rem;
}
body.printing-cert .cert-header-meta-label {
    color: #6b7280;
    text-transform: uppercase;
    font-size: 0.7rem;
    letter-spacing: 0.05em;
}
body.printing-cert .cert-header-meta-order {
    color: #dc2626;       /* red — matches the legacy's bold-red product order number */
    font-weight: 700;
    font-size: 1rem;
    margin: 0.1rem 0;
}
body.printing-cert .cert-header-meta-date {
    color: #6b7280;
    font-size: 0.8rem;
}
body.printing-cert .cert-header-meta-rev {
    margin-top: 0.25rem;
    font-size: 0.75rem;
    color: #6b7280;
}
body.printing-cert .cert-header-meta-rev .status-published { color: #047857; font-weight: 600; }
body.printing-cert .cert-header-meta-rev .status-draft     { color: #b45309; font-weight: 600; }
body.printing-cert .cert-header-meta-rev .status-void      { color: #b91c1c; font-weight: 600; }

/* ---- Body: two columns ---- */
body.printing-cert .cert-body {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0;
}
body.printing-cert .cert-col {
    display: flex;
    flex-direction: column;
    gap: 0;
}
body.printing-cert .cert-col-left  { border-right: 1px solid #000; }
body.printing-cert .cert-col-right { /* no border — sits inside the outer table border */ }

/* ---- Section table (gray header, label/value rows) ---- */
body.printing-cert .cert-section {
    width: 100%;
    border-collapse: collapse;
    border-bottom: 1px solid #000;
}
body.printing-cert .cert-col-left  .cert-section { border-right: 1px solid #000; }
body.printing-cert .cert-col-right .cert-section { border-right: none; }
body.printing-cert .cert-section thead th {
    background: #e5e5e5;
    color: #000;
    font-weight: 700;
    text-align: center;
    padding: 0.3rem 0.5rem;
    border-bottom: 1px solid #000;
    font-size: 0.85rem;
}
body.printing-cert .cert-section tbody td {
    padding: 0.25rem 0.5rem;
    border-bottom: 1px solid #e5e5e5;
    vertical-align: top;
    color: #000;
}
body.printing-cert .cert-section tbody tr:last-child td {
    border-bottom: none;
}
body.printing-cert .cert-label {
    color: #6b7280;
    font-size: 0.78rem;
    width: 40%;
    vertical-align: top;
    padding-right: 0.5rem;
}
body.printing-cert .cert-value {
    color: #2563eb;       /* blue — the legacy's "data values" treatment */
    font-size: 0.85rem;
    word-break: break-word;
}
body.printing-cert .cert-label-inline {
    color: #6b7280;
    font-size: 0.78rem;
}

/* ---- QC sub-tables (Material batch / STL / Printer sub-table
       inside the QC section's left cell). ---- */
body.printing-cert .cert-qc-sub,
body.printing-cert .cert-qc-measurements {
    width: 100%;
    border-collapse: collapse;
}
body.printing-cert .cert-qc-sub td {
    padding: 0.15rem 0.4rem 0.15rem 0;
    border: none;
    color: #000;
    font-size: 0.78rem;
}
body.printing-cert .cert-qc-sub .cert-label { width: 50%; }
body.printing-cert .cert-qc-sub .cert-value { font-size: 0.78rem; }

/* ---- QC measurements (Expected / Min / Max / Actual) ---- */
body.printing-cert .cert-qc-measurements {
    border: 1px solid #000;
    margin-bottom: 0.4rem;
}
body.printing-cert .cert-qc-measurements thead th {
    background: #f3f4f6;
    color: #000;
    font-weight: 600;
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    text-align: center;
    padding: 0.2rem 0.3rem;
    border-bottom: 1px solid #000;
}
body.printing-cert .cert-qc-measurements tbody td {
    padding: 0.2rem 0.4rem;
    border-bottom: 1px solid #e5e5e5;
    text-align: center;
    color: #2563eb;
    font-size: 0.85rem;
}
body.printing-cert .cert-qc-measurements tbody tr:last-child td {
    border-bottom: none;
}
body.printing-cert .cert-qc-measurements .cert-meas-label {
    color: #6b7280;
    text-align: left;
    font-size: 0.78rem;
    font-weight: 600;
}

/* ---- Wide section (Unpacking and Handling, takes full column width
       without a 2-col label/value layout) ---- */
body.printing-cert .cert-section-wide thead th,
body.printing-cert .cert-section-wide tbody td {
    padding: 0.4rem 0.6rem;
}
body.printing-cert .cert-boilerplate-cell {
    color: #000 !important;
    padding: 0.5rem 0.6rem !important;
}
body.printing-cert .cert-boilerplate-cell pre.boilerplate {
    color: #000;
    background: transparent;
    border: none;
    padding: 0;
    margin: 0;
    font-family: inherit;
    font-size: 0.78rem;
    line-height: 1.45;
    white-space: pre-wrap;
}

/* ---- Material batch chip ---- */
body.printing-cert .cert-batch {
    color: #2563eb;
}

/* ---- Visual check chip (Pass / Fail) ---- */
body.printing-cert .cert-visual-pass { color: #047857; font-weight: 600; }
body.printing-cert .cert-visual-fail { color: #b91c1c; font-weight: 600; }

/* ---- Empty state ---- */
body.printing-cert .cert-empty-state {
    border: 1px dashed #9ca3af;
    padding: 1rem 1.25rem;
    color: #6b7280;
    background: #f9fafb;
}
body.printing-cert .cert-empty-state h2 {
    margin: 0 0 0.5rem 0;
    font-size: 1rem;
    color: #374151;
}

/* ---- Responsive: collapse to single column on narrow viewports ---- */
@media (max-width: 720px) {
    body.printing-cert .cert-header {
        grid-template-columns: 1fr;
        text-align: center;
    }
    body.printing-cert .cert-header-meta { text-align: center; }
    body.printing-cert .cert-body { grid-template-columns: 1fr; }
    body.printing-cert .cert-col-left  { border-right: none; border-bottom: 1px solid #000; }
}

/* ---- Print adjustments for the legacy layout ---- */
@media print {
    body.printing-cert .cert-document {
        /* Legacy 3d1 PDFs are typically printed single-page. Keep
           the document together as one print block where possible. */
        page-break-inside: avoid;
    }
    body.printing-cert .cert-section {
        page-break-inside: avoid;
    }
    body.printing-cert .cert-label,
    body.printing-cert .cert-label-inline,
    body.printing-cert .cert-header-meta-label {
        color: #000 !important;
    }
    body.printing-cert .cert-value,
    body.printing-cert .cert-batch,
    body.printing-cert .cert-qc-measurements tbody td {
        /* Legacy uses blue values, but on B&W print we want them
           readable. Keep the colour for the screen view via the
           non-print rule above; for print force black so the
           numbers don't wash out on a colour/B&W printer. */
        color: #000 !important;
    }
    body.printing-cert .cert-header-meta-order {
        /* Bold-black on print (red doesn't reproduce on B&W well). */
        color: #000 !important;
    }
}

/* ============== Cert view v2 polish (Cosmo 2026-09-03 "close but can do better") ==============
   Iterating the legacy 3d1 layout:
   - Right column no longer has dead space: added production timestamps
     (process / print / curaFileCheck / postProcess / qaFinal) to the
     QC bottom cell so the right column fills naturally when present.
   - Manufacturing Information section is now compact (Volume + batch +
     SOP + pigment) instead of duplicating setup fields.
   - Setup fields (Material Batch / Stl File / Printer Profile / Printer)
     consolidated into the QC section's left cell (legacy's structure).
   - "Volume of silicone = NN.NN" rendered as a big headline number so
     the Manufacturing Info section doesn't look em-dash-heavy.
   - Unpacking and Handling rendered with bold sub-headings (PPE / Opening /
     Storage / Cleaning) instead of plain paragraphs (legacy uses bold
     labels; we parse the boilerplate's colon-terminated lines).
   - Empty rows hidden via a Jinja `row` macro in the template — no more
     em-dashes for fields that legitimately have no value. */

body.printing-cert .cert-cell-stack {
    padding: 0.35rem 0.5rem;
    color: #000;
}
body.printing-cert .cert-cell-row {
    display: flex;
    align-items: baseline;
    gap: 0.25rem;
    padding: 0.1rem 0;
    font-size: 0.85rem;
    flex-wrap: wrap;
}
body.printing-cert .cert-cell-row .cert-label-inline {
    color: #6b7280;
    font-size: 0.78rem;
}
body.printing-cert .cert-cell-row .cert-value {
    color: #2563eb;
    font-size: 0.85rem;
}
body.printing-cert .cert-big-figure {
    font-size: 1.4rem;
    font-weight: 700;
    color: #2563eb;
    padding: 0.25rem 0 0.5rem 0;
    text-align: center;
    letter-spacing: 0.02em;
}
body.printing-cert .cert-big-figure strong {
    color: #1d4ed8;
}
body.printing-cert .cert-visual-pill {
    display: inline-block;
    padding: 0 0.5rem;
    border-radius: 999px;
    font-size: 0.78rem;
    font-weight: 600;
    border: 1px solid #000;
    margin-right: 0.25rem;
}
body.printing-cert .cert-visual-pass {
    background: #d1fae5;
    color: #047857;
    border-color: #047857;
}
body.printing-cert .cert-visual-fail {
    background: #fee2e2;
    color: #b91c1c;
    border-color: #b91c1c;
}

body.printing-cert .cert-boilerplate {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
}
body.printing-cert .cert-boilerplate-heading {
    font-size: 0.85rem;
    font-weight: 700;
    color: #000;
    margin: 0.4rem 0 0.15rem 0;
    text-transform: none;
    letter-spacing: 0;
    border-bottom: 1px dotted #6b7280;
    padding-bottom: 0.1rem;
}
body.printing-cert .cert-boilerplate-line {
    margin: 0;
    color: #000;
    font-size: 0.8rem;
    line-height: 1.4;
    padding-left: 0.5rem;
}

/* QC bottom cell — render the visual-check row + timestamps as a
   stacked column inside the QC section's bottom cell. */
body.printing-cert .cert-qc-bottom {
    background: #f9fafb;
}
body.printing-cert .cert-qc-bottom .cert-cell-row:first-child {
    border-bottom: 1px solid #e5e5e5;
    padding-bottom: 0.35rem;
    margin-bottom: 0.25rem;
}

/* Print adjustments for the new QC bottom + boilerplate. */
@media print {
    body.printing-cert .cert-cell-row .cert-value {
        color: #000 !important;
    }
    body.printing-cert .cert-big-figure,
    body.printing-cert .cert-big-figure strong {
        color: #000 !important;
    }
    body.printing-cert .cert-visual-pill {
        color: #000 !important;
        background: #fff !important;
        border-color: #000 !important;
    }
    body.printing-cert .cert-boilerplate-heading {
        color: #000 !important;
        border-bottom-color: #000 !important;
    }
}

/* ===== Status strip (above the cert table; not part of the cert
       document itself, just an admin/operator hint that shows
       revision number + status + timestamps). ===== */
body.printing-cert.cert-excel .cert-status-strip {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 0.5rem;
    padding: 0.35rem 0.6rem;
    margin-bottom: 0.5rem;
    border: 1px solid #000;
    background: #f5f5f5;
    color: #000;
    font-size: 10pt;
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
}
body.printing-cert.cert-excel .cert-status-strip .cert-status-pill {
    font-weight: 700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
}
body.printing-cert.cert-excel .cert-status-strip.status-published {
    border-left: 4px solid #047857;
}
body.printing-cert.cert-excel .cert-status-strip.status-draft {
    border-left: 4px solid #b45309;
}
body.printing-cert.cert-excel .cert-status-strip.status-void {
    border-left: 4px solid #b91c1c;
}
body.printing-cert.cert-excel .cert-status-strip .cert-status-meta {
    color: #4b5563;
    font-size: 9.5pt;
}

/* =============================================================
   Cert layout: Excel `Cert - Australia` replica (Cosmo 2026-09-03).
   Self-contained table that mirrors the Excel's row/column grid
   with proportional column widths, 2px outer border, 1px dotted
   internal borders, gray-bar section headers, blue values, red
   product order #, 28pt bold title. See
   app/templates/test_certificate/view.html for the grid mapping.

   Specificity note: many legacy `body.printing-cert .cert-*` rules
   still live in this stylesheet from the previous graphical layout.
   We use `body.printing-cert.cert-excel` (both classes) to override
   them. Body is rendered with both classes via base.html's
   `{% block body_class %}`.
   ============================================================= */

body.printing-cert.cert-excel .cert-excel-page {
    width: 210mm;
    min-height: 297mm;
    padding: 13mm 13mm;
    margin: 0 auto;
    box-sizing: border-box;
    background: #fff;
    color: #000;
    font-family: Calibri, "Helvetica Neue", Arial, sans-serif;
    font-size: 12pt;
    line-height: 1.0;
    display: flex;
    flex-direction: column;
}

/* Grow the table to fill the A4 page so the bottom row reaches the
   page margin (Cosmo 2026-09-03 13:33). The page wrapper is a flex
   column; the table takes all remaining vertical space. The status
   strip above the table (when present) keeps its natural height. */
body.printing-cert.cert-excel .cert-excel-page > .cert-excel-table {
    flex: 1 1 auto;
}

body.printing-cert.cert-excel .cert-excel-table {
    width: 100%;
    border-collapse: collapse;
    table-layout: fixed;
    border: 2px solid #000;
    background: #fff;
    color: #000;
}

body.printing-cert.cert-excel .cert-excel-table td {
    border: 1px dotted #000;
    padding: 3pt 4pt;
    vertical-align: middle;
    font-size: 11pt;
    line-height: 1.0;
    color: #000;
}

/* ===== Header (rows 2-5) ===== */
body.printing-cert.cert-excel td.cert-logo-cell {
    text-align: center;
    vertical-align: middle;
    color: #9ca3af;
    font-style: italic;
    font-size: 11pt;
    padding: 4pt;
}
body.printing-cert.cert-excel .cert-logo-img {
    max-width: 100%;
    max-height: 100%;
    width: auto;
    height: auto;
    object-fit: contain;
}
body.printing-cert.cert-excel td.cert-title {
    font-size: 22pt;
    font-weight: 700;
    text-align: center;
    vertical-align: middle;
    color: #000;
    line-height: 1.0;
}
body.printing-cert.cert-excel td.cert-bar-label {
    font-size: 10pt;
    font-weight: 400;
    text-align: center;
    vertical-align: middle;
    color: #000;
}
body.printing-cert.cert-excel td.cert-product-order {
    color: #FF0000 !important;
    font-size: 14pt;
    font-weight: 700;
    text-align: center;
    vertical-align: middle;
    line-height: 1.0;
}
body.printing-cert.cert-excel td.cert-bar-date {
    font-size: 9pt;
    font-weight: 400;
    text-align: center;
    vertical-align: middle;
    color: #000;
    white-space: nowrap;
}

/* ===== Section-bar rows (row 6 / row 15) ===== */
body.printing-cert.cert-excel tr.cert-row-bar td {
    border-top: 2px solid #000;
    border-bottom: 2px solid #000;
    border-left: 1px dotted #000;
    border-right: 1px dotted #000;
    font-weight: 700;
    text-align: center;
    vertical-align: middle;
    font-size: 11pt;
    background: transparent;
    color: #000;
    height: 26.65pt;
}

/* ===== Labels and values ===== */
body.printing-cert.cert-excel td.cert-label {
    color: #000 !important;
    text-align: right;
    vertical-align: middle;
    font-weight: 400;
    font-size: 11pt;
    white-space: nowrap;
}
body.printing-cert.cert-excel .cert-label-left {
    text-align: left;
}
body.printing-cert.cert-excel .cert-label-right {
    text-align: right;
}
body.printing-cert.cert-excel td.cert-value {
    color: #0070C0 !important;
    vertical-align: middle;
    font-weight: 400;
    font-size: 11pt;
}

/* Long-text values (mfg column C: batch id, stl file, printer profile,
   printer name) may wrap because the values are longer than the
   column allows. Spec values (E-H) stay on one line. */
body.printing-cert.cert-excel .cert-value.cert-value-long {
    white-space: normal;
    word-break: break-word;
    overflow-wrap: anywhere;
}

/* Spec value cells (Expected/Min/Max/Actual) — no wrap, always one line. */
body.printing-cert.cert-excel .cert-spec-value {
    white-space: nowrap;
}
/* Unit column for attenuFlex QC (mm suffix). Light, narrow, left-aligned. */
body.printing-cert.cert-excel .cert-spec-unit {
    text-align: left;
    padding-left: 4px;
    color: #000;
    font-size: 9pt;
}
body.printing-cert.cert-excel .cert-value-right {
    text-align: right;
}
body.printing-cert.cert-excel .cert-value-left {
    text-align: left;
}
body.printing-cert.cert-excel .cert-vertical-top {
    vertical-align: top;
}

/* ===== QC spec header (row 16) ===== */
body.printing-cert.cert-excel td.cert-spec-head {
    text-align: center;
    font-weight: 400;
    color: #000;
    font-size: 10pt;
}

/* ===== Notes rows (22-23) ===== */
body.printing-cert.cert-excel tr.cert-row-notes td {
    border-top: 2px solid #000;
    vertical-align: top;
}
body.printing-cert.cert-excel td.cert-client-notes {
    color: #000 !important;
    font-weight: 700;
    font-size: 10pt;
    line-height: 1.2;
}

/* ===== Boilerplate (bottom merged cell) =====
   IMPORTANT: explicit display:table-cell to defeat the legacy
   `body.printing-cert .cert-boilerplate { display: flex }` rule
   which would otherwise collapse the colspan and shrink the
   cell to ~146px. */
body.printing-cert.cert-excel td.cert-boilerplate {
    display: table-cell !important;
    border: 2px solid #000;
    padding: 8pt 10pt;
    text-align: left;
    vertical-align: top;
}

/* ===== STL thumbnail cell (right half of the bottom row) =====
       Holds a pre-rendered PNG (when the thumbnail renderer ships)
       or a placeholder block (until then). Sized to fit a square
       240×240 thumbnail comfortably with breathing room. */
body.printing-cert.cert-excel td.cert-thumbnail-cell {
    display: table-cell !important;
    border: 2px solid #000;
    padding: 14pt;
    text-align: center;
    vertical-align: middle;
    background: #fafafa;
    position: relative;
}
body.printing-cert.cert-excel .cert-thumbnail-cell .cert-thumbnail-img {
    display: block;
    margin: 0 auto;
    max-width: 100%;
    max-height: 100%;
    width: auto;
    height: auto;
}
body.printing-cert.cert-excel .cert-thumbnail-cell .cert-thumbnail-placeholder {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 6pt;
    width: 100%;
    min-height: 200pt;
    color: #6b7280;
    font-style: italic;
}
/* Print-only overrides moved to the END of the cert-excel block
   (after the screen rules) so the cascade order doesn't let a
   same-specificity screen rule override them. */
body.printing-cert.cert-excel .cert-thumbnail-cell .cert-thumbnail-placeholder-label {
    font-weight: 700;
    font-style: normal;
    color: #000;
    font-size: 11pt;
}
body.printing-cert.cert-excel .cert-thumbnail-cell .cert-thumbnail-placeholder-hint {
    font-size: 9pt;
    color: #6b7280;
    font-style: italic;
}
body.printing-cert.cert-excel .cert-boilerplate-heading {
    font-size: 11pt;
    font-weight: 700;
    margin: 0 0 4pt 0;
    color: #000;
    border-bottom: 1px dotted #6b7280;
    padding-bottom: 2pt;
}
body.printing-cert.cert-excel .cert-boilerplate-para {
    margin: 0 0 5pt 0;
    font-size: 10.5pt;
    line-height: 1.35;
    color: #000;
}
body.printing-cert.cert-excel .cert-boilerplate-para strong {
    font-weight: 700;
}

/* ===== evaMed (eCutout) cert layout — Phase 2 (2026-09-03) ===== */
/* Tuned to match the eCutout Test Certificate PDF reference (ec-4776-001).
   Two-column: Order Information (left, 55%) | Manufacturing Information
   (right, 45%). Same cert-* class system as siliconBolus; this block is
   layout-only tweaks for the 4-column evaMed table. */
body.printing-cert.cert-excel .cert-excel-page {
    border: 3px solid #000;
    padding: 6px;
    /* Use border-collapse:separate on the outer wrapper so the 3px
       outer border is visible (a table inside with border-collapse:collapse
       can clip the outer border in some browsers). */
    border-collapse: separate;
}
body.printing-cert.cert-excel .cert-excel-table.cert-evaMed {
    border-collapse: collapse;
    width: 100%;
    table-layout: fixed;
}
body.printing-cert.cert-excel .cert-evaMed td,
body.printing-cert.cert-excel .cert-evaMed th {
    border: 1px solid #000;
    padding: 3px 6px;
    font-size: 11pt;
    color: #000;
    vertical-align: middle;
    /* Prevent long values (RTPLAN.1.2.246.352.221.4692465, etc.)
       from spilling out of cells. `overflow-wrap` is preferred over
       `word-break: break-all` because it only breaks at natural break
       points when possible, then falls back to mid-word breaks. */
    overflow-wrap: break-word;
    word-break: break-word;
    hyphens: auto;
}
body.printing-cert.cert-excel .cert-evaMed .cert-label {
    color: #000;
    font-weight: 700;
    background: transparent;
    text-align: right;
    font-size: 10pt;
    overflow-wrap: break-word;
}
body.printing-cert.cert-excel .cert-evaMed .cert-label.cert-label-left {
    text-align: left;
}
body.printing-cert.cert-excel .cert-evaMed .cert-value {
    color: #1F6FE5;
    text-align: left;
    font-size: 10pt;
    overflow-wrap: break-word;
}
body.printing-cert.cert-excel .cert-evaMed .cert-value.cert-value-right {
    text-align: left;
}
body.printing-cert.cert-excel .cert-evaMed .cert-title {
    font-size: 28pt;
    font-weight: 700;
    color: #000;
    text-align: center;
    vertical-align: middle;
    line-height: 1.1;
}
body.printing-cert.cert-excel .cert-evaMed .cert-product-order {
    font-size: 24pt;
    font-weight: 700;
    color: #E60000;
    text-align: right;
}
body.printing-cert.cert-excel .cert-evaMed .cert-bar-label {
    font-weight: 700;
    background: transparent;
    color: #000;
    text-align: right;
    font-size: 10pt;
}
body.printing-cert.cert-excel .cert-evaMed .cert-bar-date {
    color: #000;
    text-align: right;
}
/* Section header bands — bold black on light grey #D9D9D9 */
body.printing-cert.cert-excel .cert-evaMed .cert-row-bar {
    background: #D9D9D9;
}
body.printing-cert.cert-excel .cert-evaMed .cert-row-bar td {
    background: #D9D9D9;
    font-weight: 700;
    text-align: left;
    border: 1px solid #000;
    padding: 4px 6px;
}
/* Inner QC table (Contour / Visual / Header × Pass/Fail) — plain inline text */
body.printing-cert.cert-excel .cert-evaMed .cert-qc-inner {
    border-collapse: collapse;
    width: 100%;
    margin: 0;
}
body.printing-cert.cert-excel .cert-evaMed .cert-qc-inner th,
body.printing-cert.cert-excel .cert-evaMed .cert-qc-inner td {
    border: 1px solid #000;
    padding: 2px 6px;
    font-size: 10pt;
    text-align: left;
    color: #1F6FE5;
}
body.printing-cert.cert-excel .cert-evaMed .cert-qc-inner th {
    color: #000;
    font-weight: 700;
    background: transparent;
    text-align: center;
}
/* Pass/Fail: plain inline blue text (NO pill — matches PDF reference) */
body.printing-cert.cert-excel .cert-evaMed .cert-pill {
    color: #1F6FE5;
    font-weight: 400;
    margin-left: 4px;
}
/* Contour plot in bottom-right corner — mirrors the siliconBolus
   STL thumbnail placement (Cosmo 2026-09-03 19:54). The bottom row
   splits: boilerplate left (colspan=2 of 4), contour thumbnail right
   (colspan=2). */
body.printing-cert.cert-excel .cert-evaMed .cert-thumbnail-cell {
    text-align: center;
    vertical-align: middle;
    padding: 4px;
    background: #fff;
}
body.printing-cert.cert-excel .cert-evaMed .cert-contour-img {
    max-width: 100%;
    max-height: 200px;
    width: auto;
    height: auto;
    display: block;
    margin: 0 auto;
}
body.printing-cert.cert-excel .cert-evaMed .cert-thumbnail-placeholder {
    color: #999;
    font-size: 9pt;
    font-style: italic;
}
/* Client Notes value renders as HTML (paragraphs preserved) */
body.printing-cert.cert-excel .cert-evaMed .cert-client-notes {
    color: #1F6FE5;
    overflow-wrap: break-word;
}
body.printing-cert.cert-excel .cert-evaMed .cert-client-notes p {
    margin: 0 0 8px 0;
    overflow-wrap: break-word;
}
body.printing-cert.cert-excel .cert-evaMed .cert-client-notes p:last-child {
    margin-bottom: 0;
}

/* ===== Print: A4 portrait, fit on 1 page within margins ===== */
@media print {
    body.printing-cert.cert-excel {
        margin: 0;
        padding: 0;
        background: #fff;
        overflow: hidden;
    }
    body.printing-cert.cert-excel main.site-main {
        padding: 0;
        margin: 0;
        overflow: hidden;
    }
    body.printing-cert.cert-excel .cert-excel-page {
        width: 210mm;
        height: 297mm;
        min-height: 0;
        padding: 2mm;        /* minimal margin — @page handles the actual margin */
        margin: 0;
        box-sizing: border-box;
        display: block;
        overflow: hidden;     /* clip anything that would push to a 2nd page */
    }
    /* Status strip is admin-only context (Revision N — Published). Hide
       in print — the cert itself doesn't need it and it eats space
       that pushes the cert table off page 1. */
    body.printing-cert.cert-excel .cert-status-strip {
        display: none !important;
    }
    body.printing-cert.cert-excel .cert-excel-table {
        page-break-inside: avoid;
        break-inside: avoid;
        width: 100%;
        height: 100%;
        max-height: 275mm;   /* hard cap so the whole table fits on 1 A4 */
        overflow: hidden;
        table-layout: fixed;
    }
    body.printing-cert.cert-excel .cert-value {
        color: #000 !important;
    }
    body.printing-cert.cert-excel .cert-product-order {
        color: #000 !important;
    }
}
@page {
    size: A4 portrait;
    margin: 0;
}

/* Print: shrink the placeholder and boilerplate so the cert fits
   on 1 A4 page. (Kept at the END of the cert-excel block so it
   wins the cascade over same-specificity screen rules.) */
@media print {
    body.printing-cert.cert-excel .cert-thumbnail-cell .cert-thumbnail-placeholder {
        min-height: 110pt;
    }
    body.printing-cert.cert-excel .cert-boilerplate-para {
        font-size: 8.5pt;
        line-height: 1.15;
        margin: 0 0 1pt 0;
    }
    body.printing-cert.cert-excel .cert-boilerplate-heading {
        font-size: 9.5pt;
        margin: 0 0 2pt 0;
    }
    body.printing-cert.cert-excel td.cert-boilerplate {
        padding: 4pt 6pt;
    }
    body.printing-cert.cert-excel td.cert-thumbnail-cell {
        padding: 4pt;
    }
}

/* evaMed A4 print tuning (Cosmo 2026-09-04 09:50). evaMed has more
   content than siliconBolus (2x2 grid + bottom row with boilerplate +
   thumbnail) so it needs tighter fonts/padding to fit on 1 A4 without
   clipping. Without these rules the Storage bullets get cut off at
   the page bottom — a content correctness bug, not just cosmetic.
   The screen-mode rules above stay untouched. */
@media print {
    /* Tighter cells throughout evaMed so the table fits ~30pt taller. */
    body.printing-cert.cert-excel .cert-evaMed td,
    body.printing-cert.cert-excel .cert-evaMed th {
        padding: 2px 4px;
        font-size: 9pt;
    }
    body.printing-cert.cert-excel .cert-evaMed .cert-label,
    body.printing-cert.cert-excel .cert-evaMed .cert-value {
        font-size: 9pt;
    }
    body.printing-cert.cert-excel .cert-evaMed .cert-row-bar td {
        padding: 2px 4px;
    }
    /* Smaller title + product order # in print so the header row is
       shorter (the title rowspan=2 was taking ~50pt of vertical space). */
    body.printing-cert.cert-excel .cert-evaMed .cert-title {
        font-size: 22pt;
    }
    body.printing-cert.cert-excel .cert-evaMed .cert-product-order {
        font-size: 20pt;
    }
    /* Smaller contour plot so it doesn't crowd out the boilerplate. */
    body.printing-cert.cert-excel .cert-evaMed .cert-contour-img {
        max-height: 140px;
    }
    /* Boilerplate text gets smaller + tighter so Storage bullets
       survive the page edge. */
    body.printing-cert.cert-excel .cert-evaMed .cert-boilerplate-para {
        font-size: 7.5pt;
        line-height: 1.1;
        margin: 0 0 0.5pt 0;
    }
    body.printing-cert.cert-excel .cert-evaMed .cert-boilerplate-heading {
        font-size: 8.5pt;
        margin: 0 0 1pt 0;
    }
    body.printing-cert.cert-excel .cert-evaMed td.cert-boilerplate {
        padding: 2pt 4pt 6pt 4pt;   /* extra bottom so the last line breathes */
    }
    body.printing-cert.cert-excel .cert-evaMed td.cert-thumbnail-cell {
        padding: 2pt;
    }
    /* QC inner table — smaller so Other QC Checks row fits. */
    body.printing-cert.cert-excel .cert-evaMed .cert-qc-inner th,
    body.printing-cert.cert-excel .cert-evaMed .cert-qc-inner td {
        padding: 1px 4px;
        font-size: 8.5pt;
    }
    /* Client Notes — tighter paragraph margins in print. */
    body.printing-cert.cert-excel .cert-evaMed .cert-client-notes p {
        margin: 0 0 3px 0;
    }
}


/* BME nav additions (commit pending — Manufacturing Grid dropdown extras) */
.main-nav .nav-menu .nav-sep {
    border: 0;
    border-top: 1px solid var(--color-border);
    margin: var(--space-sm) 0;
    height: 0;
}
.main-nav .nav-menu .nav-subhead {
    padding: var(--space-xs) var(--space-md);
    font-size: 0.7rem;
    font-weight: 600;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    color: var(--color-text-muted);
}

/* Phase C.1 — inline cell editing on the manufacturing grid. */
.manufacturing-grid-table td.cell-editable {
    cursor: text;
    position: relative;
}
.manufacturing-grid-table td.cell-editable:hover {
    background: rgba(110, 168, 254, 0.06);
    outline: 1px dashed rgba(110, 168, 254, 0.4);
    outline-offset: -2px;
}
.manufacturing-grid-table td.cell-editing {
    background: rgba(255, 220, 100, 0.15);
    outline: 2px solid rgba(245, 158, 11, 0.7);
    outline-offset: -2px;
    padding: 0;
}
.manufacturing-grid-table td.cell-editing .cell-edit-input {
    width: 100%;
    box-sizing: border-box;
    padding: 4px 8px;
    border: 0;
    background: transparent;
    color: inherit;
    font: inherit;
    outline: none;
}
.manufacturing-grid-table td.cell-saving {
    background: rgba(245, 158, 11, 0.18);
    color: rgba(245, 158, 11, 0.7);
}
.manufacturing-grid-table td.cell-saved {
    background: rgba(34, 197, 94, 0.20);
    transition: background 0.8s ease-out;
}
.manufacturing-grid-table td.cell-error {
    background: rgba(239, 68, 68, 0.18);
    color: rgba(239, 68, 68, 0.95);
}

/* Phase C.2 — AG-Grid on the manufacturing grid route.

   Quartz-dark theme is the default; we keep all AG-Grid column
   styling stock except: (1) hint + toast notifications, (2)
   tweak the editable-cell hover to match the Phase C.1 vanilla
   hover affordance (dashed outline), (3) keep the grid container
   a fixed sensible height with sticky thead. */
.manufacturing-grid-page .orders-wrap {
    margin-top: var(--space-md);
}
#manufacturing-grid {
    height: calc(100vh - 380px);
    min-height: 320px;
    width: 100%;
}
#manufacturing-grid.ag-theme-quartz-dark {
    --ag-background-color: var(--color-bg-elev);
    --ag-foreground-color: var(--color-text);
    --ag-header-background-color: var(--color-bg);
    --ag-header-foreground-color: var(--color-text);
    --ag-odd-row-background-color: transparent;
    --ag-row-hover-color: rgba(110, 168, 254, 0.06);
    --ag-border-color: var(--color-border);
    --ag-secondary-border-color: var(--color-border);
    font-family: inherit;
    font-size: 0.875rem;
}

/* Editable column header — same dashed outline the Phase C.1
   .cell-editable:hover used, so the user knows which cells are
   editable. */
#manufacturing-grid .ag-cell-inline-editing {
    box-shadow: inset 0 0 0 2px var(--color-accent, #6ea8fe);
    padding: 0;
}

/* Non-editable cells: muted text on hover. */
#manufacturing-grid .ag-cell:not(.ag-cell-inline-editing):not(.ag-cell-edit-input) {
    opacity: 1;
}

/* The save flash on the cell. AG-Grid's built-in 'cellFlash' /
   'cellFade' classes handle most of it, but we add a brief
   highlight to make the save visually obvious in a grid full of
   rows. */
#manufacturing-grid .ag-cell-data-changed {
    background: rgba(34, 197, 94, 0.25) !important;
    transition: background 0.6s ease-out;
}
#manufacturing-grid .ag-cell-data-changed-animation {
    background: rgba(34, 197, 94, 0.30) !important;
    transition: background 0.6s ease-out !important;
}

/* Excel-paste preview (the dashed border that AG-Grid shows when
   the user pastes a range). */
#manufacturing-grid .ag-range-selected {
    border: 1px dashed var(--color-accent, #6ea8fe) !important;
}

/* .order-link used inside the renderer. */
.order-link {
    color: var(--color-link, #6ea8fe);
    text-decoration: none;
}
.order-link:hover { text-decoration: underline; }

/* Hint under the grid. */
.mfg-hint {
    font-size: 0.8rem;
    color: var(--color-text-muted);
    margin-top: var(--space-sm);
}

/* Toast notifications (replaces Phase C.1 alert()). Stacks
   bottom-right, auto-dismisses. */
.mfg-toast-root {
    position: fixed;
    bottom: 1.25rem;
    right: 1.25rem;
    z-index: 9999;
    display: flex;
    flex-direction: column-reverse;
    gap: 0.5rem;
    pointer-events: none;
}
.mfg-toast {
    padding: 0.625rem 1rem;
    border-radius: var(--radius, 6px);
    font-size: 0.875rem;
    line-height: 1.2;
    color: white;
    box-shadow: var(--shadow-lg, 0 4px 16px rgba(0,0,0,0.3));
    opacity: 0;
    transform: translateY(8px);
    transition: opacity 0.2s ease-out, transform 0.2s ease-out;
    pointer-events: auto;
}
.mfg-toast-in { opacity: 1; transform: translateY(0); }
.mfg-toast-out { opacity: 0; transform: translateY(-4px); }
.mfg-toast-success { background: #16a34a; }
.mfg-toast-error { background: #dc2626; }
.mfg-toast-info { background: var(--color-bg-elev, #2a3a5f); color: var(--color-text); }

/* Phase C.2 Excel-blank-master parity: section-banded header tints.

   ecutoutBlank grid uses Excel's 3-color theme palette to mark
   3 column groupings (theme 9 = order+linking+admin, theme 7 =
   manufacturing). The dark-theme equivalents are mapped below so
   the bands still read as distinct groups on the dark UI. */
#manufacturing-grid .ag-header-cell.ag-header-theme9 {
    background-color: var(--mfg-theme-9, #3a4660) !important;
    color: var(--color-text, #e7eaf3) !important;
}
#manufacturing-grid .ag-header-cell.ag-header-theme7 {
    background-color: var(--mfg-theme-7, #28324a) !important;
    color: var(--color-text, #e7eaf3) !important;
}
/* Subgroup cells (L/M/N under "Thickness measurements") share the
   theme-7 background but with a visible top divider so the merged
   group label reads correctly. The left subgroup cell carries the
   label; middle/right are blank but visually bounded. */
#manufacturing-grid .ag-header-cell.ag-header-subgroup-left {
    border-right: 1px solid var(--color-border-strong, #2a3a5f) !important;
}
#manufacturing-grid .ag-header-cell.ag-header-subgroup-mid {
    border-right: 1px solid var(--color-border-strong, #2a3a5f) !important;
}
#manufacturing-grid .ag-header-cell.ag-header-subgroup-right {
    border-right: 2px solid var(--color-border-strong, #2a3a5f) !important;
}
/* The "Thickness measurements" group label is in the left subgroup
   header cell, centred + bold to match Excel. */
#manufacturing-grid .ag-header-cell.ag-header-subgroup-left .ag-header-cell-text {
    font-weight: 600;
    width: 100%;
    text-align: center;
}
/* Hide empty header text in middle/right subgroup cells but keep
   the divider visible. */
#manufacturing-grid .ag-header-cell.ag-header-subgroup-mid .ag-header-cell-text:empty,
#manufacturing-grid .ag-header-cell.ag-header-subgroup-right .ag-header-cell-text:empty {
    visibility: hidden;
}

/* Manufacturing grid scrollbars — single, thick, easy to click.

   Cosmo 2026-09-07 16:18: 'fix the duplicated vertical scroll bar bug
   in the GUI. Then double the scroll bar thickness so its easier
   to click.'

   History: previous CSS had FIVE overlapping scrollbar rule blocks
   (lines 3842-3962 before this commit). Three targeted native browser
   scrollbars (width 12px), one targeted higher-specificity selectors
   (also 12px), and one painted custom gradient thumbs via
   `.ag-body-*-scroll-container::after` pseudo-elements. With the JS
   MutationObserver stripping the `ag-scrollbar-invisible` class, both
   the native scrollbar AND the custom pseudo-element thumb became
   visible at the same position, producing the duplicated thumb.

   Fix: removed all five blocks and consolidated into ONE rule block
   that paints only the native browser scrollbar. Doubled the
   width/height from 12px to 24px (with a 4px transparent border on
   the thumb so the visible thumb is still 16px — keeps the click
   target generous without making the thumb look out-of-place).
   Removed the custom `::after` pseudo-element thumbs entirely.

   The JS in manufacturing_grid.js still strips the
   `ag-scrollbar-invisible` class so the native scrollbar paints;
   no JS changes needed for this fix.
*/
/* Vertical viewport: fixed 24px width so the vertical scrollbar
   has a wide click target. Height auto so it stretches the full
   grid height. */
#manufacturing-grid .ag-body-vertical-scroll,
#manufacturing-grid .ag-body-vertical-scroll-viewport {
    scrollbar-width: auto;
    scrollbar-color: var(--color-accent, #6ea8fe) transparent;
    width: 24px !important;
    min-width: 24px !important;
    max-width: 24px !important;
}
/* Horizontal viewport: fixed 24px height so the horizontal
   scrollbar has a wide click target. Width auto so it fills the
   full grid width (which it needs to scroll horizontally). */
#manufacturing-grid .ag-body-horizontal-scroll,
#manufacturing-grid .ag-body-horizontal-scroll-viewport {
    scrollbar-width: auto;
    scrollbar-color: var(--color-accent, #6ea8fe) transparent;
    height: 24px !important;
    min-height: 24px !important;
    max-height: 24px !important;
}
#manufacturing-grid .ag-body-horizontal-scroll,
#manufacturing-grid .ag-body-vertical-scroll {
    background: rgba(110, 168, 254, 0.06) !important;
    border-top: 1px solid rgba(110, 168, 254, 0.15) !important;
}
#manufacturing-grid .ag-body-vertical-scroll {
    border-top: none !important;
    border-left: 1px solid rgba(110, 168, 254, 0.15) !important;
}
#manufacturing-grid.ag-theme-quartz ::-webkit-scrollbar,
#manufacturing-grid .ag-root-wrapper ::-webkit-scrollbar,
#manufacturing-grid .ag-root ::-webkit-scrollbar,
#manufacturing-grid .ag-body-viewport ::-webkit-scrollbar,
#manufacturing-grid .ag-body-horizontal-scroll ::-webkit-scrollbar,
#manufacturing-grid .ag-body-horizontal-scroll-viewport ::-webkit-scrollbar,
#manufacturing-grid .ag-body-vertical-scroll ::-webkit-scrollbar,
#manufacturing-grid .ag-body-vertical-scroll-viewport ::-webkit-scrollbar {
    display: block !important;
    -webkit-appearance: none !important;
    width: 24px !important;
    height: 24px !important;
}
#manufacturing-grid.ag-theme-quartz ::-webkit-scrollbar-thumb,
#manufacturing-grid .ag-root-wrapper ::-webkit-scrollbar-thumb,
#manufacturing-grid .ag-body-horizontal-scroll ::-webkit-scrollbar-thumb,
#manufacturing-grid .ag-body-horizontal-scroll-viewport ::-webkit-scrollbar-thumb,
#manufacturing-grid .ag-body-vertical-scroll ::-webkit-scrollbar-thumb,
#manufacturing-grid .ag-body-vertical-scroll-viewport ::-webkit-scrollbar-thumb {
    background: var(--color-accent, #6ea8fe) !important;
    border-radius: 12px !important;
    /* Transparent border shrinks the visible thumb to 16px while
       keeping the 24px hit target. background-clip:content-box
       keeps the thumb fill inside the inner 16px box. */
    border: 4px solid transparent !important;
    background-clip: content-box !important;
}
#manufacturing-grid.ag-theme-quartz ::-webkit-scrollbar-thumb:hover,
#manufacturing-grid .ag-body-horizontal-scroll ::-webkit-scrollbar-thumb:hover,
#manufacturing-grid .ag-body-vertical-scroll ::-webkit-scrollbar-thumb:hover {
    background: #8fb8ff !important;
    background-clip: content-box !important;
}
#manufacturing-grid.ag-theme-quartz ::-webkit-scrollbar-track,
#manufacturing-grid .ag-root-wrapper ::-webkit-scrollbar-track,
#manufacturing-grid .ag-body-horizontal-scroll ::-webkit-scrollbar-track,
#manufacturing-grid .ag-body-vertical-scroll ::-webkit-scrollbar-track {
    background: rgba(110, 168, 254, 0.04) !important;
    border-radius: 12px !important;
}
#manufacturing-grid .ag-body-horizontal-scroll-corner {
    background: var(--color-bg, #0f172a) !important;
}


/* ---------------------------------------------------------------------------
 * Manufacturing grid cell-type color coding (Phase C.6)
 *
 * Five distinct cell states. Adapted from the financial-modeling
 * convention (Blue=input / Black=formula / Green=link) plus a
 * backfilled state for data loaded from the legacy Excel source.
 *
 * Tints use 6-10% opacity so they don't fight with the row hover
 * state (#row-hover @ 6%) or the dark UI saturation. Foreground text
 * color stays at --color-fg (high contrast) so the tints don't
 * require extra contrast work.
 *
 * The CSS class is added via AG-Grid's cellClass / cellClassRules
 * (see app/ag_grid_layouts.py and the JS onCellValueChanged in
 * app/static/js/manufacturing_grid.js). The mapping:
 *
 *   .mfg-cell-input         operator types this value (editable)
 *   .mfg-cell-derived       formula-driven (min/max, etc.); read-only
 *   .mfg-cell-backfilled    loaded from Excel backfill; read-only
 *   .mfg-cell-linked        cross-source ref (Order #, blank, etc.)
 *   .mfg-cell-locked        admin-only field (off-limits to operator)
 * --------------------------------------------------------------------------- */
#manufacturing-grid .mfg-cell-input {
    background-color: rgba(56, 189, 248, 0.14);  /* blue tint (--color-accent) */
}
#manufacturing-grid .mfg-cell-input:hover {
    background-color: rgba(56, 189, 248, 0.32);
}
#manufacturing-grid .mfg-cell-derived {
    background-color: rgba(167, 139, 250, 0.14);  /* purple tint */
    font-style: italic;  /* signal "this is a computation" */
    color: rgba(167, 139, 250, 0.95);  /* tint the text too */
}
#manufacturing-grid .mfg-cell-derived:hover {
    background-color: rgba(167, 139, 250, 0.24);
}
#manufacturing-grid .mfg-cell-backfilled {
    background-color: rgba(251, 191, 36, 0.14);  /* amber tint */
    border-left: 2px solid rgba(251, 191, 36, 0.4);  /* left border as extra signal */
}
#manufacturing-grid .mfg-cell-linked {
    background-color: rgba(52, 211, 153, 0.14);  /* green tint (--color-success) */
}
/* Locked (admin-only): subtle so it doesn't fight with content but
 * clearly says "off-limits." Use a left border to pair with the
 * subtle bg tint (color-only is not WCAG accessible). */
#manufacturing-grid .mfg-cell-locked {
    background-color: rgba(148, 163, 184, 0.08);
    border-left: 2px dashed rgba(148, 163, 184, 0.5);
    color: var(--color-fg-muted);
}
#manufacturing-grid .mfg-cell-locked:hover {
    background-color: rgba(148, 163, 184, 0.24);
    cursor: not-allowed;
}

/* Header-band tint indicator. Add a small dot before the header label
 * to signal "this column contains derived cells" or "this column is
 * locked." Combined with the existing theme9 / theme7 bands. */
#manufacturing-grid .ag-header-cell.mfg-header-derived::before {
    content: "↻ ";
    color: rgba(167, 139, 250, 0.7);
    font-weight: 600;
}
#manufacturing-grid .ag-header-cell.mfg-header-locked::before {
    content: "🔒 ";
    font-size: 0.85em;
    opacity: 0.7;
}

/* Hover affordance for editable cells. The existing dashed-outline
 * Phase C.2 hover state stays, but we make it colour-coded so the
 * operator sees the colour of the cell type when they hover. */
#manufacturing-grid .mfg-cell-input.ag-cell-inline-editing {
    background-color: rgba(56, 189, 248, 0.18);
    box-shadow: inset 0 0 0 2px rgba(56, 189, 248, 0.7);
}
#manufacturing-grid .mfg-cell-derived.ag-cell-inline-editing {
    /* Derived cells aren't editable, but if operator somehow focuses
     * one we still want the indigo lockout. */
    box-shadow: inset 0 0 0 2px rgba(167, 139, 250, 0.7);
}


/* ---------------------------------------------------------------------------
 * Header-band semantic tint (Phase C.6 part 2)
 *
 * Per the user ask: the column header bands should also use the
 * semantic colour-coding scheme. Each header cell gets one class
 * (mfg-header-input / derived / linked / locked / backfilled) based
 * on the column's role, and the CSS picks a stronger tint than the
 * data-cell version (because headers can take bolder colours
 * without competing with row data).
 *
 * Header tints use ~22% opacity — visible but not eye-burning.
 * The existing theme9 (darker grey) / theme7 (lighter grey) bands
 * remain as the base layer; the semantic tint is overlaid.
 *
 * This is a coarse per-column classification (one header = one
 * colour) because the underlying cells in a column are usually
 * homogeneous. The data-cell tints below the header still give
 * row-level nuance for the few mixed-state columns.
 * --------------------------------------------------------------------------- */
#manufacturing-grid .ag-header-cell.mfg-header-input {
    background-color: rgba(56, 189, 248, 0.32) !important;
    border-bottom: 2px solid rgba(56, 189, 248, 0.7) !important;
}
#manufacturing-grid .ag-header-cell.mfg-header-derived {
    background-color: rgba(167, 139, 250, 0.32) !important;
    border-bottom: 2px solid rgba(167, 139, 250, 0.7) !important;
}
#manufacturing-grid .ag-header-cell.mfg-header-linked {
    background-color: rgba(52, 211, 153, 0.32) !important;
    border-bottom: 2px solid rgba(52, 211, 153, 0.7) !important;
}
#manufacturing-grid .ag-header-cell.mfg-header-locked {
    background-color: rgba(148, 163, 184, 0.24) !important;
    border-bottom: 2px dashed rgba(148, 163, 184, 0.55) !important;
}
#manufacturing-grid .ag-header-cell.mfg-header-backfilled {
    background-color: rgba(251, 191, 36, 0.32) !important;
    border-bottom: 2px solid rgba(251, 191, 36, 0.7) !important;
}

/* The existing theme7/theme9 base bands should NOT show through the
 * semantic tint. The rules above use !important on background-color
 * which takes precedence over theme7/theme9. We don't need an
 * override rule — the !important on the per-state rules already
 * wins over the theme bg. (Earlier draft tried to darken the base
 * layer but that just overrode the per-state tint.) */
#manufacturing-grid .ag-header-cell.ag-header-theme7.mfg-header-input,
#manufacturing-grid .ag-header-cell.ag-header-theme9.mfg-header-input,
#manufacturing-grid .ag-header-cell.ag-header-theme7.mfg-header-derived,
#manufacturing-grid .ag-header-cell.ag-header-theme9.mfg-header-derived,
#manufacturing-grid .ag-header-cell.ag-header-theme7.mfg-header-linked,
#manufacturing-grid .ag-header-cell.ag-header-theme9.mfg-header-linked,
#manufacturing-grid .ag-header-cell.ag-header-theme7.mfg-header-locked,
#manufacturing-grid .ag-header-cell.ag-header-theme9.mfg-header-locked,
#manufacturing-grid .ag-header-cell.ag-header-theme7.mfg-header-backfilled,
#manufacturing-grid .ag-header-cell.ag-header-theme9.mfg-header-backfilled {
    background-image: none !important;
}

/* Header text gets a touch brighter on semantic headers so the
 * label is legible against the now-darker band. */
#manufacturing-grid .ag-header-cell.mfg-header-input .ag-header-cell-label,
#manufacturing-grid .ag-header-cell.mfg-header-derived .ag-header-cell-label,
#manufacturing-grid .ag-header-cell.mfg-header-linked .ag-header-cell-label,
#manufacturing-grid .ag-header-cell.mfg-header-locked .ag-header-cell-label,
#manufacturing-grid .ag-header-cell.mfg-header-backfilled .ag-header-cell-label {
    color: var(--color-fg);
    font-weight: 500;
}
