/* ============================================================
   Paxon MultiBoxPacker — design system
   Paxon brand identity (202600106_Paxon_brandguidelines.pdf, v. 1 June 2026):
   Paxon Blue dominant, Dark Blue / Warm Light Grey as content zones, Orange as a
   sparing accent. Headline font PP Valve Stencil Paxon, body font Haffer — both are
   licensed brand fonts not bundled in this repo, so the stack below names them
   correctly and falls back to DM Sans, then a generic sans-serif, per the brand
   guide's own documented fallback chain. On a machine with the brand fonts
   installed, the correct ones render; elsewhere the fallback is expected, not a bug.
   WCAG AA color tokens (a few brand hexes are nudged for text-contrast — see notes).
   ============================================================ */

:root {
    /* Typography — PP Valve Stencil Paxon / Haffer are Paxon's licensed brand fonts;
       see the file banner above for the fallback rationale. */
    --font-headline: "PP Valve Stencil Paxon", "DM Sans", sans-serif;
    --font-sans: "Haffer", "DM Sans", Arial, sans-serif;
    --font-mono: ui-monospace, "SF Mono", "Cascadia Code", "Segoe UI Mono", Menlo, Consolas, monospace;

    /* Neutral surfaces (Warm Light Grey / Warm Grey — Paxon's warm neutral family) */
    --bg: #F6F3F0;
    --surface: #ffffff;
    --surface-2: #F6F3F0;
    --surface-3: #E8E0DA;
    --border: #E8E0DA;
    --border-strong: #d5cec9;

    /* Text — body copy is Dark Blue per brand spec; muted/subtle are Dark-Blue tints,
       tuned to stay WCAG AA (>=4.5:1 on white/surface). */
    --text: #0B1A4D;        /* Dark Blue — ~14.9:1 on white */
    --text-muted: #5c6688;  /* Dark Blue tint — ~5.7:1 on white */
    --text-subtle: #687191; /* Dark Blue tint — ~4.8:1 on white (WCAG AA) */

    /* Brand / accent — Paxon Blue, with its ton-sur-ton pair for hover and Dark Blue
       for the pressed state. */
    --primary: #006AEF;       /* Paxon Blue */
    --primary-hover: #0055CC; /* Paxon Tinted Blue (brand ton-sur-ton pair) */
    --primary-active: #0B1A4D; /* Dark Blue */
    --primary-weak: #e6f0fd;
    --on-primary: #ffffff;

    /* Status — Paxon's defined UI feedback colours. Success is nudged from the brand
       swatch #1F9B34 (3.6:1 on white, fails AA text) down to #1a842c (~4.8:1) for use
       as text; the original hex is fine for icons/fills where text contrast doesn't apply. */
    --danger: #E62337;   /* Paxon "Error" */
    --danger-weak: #fdedef;
    --danger-border: #f9c8cd;
    --success: #1a842c;  /* AA-adjusted from Paxon "Success" #1F9B34 */
    --success-weak: #edf5ee;
    --warning: #FFA64C;  /* Paxon Orange / "Warning" — sparing accent, not a body-text colour (1.9:1 on white) */

    --border-hover: #bab3ae;

    /* Effects */
    --radius-sm: 6px;
    --radius: 10px;
    --radius-lg: 14px;
    --ring: 0 0 0 3px rgba(0, 106, 239, 0.30);
    --shadow-xs: 0 1px 2px rgba(11, 26, 77, 0.05);
    --shadow-sm: 0 1px 3px rgba(11, 26, 77, 0.08), 0 1px 2px rgba(11, 26, 77, 0.04);
    --shadow-md: 0 4px 12px rgba(11, 26, 77, 0.08), 0 2px 4px rgba(11, 26, 77, 0.04);

    /* 3D viewer canvas background (white in light mode) */
    --viewer-bg: #ffffff;

    color-scheme: light;
}

/* ---------- Dark theme ---------- */
/* Built from Paxon's own two dark brand colours (Dark Tinted Blue / Dark Blue) rather
   than an off-brand neutral grey — the brand guide already lists Dark Blue as a
   large-background, "premium feel" colour, which is exactly what a dark theme needs. */
[data-theme="dark"] {
    --bg: #020C2E;       /* Dark Tinted Blue */
    --surface: #0B1A4D;  /* Dark Blue */
    --surface-2: #1f2c5b;
    --surface-3: #303c68;
    --border: #37436d;
    --border-strong: #4f5a7f;

    --text: #f3f4f6;
    --text-muted: #a2a8bb;  /* ~7.0:1 on the dark surface */
    --text-subtle: #8f96ad; /* ~5.6:1 on the dark surface (WCAG AA) */

    /* Paxon Blue lightened for dark backgrounds (a saturated mid-blue reads flat against
       Dark Blue); on-primary flips to a dark brand colour, mirroring how the light theme
       pairs Paxon Blue with white — same pattern, opposite direction. */
    --primary: #529af4;
    --primary-hover: #73adf6;
    --primary-active: #2680f1;
    --primary-weak: #16263f;
    --on-primary: #020C2E;

    --danger: #ff6b6b;
    --danger-weak: #29060a;
    --danger-border: #671019;
    --success: #46d18a;
    --success-weak: #061c09;
    --warning: #e0a458;

    --border-hover: #727b99;

    --ring: 0 0 0 3px rgba(82, 154, 244, 0.35);
    --shadow-xs: 0 1px 2px rgba(0, 0, 0, 0.4);
    --shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.5), 0 1px 2px rgba(0, 0, 0, 0.4);
    --shadow-md: 0 4px 12px rgba(0, 0, 0, 0.5), 0 2px 4px rgba(0, 0, 0, 0.4);

    --viewer-bg: #020C2E;

    color-scheme: dark;
}

* { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
    margin: 0;
    font-family: var(--font-sans);
    font-size: 15px;
    line-height: 1.5;
    color: var(--text);
    background: var(--bg);
    -webkit-font-smoothing: antialiased;
    text-rendering: optimizeLegibility;
}

/* Headline scale per brand spec: PP Valve Stencil Paxon, Paxon Blue, 0 tracking, 100% leading. */
h1, h2, h3 {
    font-family: var(--font-headline);
    font-weight: 650;
    color: var(--primary);
    letter-spacing: 0;
    line-height: 1;
    margin: 0;
}

/* ---------- App shell ---------- */
.app-header {
    background: var(--surface);
    border-bottom: 1px solid var(--border);
    position: sticky;
    top: 0;
    z-index: 20;
}

.app-header .header-bar {
    max-width: 1760px;
    margin: 0 auto;
    padding: 0.85rem 1.5rem;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
}

.app-header .brand {
    display: flex;
    align-items: center;
    gap: 0.7rem;
}

.theme-toggle { flex: none; }
.theme-toggle .theme-icon { font-size: 0.95rem; line-height: 1; }

/* Header actions: language menu + theme toggle */
.header-actions { display: flex; align-items: center; gap: 0.6rem; }
.flag {
    width: 20px; height: 14px;
    object-fit: cover;
    border-radius: 2px;
    border: 1px solid var(--border);
    display: inline-block;
    vertical-align: middle;
}

.lang-menu { position: relative; }
.lang-menu > summary {
    list-style: none;
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    cursor: pointer;
}
.lang-menu > summary::-webkit-details-marker { display: none; }
.lang-summary .lang-code { font-weight: 600; font-size: 0.85rem; }
.lang-summary .caret { font-size: 0.7rem; color: var(--text-muted); }
.lang-list {
    position: absolute;
    right: 0;
    top: calc(100% + 6px);
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow-md);
    padding: 0.3rem;
    min-width: 140px;
    z-index: 30;
    display: flex;
    flex-direction: column;
    gap: 0.1rem;
}
.lang-item {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.4rem 0.55rem;
    border-radius: var(--radius-sm);
    text-decoration: none;
    color: var(--text);
    font-size: 0.88rem;
}
.lang-item:hover { background: var(--surface-3); }
.lang-item.active { background: var(--primary-weak); font-weight: 600; }

/* Official Paxon symbol mark (no fake lettermark) — clear-space padding approximates the
   brand's "half the symbol's width" rule within the header's own spacing. */
.brand-mark { width: 30px; height: 30px; flex: none; }

.brand-text { display: flex; flex-direction: column; line-height: 1.1; }
.brand-text .name { font-family: var(--font-headline); font-weight: 650; font-size: 1rem; color: var(--primary); }
.brand-text .tag { font-size: 0.78rem; color: var(--text-muted); }

.container {
    max-width: 1760px;
    margin: 0 auto;
    padding: 1.75rem 1.5rem 4rem;
}

/* ---------- Card / section ---------- */
.card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-sm);
}

.section { padding: 1.25rem 1.35rem; }
.section + .section { border-top: 1px solid var(--border); }

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

.section-head h2 { font-size: 0.95rem; }
.section-head .hint { font-size: 0.8rem; color: var(--text-muted); }
.section-head .head-actions { display: flex; align-items: center; gap: 0.75rem; }

.page-title { margin-bottom: 1.35rem; }
.page-title h1 { font-size: 1.4rem; }
.page-title p { margin: 0.3rem 0 0; color: var(--text-muted); font-size: 0.92rem; }

/* ---------- Controls bar ---------- */
.controls {
    display: flex;
    gap: 1.1rem;
    align-items: flex-end;
    flex-wrap: wrap;
}

.field { display: flex; flex-direction: column; gap: 0.35rem; }
.field > span { font-size: 0.78rem; font-weight: 550; color: var(--text-muted); }
.field.grow { flex: 1 1 auto; }
.controls .spacer { flex: 1 1 auto; }

/* ---------- Inputs ---------- */
input[type="number"],
input[type="text"],
select,
textarea {
    font: inherit;
    color: var(--text);
    background: var(--surface);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm);
    padding: 0.45rem 0.6rem;
    transition: border-color 0.12s ease, box-shadow 0.12s ease;
}

input[type="number"]:hover,
input[type="text"]:hover,
select:hover { border-color: var(--border-hover); }

input:focus-visible,
select:focus-visible,
textarea:focus-visible {
    outline: none;
    border-color: var(--primary);
    box-shadow: var(--ring);
}

.field input[type="number"],
.field select { min-width: 8.5rem; }
/* Compact numeric field (Max parcels: at most 3 digits). */
.field input[type="number"].narrow { min-width: 0; width: 6rem; }

/* ---------- Spreadsheet grid ---------- */
.grid-wrap {
    border: 1px solid var(--border);
    border-radius: var(--radius);
    overflow: hidden;
    overflow-x: auto;
}

table.grid {
    width: 100%;
    border-collapse: collapse;
    font-variant-numeric: tabular-nums;
}

table.grid thead th {
    text-align: left;
    font-size: 0.72rem;
    font-weight: 600;
    letter-spacing: 0.03em;
    text-transform: uppercase;
    color: var(--text-muted);
    background: var(--surface-2);
    padding: 0.45rem 0.4rem;
    border-bottom: 1px solid var(--border);
    white-space: nowrap;
}

table.grid th.num, table.grid td.num { text-align: right; }
table.grid th.center, table.grid td.center { text-align: center; }

/* Numeric columns (length/width/height/weight/qty) shrink to their content instead of
   sharing the table width equally, leaving more room for the ID column. */
table.grid td.num { width: 1%; }
table.grid td.num .cell.num { width: 3.4rem; }

table.grid tbody td {
    padding: 0;
    border-bottom: 1px solid var(--border);
    background: var(--surface);
}

table.grid tbody tr:last-child td { border-bottom: none; }
table.grid tbody tr:hover td { background: var(--surface-2); }

/* Cell-embedded inputs → borderless so the table reads as a sheet */
table.grid td .cell {
    width: 100%;
    border: none;
    background: transparent;
    border-radius: 0;
    padding: 0.45rem 0.5rem;
}
table.grid td .cell:hover { border-color: transparent; }
table.grid td .cell:focus-visible {
    box-shadow: inset 0 0 0 2px var(--primary);
    border-radius: var(--radius-sm);
    background: var(--surface);
}
table.grid td .cell.num { text-align: right; }
table.grid td.id-col .cell { font-weight: 550; }
table.grid td.id-col .cell-static { display: block; white-space: nowrap; color: var(--text); }
table.grid td.tags-col .cell { min-width: 6rem; }

/* The Rules editor has few, sparse rows (not an obvious spreadsheet at a glance), so its fields get a
   persistent visible border instead of the borderless "cell" style used in the dense product/box grids
   above — otherwise Tag A / Tag B / quantity read as static text rather than input fields. */
table.rules-grid td .cell {
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm);
    background: var(--surface);
    margin: 0.3rem 0.35rem;
    width: calc(100% - 0.7rem);
}
table.rules-grid td.rule-type-col select.cell { min-width: 9rem; }
table.rules-grid td .cell:hover { border-color: var(--border-hover); }
table.rules-grid td .cell:focus-visible {
    box-shadow: var(--ring);
    border-color: var(--primary);
}
table.rules-grid .cell:disabled {
    opacity: 0.5;
    background: var(--surface-2);
    border-style: dashed;
}

td.checkbox-cell { text-align: center; }
td.action-cell { width: 1%; padding: 0 0.4rem; text-align: center; }

/* ---------- Checkboxes ---------- */
input[type="checkbox"] {
    width: 1.05rem;
    height: 1.05rem;
    accent-color: var(--primary);
    cursor: pointer;
    vertical-align: middle;
}

/* Browsers apply a small default margin around checkboxes/radios (unlike text/number inputs and
   selects, which default to margin:0), so under the settings row's align-items:flex-end it sits a
   few px above the shared bottom edge of its siblings. Zero it out to make the box flush. */
.field-check input[type="checkbox"] { margin: 0; }

/* ---------- Buttons ---------- */
button {
    font: inherit;
    font-weight: 550;
    cursor: pointer;
    border: 1px solid transparent;
    border-radius: var(--radius-sm);
    padding: 0.5rem 0.9rem;
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    transition: background 0.12s ease, border-color 0.12s ease, box-shadow 0.12s ease, color 0.12s ease;
}
button:focus-visible { outline: none; box-shadow: var(--ring); }

/* secondary / default */
button.btn-secondary,
button:not(.btn-primary):not(.btn-ghost):not(.btn-icon) {
    background: var(--surface);
    border-color: var(--border-strong);
    color: var(--text);
}
button.btn-secondary:hover,
button:not(.btn-primary):not(.btn-ghost):not(.btn-icon):hover {
    background: var(--surface-3);
    border-color: var(--border-hover);
}

button.btn-primary {
    background: var(--primary);
    color: var(--on-primary);
    box-shadow: var(--shadow-xs);
}
button.btn-primary:hover { background: var(--primary-hover); }
button.btn-primary:active { background: var(--primary-active); }

button.btn-ghost {
    background: transparent;
    color: var(--primary);
    border-color: transparent;
    padding: 0.4rem 0.55rem;
}
button.btn-ghost:hover { background: var(--primary-weak); }

button.btn-icon {
    background: transparent;
    color: var(--text-subtle);
    border-color: transparent;
    padding: 0.4rem;
    border-radius: var(--radius-sm);
    line-height: 1;
}
button.btn-icon:hover { background: var(--danger-weak); color: var(--danger); }

.section-actions { margin-top: 0.85rem; }
.section-actions.spread { display: flex; align-items: center; justify-content: space-between; gap: 1rem; }
button.btn-ghost.danger { color: var(--danger); }
button.btn-ghost.danger:hover { background: var(--danger-weak); }

/* ---------- Modal (box type set picker) ---------- */
.modal-overlay {
    position: fixed;
    inset: 0;
    background: rgba(16, 24, 40, 0.45);
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 1.5rem;
    z-index: 50;
}
.modal {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-md);
    width: min(520px, 100%);
    max-height: 85vh;
    display: flex;
    flex-direction: column;
    overflow: hidden;
}
.modal-head { padding: 1.1rem 1.35rem 0.6rem; }
.modal-head h2 { font-size: 1.05rem; }
.modal-body { padding: 0.4rem 1.35rem 0.6rem; overflow-y: auto; }
.modal-actions {
    display: flex;
    justify-content: flex-end;
    gap: 0.6rem;
    padding: 0.9rem 1.35rem 1.1rem;
    border-top: 1px solid var(--border);
}
.set-options { display: flex; flex-direction: column; gap: 0.45rem; }
.set-option {
    display: flex;
    align-items: baseline;
    gap: 0.6rem;
    padding: 0.55rem 0.7rem;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    cursor: pointer;
}
.set-option:hover { background: var(--surface-3); }
.set-option.selected { border-color: var(--primary); background: var(--primary-weak); }
.set-option .set-code { font-weight: 650; }
.set-option .set-count { color: var(--text-muted); font-size: 0.82rem; }
.set-option .set-preview {
    margin-left: auto;
    color: var(--text-subtle);
    font-size: 0.78rem;
    text-align: right;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
/* Product-set rows are click-to-add buttons (additive picker). */
button.set-add {
    font: inherit;
    color: inherit;
    text-align: left;
    width: 100%;
    background: var(--surface);
}
.set-option .set-status {
    margin-left: 0.5rem;
    flex: none;
    width: 1.1rem;
    text-align: center;
    font-weight: 700;
    color: var(--primary);
}
.set-option .set-status.in { color: var(--success); }
.modal-hint { margin: 0 0 0.7rem; color: var(--text-muted); font-size: 0.85rem; }

.pack-bar {
    display: flex;
    align-items: center;
    gap: 1rem;
    margin-top: 1.35rem;
    /* When scrolled to the top after a pack, stay clear of the sticky header. */
    scroll-margin-top: 4.5rem;
}
button.btn-lg { padding: 0.65rem 1.5rem; font-size: 0.98rem; font-weight: 600; }
button:disabled { opacity: 0.55; cursor: not-allowed; }
.validation-list { margin: 0; padding-left: 1.1rem; display: flex; flex-direction: column; gap: 0.2rem; }

/* ---------- Disclosure (Paste JSON) ---------- */
details.json {
    margin-top: 1.1rem;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: var(--surface-2);
}
details.json > summary {
    cursor: pointer;
    padding: 0.7rem 1rem;
    font-weight: 550;
    font-size: 0.88rem;
    color: var(--text-muted);
    list-style: none;
    display: flex;
    align-items: center;
    gap: 0.5rem;
}
details.json > summary::-webkit-details-marker { display: none; }
details.json > summary::before { content: "›"; font-size: 1.1rem; transition: transform 0.15s ease; display: inline-block; }
details.json[open] > summary::before { transform: rotate(90deg); }
details.json .json-body { padding: 0 1rem 1rem; }
details.json textarea {
    width: 100%;
    font-family: var(--font-mono);
    font-size: 0.82rem;
    resize: vertical;
    background: var(--surface);
}

/* ---------- Help indicator (sample JSON) ---------- */
.help {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.15rem;
    height: 1.15rem;
    border-radius: 50%;
    border: 1px solid var(--border);
    background: var(--surface);
    color: var(--text-muted);
    font-size: 0.72rem;
    font-weight: 700;
    cursor: help;
}
.help:hover, .help:focus-visible {
    color: var(--primary);
    border-color: var(--primary);
    outline: none;
}
.help-pop {
    position: absolute;
    top: calc(100% + 8px);
    left: 0;
    z-index: 50;
    isolation: isolate;
    width: min(30rem, 78vw);
    padding: 0.75rem 0.85rem;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: var(--surface);
    box-shadow: var(--shadow-md, 0 8px 24px rgba(0,0,0,0.12));
    opacity: 0;
    visibility: hidden;
    transform: translateY(-4px);
    transition: opacity 0.12s ease, transform 0.12s ease, visibility 0.12s;
    text-align: left;
}
.help-pop::before {
    content: "";
    position: absolute;
    top: -8px; left: 0; right: 0; height: 8px;
}
.help:hover .help-pop,
.help:focus .help-pop,
.help:focus-within .help-pop {
    opacity: 1;
    visibility: visible;
    transform: translateY(0);
}
.help-pop-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
    font-size: 0.78rem;
    font-weight: 600;
    color: var(--text);
    margin-bottom: 0.5rem;
}
.btn-sm {
    padding: 0.25rem 0.6rem;
    font-size: 0.75rem;
    line-height: 1.2;
}
.help-pop pre {
    margin: 0;
    max-height: 20rem;
    overflow: auto;
    font-family: var(--font-mono);
    font-size: 0.76rem;
    line-height: 1.45;
    color: var(--text);
    white-space: pre;
}

/* ---------- Feedback ---------- */
.loading {
    display: inline-flex; align-items: center; gap: 0.5rem;
    color: var(--text-muted); font-style: normal; margin-top: 1.25rem;
}
.loading::before {
    content: ""; width: 0.9rem; height: 0.9rem; border-radius: 50%;
    border: 2px solid var(--border-strong); border-top-color: var(--primary);
    animation: spin 0.7s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

/* Calculating panel: message + Cancel + a 0→10s progress bar (10s = solver timeout). */
.calculating {
    margin-top: 1.25rem;
    max-width: 460px;
    display: flex;
    flex-direction: column;
    gap: 0.55rem;
}
.calc-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    color: var(--text);
    font-size: 0.92rem;
}
.calc-progress {
    height: 8px;
    border-radius: 999px;
    background: var(--surface-3);
    border: 1px solid var(--border);
    overflow: hidden;
}
.calc-progress-fill {
    height: 100%;
    width: 0;
    background: var(--primary);
    /* Fill over the 10s solver deadline; if the request runs longer (network, 20s HTTP timeout),
       pulse at 100% so the UI never looks frozen. */
    animation: calc-fill 10s linear forwards, calc-pulse 1.4s ease-in-out 10s infinite;
}
@keyframes calc-fill { from { width: 0; } to { width: 100%; } }
@keyframes calc-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.45; } }
.calc-scale {
    display: flex;
    justify-content: space-between;
    color: var(--text-muted);
    font-size: 0.72rem;
    font-variant-numeric: tabular-nums;
}

.banner {
    margin-top: 1.25rem;
    padding: 0.8rem 1rem;
    border-radius: var(--radius);
    font-size: 0.9rem;
    border: 1px solid;
}
.banner.error { background: var(--danger-weak); border-color: var(--danger-border); color: var(--danger); }

.error { color: var(--danger); }
.field-error { display: block; margin-top: 0.5rem; color: var(--danger); font-size: 0.85rem; }
.field-notice { display: block; margin-top: 0.5rem; color: var(--success); font-size: 0.85rem; }

/* ---------- Result ---------- */
.result { margin-top: 1.5rem; }
.result-summary {
    display: flex; align-items: center; gap: 0.6rem;
    margin-bottom: 1rem; font-size: 1.05rem;
}
.result-summary .elapsed { color: var(--text-muted); font-size: 0.9rem; }
.badge {
    display: inline-flex; align-items: center; gap: 0.35rem;
    background: var(--success-weak); color: var(--success);
    border-radius: 999px; padding: 0.15rem 0.7rem;
    font-size: 0.82rem; font-weight: 600;
}

.box-cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(210px, 1fr)); gap: 0.9rem; }
.box-card {
    position: relative;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 0.9rem 1rem;
    background: var(--surface);
    box-shadow: var(--shadow-xs);
    display: flex; flex-direction: column; gap: 0.55rem;
    cursor: default;
    transition: border-color 0.12s ease, box-shadow 0.12s ease;
}
/* Parcel cards carry their box type's colour (inline border-color, matching the 3D outline). */
.box-card.typed { border-width: 2px; }
.box-card:hover { box-shadow: var(--shadow-sm); }
.box-card:focus-visible { outline: none; box-shadow: var(--ring); }
.box-card:hover, .box-card:focus-within { z-index: 30; }
.box-card .box-name { font-weight: 650; font-size: 0.95rem; }
.box-card .box-meta { display: flex; flex-wrap: wrap; gap: 0.15rem 1rem; font-size: 0.82rem; color: var(--text-muted); }
.box-card .box-meta b { color: var(--text); font-weight: 600; }
.box-card .box-volume { margin-top: 0.3rem; font-size: 0.78rem; color: var(--text-subtle); }

/* ---------- Box hover/focus popover ---------- */
.box-popover {
    position: absolute;
    top: calc(100% + 8px);
    left: 0;
    min-width: 100%;
    width: max-content;
    max-width: min(360px, 90vw);
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow-md);
    padding: 0.85rem 0.95rem;
    z-index: 40;
    opacity: 0;
    visibility: hidden;
    transform: translateY(-4px);
    transition: opacity 0.14s ease, transform 0.14s ease, visibility 0.14s;
}
/* transparent bridge so the pointer can cross the gap without losing hover */
.box-popover::before {
    content: "";
    position: absolute;
    top: -8px; left: 0; right: 0; height: 8px;
}
.box-card:hover .box-popover,
.box-card:focus-within .box-popover {
    opacity: 1;
    visibility: visible;
    transform: translateY(0);
}
.box-popover .pop-head {
    display: flex; align-items: baseline; justify-content: space-between;
    gap: 1rem; margin-bottom: 0.5rem;
}
.box-popover .box-meta { margin-bottom: 0.6rem; }
.pop-table-wrap { max-height: 210px; overflow-y: auto; border-top: 1px solid var(--border); }
table.pop-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.82rem;
    font-variant-numeric: tabular-nums;
}
table.pop-table thead th {
    position: sticky; top: 0;
    text-align: left;
    font-size: 0.68rem; font-weight: 600; letter-spacing: 0.03em; text-transform: uppercase;
    color: var(--text-muted); background: var(--surface-2);
    padding: 0.4rem 0.5rem; white-space: nowrap;
}
table.pop-table th.num, table.pop-table td.num { text-align: right; }
table.pop-table tbody td { padding: 0.4rem 0.5rem; border-top: 1px solid var(--border); white-space: nowrap; }
table.pop-table tbody tr:first-child td { border-top: none; }

@media (hover: none) {
    /* touch devices: reveal on focus (tap) since there is no hover */
    .box-card:focus-within .box-popover { opacity: 1; visibility: visible; transform: translateY(0); }
}

.fill-track { position: relative; height: 6px; border-radius: 999px; background: var(--surface-3); overflow: hidden; }
.fill-bar { position: absolute; top: 0; left: 0; height: 100%; background: linear-gradient(90deg, var(--primary), var(--primary-hover)); }
.fill-bar-cut { position: absolute; top: 0; height: 100%; background: var(--success); }
.fill-label { font-size: 0.78rem; color: var(--text-muted); }
.fill-label-cut { font-size: 0.78rem; color: var(--success); }
.pop-fill-group { display: flex; flex-direction: column; align-items: flex-end; gap: 0.1rem; }
.stack-label { font-size: 0.78rem; color: var(--text-muted); margin-top: 0.35rem; }
.stack-label b { color: var(--text); font-weight: 600; }
.cut-label { font-size: 0.78rem; color: var(--primary); font-weight: 600; margin-top: 0.2rem; }

.failure {
    margin-top: 1.5rem;
    padding: 1rem 1.15rem;
    border-radius: var(--radius);
    background: var(--danger-weak);
    border: 1px solid var(--danger-border);
    color: var(--danger);
    font-size: 0.95rem;
}
.failure-detail { margin-top: 0.7rem; font-size: 0.88rem; color: var(--text); }
.failure-list { margin: 0.35rem 0 0; padding-left: 1.2rem; display: flex; flex-direction: column; gap: 0.2rem; }
.failure-note { margin-top: 0.5rem; color: var(--text-muted); font-size: 0.82rem; }
.boxless-note { color: var(--text-muted); }
.boxless-note .failure-list { color: var(--text-muted); }

/* ---------- Boxless parcel card ---------- */
.boxless-card { border-style: dashed; border-color: var(--border-strong); }
.boxless-tag {
    align-self: flex-start;
    display: inline-flex; align-items: center;
    background: var(--success-weak); color: var(--success);
    border-radius: 999px; padding: 0.1rem 0.55rem;
    font-size: 0.72rem; font-weight: 600;
}

/* ---------- Cost explanation ---------- */
.cost-explain {
    margin-top: 1.5rem;
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    background: var(--surface-2);
    padding: 1.1rem 1.25rem;
}
.cost-head { font-weight: 650; font-size: 0.98rem; margin-bottom: 0.35rem; }
.score-strip {
    display: flex;
    flex-wrap: wrap;
    gap: 0.6rem;
    margin: 0.6rem 0 1rem;
}
.score-tile {
    flex: 1 1 auto;
    min-width: 6rem;
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
    padding: 0.6rem 0.8rem;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: var(--surface);
}
.score-tile .score-value { font-weight: 650; font-size: 1.05rem; font-variant-numeric: tabular-nums; }
.score-tile .score-label { color: var(--text-muted); font-size: 0.78rem; }
.cost-intro { margin: 0 0 0.9rem; color: var(--text-muted); font-size: 0.88rem; max-width: 62ch; }
.cost-rules { margin: 0; padding-left: 1.4rem; display: flex; flex-direction: column; gap: 0.6rem; }
.cost-rules .rule-label { font-weight: 600; }
.cost-rules .rule-value { margin-left: 0.5rem; font-variant-numeric: tabular-nums; }
.cost-rules .rule-note { display: block; color: var(--text-muted); font-size: 0.82rem; margin-top: 0.1rem; }

/* ---------- 3D viewer ---------- */
.viewer-wrap { margin-top: 1.5rem; }
/* Canvas on the left, control panel (slider, download, legends) on the right. */
.viewer-layout {
    display: grid;
    grid-template-columns: minmax(0, 2.2fr) minmax(240px, 1fr);
    gap: 1.5rem;
    align-items: start;
}
.viewer-canvas-col { min-width: 0; }
.viewer-panel { min-width: 0; display: flex; flex-direction: column; gap: 1rem; }
.viewer {
    width: 100%;
    aspect-ratio: 16 / 9;
    max-height: 600px;
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    background: var(--viewer-bg);
    box-shadow: var(--shadow-sm);
}
.hint { color: var(--text-muted); font-size: 0.83rem; margin-top: 0.5rem; }

@media (max-width: 1100px) {
    .viewer-layout { grid-template-columns: 1fr; }
}

/* Colour legends (match the per-product fills and per-box-type outlines in the 3D scene). */
.legend-block { display: flex; flex-direction: column; gap: 0.35rem; }
.legend-title { font-size: 0.78rem; font-weight: 600; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.03em; }
.legend-hint { font-weight: 400; text-transform: none; letter-spacing: normal; }
.viewer-legend {
    display: flex;
    flex-wrap: wrap;
    gap: 0.35rem 0.9rem;
}
.legend-item { display: inline-flex; align-items: center; gap: 0.4rem; font-size: 0.82rem; }
.legend-swatch {
    min-width: 1.5rem;
    height: 1.25rem;
    padding: 0 0.2rem;
    border-radius: 3px;
    border: 1px solid rgba(0, 0, 0, 0.15);
    flex: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 0.68rem;
    font-weight: 700;
    line-height: 1;
    color: rgba(255, 255, 255, 0.92);
    text-shadow: 0 0 3px rgba(0, 0, 0, 0.45);
}
.legend-swatch.outline { background: transparent; border-width: 2px; border-style: solid; text-shadow: none; }
.legend-label { color: var(--text); }

/* Product legend entries are toggle buttons (click to show/hide in the 3D scene). */
button.legend-toggle {
    font: inherit;
    background: transparent;
    border: none;
    padding: 0.1rem 0.25rem;
    border-radius: var(--radius-sm);
    cursor: pointer;
}
button.legend-toggle:hover { background: var(--surface-3); }
button.legend-toggle:focus-visible { outline: none; box-shadow: var(--ring); }
button.legend-toggle.off { opacity: 0.4; }
button.legend-toggle.off .legend-label { text-decoration: line-through; }

/* Narrow, plain-text variant of the help popover for the info tips. */
.info-tip { vertical-align: middle; }
.info-pop {
    width: min(20rem, 78vw);
    font-size: 0.8rem;
    font-weight: 400;
    line-height: 1.45;
    color: var(--text);
}

.gif-download { align-self: flex-start; }
.gif-download:disabled { opacity: 0.6; cursor: progress; }

/* ---------- Step-through controls ---------- */
.viewer-controls {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}
.step-slider {
    -webkit-appearance: none;
    appearance: none;
    width: 100%;
    height: 6px;
    border-radius: 999px;
    background: var(--surface-3);
    border: none;
    cursor: pointer;
    padding: 0;
}
.step-slider:focus-visible { outline: none; box-shadow: var(--ring); }
.step-slider::-webkit-slider-thumb {
    -webkit-appearance: none;
    appearance: none;
    width: 18px; height: 18px;
    border-radius: 50%;
    background: var(--primary);
    border: 2px solid #fff;
    box-shadow: var(--shadow-sm);
    cursor: pointer;
}
.step-slider::-moz-range-thumb {
    width: 18px; height: 18px;
    border-radius: 50%;
    background: var(--primary);
    border: 2px solid #fff;
    box-shadow: var(--shadow-sm);
    cursor: pointer;
}
.slider-track { position: relative; padding-bottom: 12px; }
.step-slider { position: relative; z-index: 1; }
.step-ticks {
    position: absolute;
    left: 9px; right: 9px; bottom: 0;
    display: flex;
    justify-content: space-between;
    pointer-events: none;
}
.step-ticks .tick {
    width: 2px; height: 8px;
    border-radius: 999px;
    background: var(--border-strong);
}
.step-ticks .tick.on { background: var(--primary); }
.step-caption { font-size: 0.88rem; color: var(--text-muted); }
.step-caption b { color: var(--text); font-weight: 600; }

/* ---------- Result model (JSON) ---------- */
.model-output { margin-top: 1.75rem; }
.model-output .model-actions { margin-top: 0.6rem; display: flex; justify-content: flex-end; }
.model-output .section-head { align-items: center; }
.model-json {
    width: 100%;
    font-family: var(--font-mono);
    font-size: 0.8rem;
    line-height: 1.5;
    color: var(--text);
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 0.85rem 1rem;
    resize: vertical;
    white-space: pre;
    overflow: auto;
}

@media (max-width: 640px) {
    .container { padding: 1.25rem 1rem 3rem; }
    .section { padding: 1rem; }
}

/* Disable the two looping/decorative animations (spinner rotation, post-timeout pulse) for users who
   request less motion. The 0->100% progress fill is left alone: it is one-shot ("forwards", not
   infinite) and conveys real elapsed-time information rather than decoration. */
@media (prefers-reduced-motion: reduce) {
    .loading::before { animation: none; }
    .calc-progress-fill { animation: calc-fill 10s linear forwards; }
}

/* ---------- API docs page ---------- */
.api-docs { max-width: 900px; margin: 0 auto; padding: 1.5rem 0 3rem; }
.api-docs .api-lead { color: var(--text-muted); font-size: 1.05rem; }
.api-docs h2 { font-size: 1.1rem; margin: 1.8rem 0 0.6rem; }
.api-actions { display: flex; flex-wrap: wrap; gap: 0.6rem; margin: 1.2rem 0; }
.api-code {
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    padding: 0.75rem 1rem;
    overflow-x: auto;
    font-family: var(--font-mono);
    font-size: 0.85rem;
    line-height: 1.5;
}
.api-table-wrap { overflow-x: auto; }
.api-table { width: 100%; border-collapse: collapse; font-size: 0.9rem; }
.api-table th, .api-table td {
    text-align: left;
    padding: 0.5rem 0.7rem;
    border-bottom: 1px solid var(--border);
    vertical-align: top;
}
.api-table th { color: var(--text-muted); font-weight: 600; }
.api-table td:first-child { font-family: var(--font-mono); white-space: nowrap; }
.nav-api { font-weight: 600; }
