/* KMZ Tasks - Design System v3
   Light palette matched to the KMZ logo — navy primary, yellow accent, cool neutrals */

:root {
    /* Brand — navy from the logo letters (lighter tint as primary; deep navy for text/borders when needed) */
    --brand-50:  #eff6ff;
    --brand-100: #dbeafe;
    --brand-200: #bfdbfe;
    --brand-500: #3b82f6;
    --brand-600: #2563eb;
    --brand-700: #1d4ed8;
    --brand: #2563eb;         /* lighter blue primary */
    --brand-dark: #1e3a8a;     /* logo navy — used for strong text on tinted bg */
    --brand-light: #93c5fd;
    --accent: #eab308;         /* yellow from the logo bars */
    --accent-light: #fef3c7;

    /* Neutrals */
    --white: #ffffff;
    --gray-25:  #fcfcfd;
    --gray-50:  #f9fafb;
    --gray-100: #f2f4f7;
    --gray-200: #e4e7ec;
    --gray-300: #d0d5dd;
    --gray-400: #98a2b3;
    --gray-500: #667085;
    --gray-600: #475467;
    --gray-700: #344054;
    --gray-800: #1d2939;
    --gray-900: #101828;

    /* Semantic */
    --success: #10b981;
    --success-light: #d1fae5;
    --success-dark: #065f46;
    --warning: #f79009;
    --warning-light: #fef0c7;
    --danger:  #d92d20;
    --danger-light: #fee4e2;
    --info: #2e90fa;
    --info-light: #d1e9ff;

    /* Surfaces */
    --bg: #f5f7f9;
    --surface: #ffffff;
    --surface-2: #f9fafb;
    --border: #e4e7ec;
    --border-strong: #d0d5dd;

    /* Text */
    --text: #101828;
    --text-2: #344054;
    --text-muted: #667085;
    --text-subtle: #98a2b3;

    /* Radius */
    --r-sm: 6px;
    --r: 8px;
    --r-md: 10px;
    --r-lg: 12px;
    --r-xl: 16px;
    --r-full: 999px;

    /* Shadows */
    --shadow-xs: 0 1px 2px rgba(16,24,40,.05);
    --shadow-sm: 0 1px 3px rgba(16,24,40,.10), 0 1px 2px rgba(16,24,40,.06);
    --shadow:    0 4px 8px -2px rgba(16,24,40,.10), 0 2px 4px -2px rgba(16,24,40,.06);
    --shadow-lg: 0 12px 16px -4px rgba(16,24,40,.08), 0 4px 6px -2px rgba(16,24,40,.03);
    --shadow-xl: 0 20px 24px -4px rgba(16,24,40,.08), 0 8px 8px -4px rgba(16,24,40,.03);
    --shadow-2xl: 0 25px 50px -12px rgba(16,24,40,.25);

    /* Motion */
    --ease-out: cubic-bezier(.16, 1, .3, 1);
    --dur: 180ms;

    /* Focus */
    --focus-ring: 0 0 0 4px rgba(37, 99, 235, 0.18);
}

/* -------------------------------------------------------------------------
 * Dark theme
 * -------------------------------------------------------------------------
 * Applied when <html> or <body> carries data-theme="dark". Overrides the
 * semantic tokens above; anywhere the stylesheet reads var(--surface) /
 * var(--text) / var(--gray-50) etc. automatically follows.
 *
 * Neutrals invert (surfaces get darker, text gets lighter). Brand hues
 * stay recognisable but a hair softer so blue-on-navy doesn't glare.
 */
[data-theme="dark"] {
    color-scheme: dark;

    /* Brand — lighten primary so blue reads against a dark background;
       keep --brand-dark identical (still used as an accent). */
    --brand-50:  #172554;
    --brand-100: #1e3a8a;
    --brand-200: #1d4ed8;
    --brand-500: #60a5fa;
    --brand-600: #93c5fd;
    --brand-700: #bfdbfe;
    --brand: #60a5fa;
    --brand-light: #3b82f6;
    --accent-light: #422006;

    /* Neutrals — inverted ramp. white/gray-25 stay opaque so places that
       explicitly want "white" (badges, dropdown surfaces) still contrast. */
    --white: #0b1220;
    --gray-25:  #0f172a;
    --gray-50:  #0f172a;
    --gray-100: #1e293b;
    --gray-200: #334155;
    --gray-300: #475569;
    --gray-400: #64748b;
    --gray-500: #94a3b8;
    --gray-600: #cbd5e1;
    --gray-700: #e2e8f0;
    --gray-800: #f1f5f9;
    --gray-900: #f8fafc;

    /* Semantic — bumped saturation so success/warn/danger still read at low bg. */
    --success-light: #052e1a;
    --warning-light: #422006;
    --danger-light:  #4c0519;
    --info-light:    #0c2f52;

    /* Surfaces + borders */
    --bg: #0b1220;
    --surface: #111827;
    --surface-2: #1e293b;
    --border: #1f2937;
    --border-strong: #334155;

    /* Text */
    --text: #e5e7eb;
    --text-2: #cbd5e1;
    --text-muted: #94a3b8;
    --text-subtle: #64748b;

    /* Shadows — dark bg swallows soft light shadows, so use higher contrast. */
    --shadow-xs: 0 1px 2px rgba(0,0,0,.4);
    --shadow-sm: 0 1px 3px rgba(0,0,0,.5), 0 1px 2px rgba(0,0,0,.35);
    --shadow:    0 4px 8px -2px rgba(0,0,0,.55), 0 2px 4px -2px rgba(0,0,0,.35);
    --shadow-lg: 0 12px 16px -4px rgba(0,0,0,.55), 0 4px 6px -2px rgba(0,0,0,.35);
    --shadow-xl: 0 20px 24px -4px rgba(0,0,0,.55), 0 8px 8px -4px rgba(0,0,0,.4);
    --shadow-2xl: 0 25px 50px -12px rgba(0,0,0,.7);

    --focus-ring: 0 0 0 4px rgba(96, 165, 250, 0.28);
}

/* Any raw white backgrounds anywhere in the app pick up the theme too. */
[data-theme="dark"] img[src*="favicon"] { filter: brightness(0.95); }

/* The header now swaps src to logo-dark.svg on dark mode via inline JS in
   includes/header.php, so no CSS filter needed there. The login card's
   .brand-logo-lg still uses the navy horizontal logo; on dark mode the
   auth panel bg is already dark-blue so the navy reads fine — no filter. */

/* --------------------------------------------------------------------- */
/* Hardcoded light tints — override per-widget for dark mode.            */
/* Chat bubbles: mine (was light green) + others (var(--surface) already dark). */
[data-theme="dark"] .chat-mine .chat-bubble {
    background: rgba(16, 185, 129, 0.12);
    border-color: rgba(16, 185, 129, 0.4);
    color: var(--text);
}
[data-theme="dark"] .chat-mine .chat-body,
[data-theme="dark"] .chat-body { color: var(--text); }
[data-theme="dark"] .chat-meta .muted,
[data-theme="dark"] .chat-meta .tiny { color: var(--text-muted); }

/* History timeline chips */
[data-theme="dark"] .hist-sign-success {
    background: rgba(16, 185, 129, 0.14); color: #6ee7b7; border-left-color: #10b981;
}
[data-theme="dark"] .hist-sign-info {
    background: rgba(59, 130, 246, 0.14); color: #93c5fd; border-left-color: #3b82f6;
}
[data-theme="dark"] .hist-sign-warn {
    background: rgba(234, 179, 8, 0.14); color: #fde68a; border-left-color: #eab308;
}

/* SLA due chips (large + mini) */
[data-theme="dark"] .stage-due-chip.stage-due-ok,
[data-theme="dark"] .stage-due-mini-ok {
    background: rgba(16, 185, 129, 0.12); color: #6ee7b7; border-color: rgba(16, 185, 129, 0.4);
}
[data-theme="dark"] .stage-due-chip.stage-due-warn,
[data-theme="dark"] .stage-due-mini-warn {
    background: rgba(234, 179, 8, 0.14); color: #fde68a; border-color: rgba(234, 179, 8, 0.4);
}
[data-theme="dark"] .stage-due-chip.stage-due-late,
[data-theme="dark"] .stage-due-mini-late {
    background: rgba(220, 38, 38, 0.16); color: #fca5a5; border-color: rgba(220, 38, 38, 0.4);
}

/* Priority chips */
[data-theme="dark"] .prio-chip-low {
    background: rgba(16, 185, 129, 0.12); color: #6ee7b7; border-color: rgba(16, 185, 129, 0.4);
}
[data-theme="dark"] .prio-chip-medium {
    background: rgba(234, 179, 8, 0.14); color: #fde68a; border-color: rgba(234, 179, 8, 0.4);
}
[data-theme="dark"] .prio-chip-urgent {
    background: rgba(220, 38, 38, 0.16); color: #fca5a5; border-color: rgba(220, 38, 38, 0.4);
}

/* Worker-done pill variants */
[data-theme="dark"] .worker-done-pill {
    background: rgba(16, 185, 129, 0.14); color: #6ee7b7; border-color: rgba(16, 185, 129, 0.4);
}
[data-theme="dark"] .worker-done-pill.trade-status-in_progress {
    background: rgba(59, 130, 246, 0.16); color: #93c5fd; border-color: rgba(59, 130, 246, 0.4);
}
[data-theme="dark"] .worker-done-pill.trade-status-not_started {
    background: var(--gray-100); color: var(--text-muted); border-color: var(--border);
}
[data-theme="dark"] .worker-done-pill.trade-status-blocked {
    background: rgba(220, 38, 38, 0.16); color: #fca5a5; border-color: rgba(220, 38, 38, 0.4);
}

/* Row highlight for worker-done cards */
[data-theme="dark"] .job-row.is-worker-done,
[data-theme="dark"] .my-row.is-worker-done {
    background: rgba(16, 185, 129, 0.08) !important;
    border-color: rgba(16, 185, 129, 0.35) !important;
}

/* Check-grid done row (BOM + basic + fittings share this) */
[data-theme="dark"] .check-grid-row.is-done {
    background: rgba(16, 185, 129, 0.12);
    border-color: rgba(16, 185, 129, 0.35);
}
[data-theme="dark"] .check-grid-row.is-done::before { color: #6ee7b7; }
[data-theme="dark"] .check-grid-row .cg-input,
[data-theme="dark"] .check-grid-row .cg-multi { background: var(--surface); }
[data-theme="dark"] .check-grid-empty {
    background: var(--gray-100); border-color: var(--border-strong);
}

/* Stock alert rows + banners */
[data-theme="dark"] .stock-alert-table tr.row-out {
    background: rgba(220, 38, 38, 0.12);
}
[data-theme="dark"] .stock-alert-table tr.row-low {
    background: rgba(234, 179, 8, 0.12);
}
[data-theme="dark"] .stock-alert-banner {
    background: rgba(220, 38, 38, 0.14); border-color: rgba(220, 38, 38, 0.4);
}
[data-theme="dark"] .stock-alert-banner:hover {
    background: rgba(220, 38, 38, 0.22); border-color: rgba(220, 38, 38, 0.5);
}
[data-theme="dark"] .stock-item.is-empty {
    background: rgba(220, 38, 38, 0.1);
}

/* QC "approved" style + generic status danger row (line 542) */
[data-theme="dark"] .qc-approval.qc-approved {
    background: rgba(16, 185, 129, 0.14); border-color: rgba(16, 185, 129, 0.4);
}

/* Any surface that legitimately wanted "white" should not stay white in dark
   mode. This catch-all fixes leftover hardcoded #fff surfaces (dropdowns etc). */
[data-theme="dark"] .wl-pop,
[data-theme="dark"] .notif-panel,
[data-theme="dark"] .modal-body {
    background: var(--surface);
    color: var(--text);
}

/* Textarea / input contrast bump so placeholder isn't ghosted out on --surface. */
[data-theme="dark"] input,
[data-theme="dark"] textarea,
[data-theme="dark"] select {
    background: var(--surface);
    color: var(--text);
    border-color: var(--border-strong);
}
[data-theme="dark"] input::placeholder,
[data-theme="dark"] textarea::placeholder { color: var(--text-subtle); }

/* Screenshot / print always uses light. */
@media print {
    :root[data-theme="dark"], [data-theme="dark"] body { background: #fff; color: #000; }
}

@media (prefers-reduced-motion: reduce) {
    * { animation-duration: 0.001ms !important; transition-duration: 0.001ms !important; }
}

* { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }
*::selection { background: var(--brand-200); color: var(--brand-dark); }

html, body {
    margin: 0;
    padding: 0;
    /* Native OS font stack — SF on iOS/macOS, Segoe UI on Windows, Roboto
       on Android. Feels native everywhere and skips the Google Fonts
       network hop that Inter needed. */
    font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Text', 'SF Pro Display', 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    text-rendering: optimizeLegibility;
    font-feature-settings: 'cv02', 'cv03', 'cv04', 'cv11';
    background: var(--bg);
    color: var(--text);
    font-size: 14px;
    line-height: 1.5;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}

a { color: var(--brand); text-decoration: none; transition: color var(--dur) var(--ease-out); }
a:hover { color: var(--brand-dark); }

h1, h2, h3, h4, h5, h6 { margin: 0; font-weight: 600; letter-spacing: -0.01em; color: var(--text); }
h1 { font-size: 24px; line-height: 1.25; }
h2 { font-size: 20px; line-height: 1.3; }
h3 { font-size: 16px; line-height: 1.35; }

.muted { color: var(--text-muted); }
.small { font-size: 12px; }
.tiny  { font-size: 11px; }

/* ===== Icons ===== */
svg { display: inline-block; vertical-align: middle; flex-shrink: 0; }
.i-inline { margin-right: 6px; }

/* ===== Buttons ===== */
.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    padding: 8px 14px;
    min-height: 36px;
    border: 1px solid var(--border);
    background: var(--surface);
    color: var(--text-2);
    border-radius: var(--r);
    font-size: 14px;
    font-weight: 500;
    cursor: pointer;
    transition: all var(--dur) var(--ease-out);
    text-decoration: none;
    line-height: 1;
    white-space: nowrap;
    user-select: none;
}
.btn:hover { background: var(--gray-50); border-color: var(--border-strong); }
.btn:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.btn:active { transform: translateY(1px); }
.btn:disabled { opacity: .5; cursor: not-allowed; }

.btn-primary { background: var(--brand); color: #fff; border-color: var(--brand); }
.btn-primary:hover { background: var(--brand-dark); border-color: var(--brand-dark); color: #fff; }

.btn-success { background: var(--success); color: #fff; border-color: var(--success); }
.btn-success:hover { background: var(--success-dark); border-color: var(--success-dark); color: #fff; }

.btn-danger  { background: var(--danger); color: #fff; border-color: var(--danger); }
.btn-danger:hover { background: #b42318; border-color: #b42318; color: #fff; }

.btn-ghost { background: transparent; border-color: transparent; color: var(--text-2); }
.btn-ghost:hover { background: var(--gray-100); }

.btn-block { width: 100%; }
.btn-sm { padding: 6px 10px; min-height: 30px; font-size: 13px; }
.btn-lg { padding: 10px 18px; min-height: 44px; font-size: 15px; }
.btn-icon { padding: 8px; }

/* ===== Icon button (single icon, no text) ===== */
.icon-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    border-radius: var(--r-sm);
    background: transparent;
    border: none;
    color: var(--text-muted);
    cursor: pointer;
    transition: all var(--dur) var(--ease-out);
}
.icon-btn:hover { background: var(--gray-100); color: var(--text); }
.icon-btn:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.icon-btn.danger:hover { background: var(--danger-light); color: var(--danger); }

/* ===== Forms ===== */
input, textarea, select {
    font-family: inherit;
    font-size: 14px;
    padding: 9px 12px;
    border: 1px solid var(--border-strong);
    border-radius: var(--r);
    background: var(--surface);
    color: var(--text);
    width: 100%;
    min-height: 38px;
    transition: border-color var(--dur), box-shadow var(--dur);
}
input:focus, textarea:focus, select:focus {
    outline: none;
    border-color: var(--brand-light);
    box-shadow: 0 0 0 4px rgba(82, 183, 136, 0.15);
}
input::placeholder, textarea::placeholder { color: var(--text-subtle); }
textarea { min-height: 76px; resize: vertical; line-height: 1.5; }

label {
    display: block;
    font-weight: 500;
    margin-bottom: 14px;
    color: var(--text-2);
    font-size: 13px;
}
/* `display: block` above beats the hidden attribute's UA `display: none`, so
   toggling `el.hidden = true` on a <label> did nothing. There's no global
   [hidden] reset in this file (each component declares its own), so labels
   need one explicitly — e.g. the quote modal hides the single "Freight
   charges" field when the per-cabin freight rows take over. */
label[hidden] { display: none !important; }
label > input, label > textarea, label > select { margin-top: 6px; }

.alert {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    background: var(--danger-light);
    border: 1px solid #fecdca;
    color: #b42318;
    padding: 10px 12px;
    border-radius: var(--r);
    margin-bottom: 16px;
    font-size: 13px;
}

/* ===== Auth ===== */
html:has(.auth-page), body:has(.auth-page) { min-height: 100%; }
.auth-page {
    /* dvh > vh — vh on desktop Chrome can under-report by the URL/tab-bar
       height during animation, leaving the left panel visibly short. */
    min-height: 100dvh;
    display: grid;
    grid-template-columns: 1fr 1fr;
    align-items: stretch;
    background: var(--surface);
}
.auth-page .auth-visual {
    /* Lighter navy-to-sky gradient matching the KMZ logo blue. */
    background: linear-gradient(140deg, #1e3a8a 0%, #2563eb 45%, #3b82f6 100%);
    color: #fff;
    padding: 48px;
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    position: relative;
    overflow: hidden;
    min-height: 100dvh;
}
.auth-visual::before {
    content: '';
    position: absolute;
    inset: -50% -20% auto auto;
    width: 500px; height: 500px;
    background: radial-gradient(circle, rgba(255,255,255,.14), transparent 60%);
    pointer-events: none;
}
/* Subtle glow at the bottom-left too so the whole panel feels lit, not flat. */
.auth-visual::after {
    content: '';
    position: absolute;
    inset: auto auto -30% -20%;
    width: 420px; height: 420px;
    background: radial-gradient(circle, rgba(255,255,255,.10), transparent 65%);
    pointer-events: none;
}
.auth-visual h2 { color: #fff; font-size: 36px; line-height: 1.15; max-width: 380px; letter-spacing: -0.02em; }
.auth-visual p { color: rgba(255,255,255,.75); font-size: 16px; max-width: 380px; margin-top: 12px; }
.auth-visual .stat-row {
    display: flex;
    gap: 32px;
    z-index: 1;
    position: relative;
}
.auth-visual .stat-row div { color: rgba(255,255,255,.85); }
.auth-visual .stat-row strong { display: block; font-size: 22px; color: #fff; }

.auth-form-wrap {
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 40px;
    position: relative;
    overflow: hidden;
    /* Warm neutral base — softer than pure white so the card and background
       have a visible edge. Two low-opacity radial glows in the KMZ brand
       colors give the panel a "welcoming" feel on mobile where the split
       navy panel disappears. */
    background:
        radial-gradient(circle at 12% 90%, rgba(37, 99, 235, 0.08), transparent 55%),
        radial-gradient(circle at 88% 10%, rgba(234, 179, 8, 0.10),  transparent 50%),
        radial-gradient(circle at 50% 50%, rgba(255, 255, 255, 0.6), rgba(249, 250, 251, 0) 70%),
        #ffffff;
}
/* Subtle grid mesh — draws attention without competing with the card. */
.auth-form-wrap::before {
    content: '';
    position: absolute;
    inset: 0;
    background-image:
        linear-gradient(rgba(37,99,235,0.05) 1px, transparent 1px),
        linear-gradient(90deg, rgba(37,99,235,0.05) 1px, transparent 1px);
    background-size: 48px 48px;
    mask-image: radial-gradient(circle at center, black 30%, transparent 80%);
    -webkit-mask-image: radial-gradient(circle at center, black 30%, transparent 80%);
    pointer-events: none;
    opacity: 0.9;
}
[data-theme="dark"] .auth-form-wrap {
    background:
        radial-gradient(circle at 12% 90%, rgba(37, 99, 235, 0.14), transparent 55%),
        radial-gradient(circle at 88% 10%, rgba(234, 179, 8, 0.12), transparent 50%),
        var(--bg);
}
[data-theme="dark"] .auth-form-wrap::before {
    background-image:
        linear-gradient(rgba(96,165,250,0.09) 1px, transparent 1px),
        linear-gradient(90deg, rgba(96,165,250,0.09) 1px, transparent 1px);
}
.auth-card { position: relative; z-index: 1; }
.auth-card {
    width: 100%;
    max-width: 380px;
}
.auth-card .brand { margin-bottom: 32px; }
.auth-card .brand-logo-lg {
    display: block;
    /* Horizontal SVG. Same width as the inputs beneath: both are 100% of
       the card (380px cap). box-sizing to be safe. If the SVG's viewBox
       has any built-in whitespace padding the logo will still look inset
       vs the inputs — no CSS trick fixes that without cropping the SVG. */
    width: 100%;
    max-width: 100%;
    height: auto;
    box-sizing: border-box;
    padding: 0;
    object-fit: contain;
    margin: 0 auto 20px;
}
.auth-card h1 { font-size: 26px; margin: 0 0 6px; }
.auth-card .brand p { color: var(--text-muted); margin: 0; font-size: 15px; }
/* Only visible when the desktop split-panel visual is collapsed (mobile). */
.auth-mobile-only { display: none; }
.auth-tagline {
    color: var(--brand-dark);
    background: linear-gradient(135deg, rgba(37,99,235,0.08), rgba(234,179,8,0.10));
    border: 1px solid rgba(37,99,235,0.15);
    padding: 8px 12px;
    border-radius: var(--r);
    text-align: center;
    margin: 0 0 20px;
    font-weight: 500;
}
[data-theme="dark"] .auth-tagline {
    color: #93c5fd;
    background: linear-gradient(135deg, rgba(37,99,235,0.18), rgba(234,179,8,0.14));
    border-color: rgba(37,99,235,0.3);
}
.auth-form label { margin-bottom: 18px; }

/* PIN input: big spaced digits, easy on the eye */
.pin-input {
    letter-spacing: 0.6em;
    padding-left: 1.2em;
    text-align: center;
    font-size: 22px;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    -moz-appearance: textfield;
}
.pin-input::-webkit-outer-spin-button,
.pin-input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.auth-alt { text-align: center; margin: 24px 0 0; color: var(--text-muted); font-size: 13px; }

@media (max-width: 900px) {
    .auth-page { grid-template-columns: 1fr; }
    .auth-page .auth-visual { display: none; }
    /* Restore the mobile-only tagline chip once the left panel is gone. */
    .auth-mobile-only { display: block; }
}

/* ===== Stock page ===== */
/* Horizontally scrollable tab strip.
   Each tab is flex-shrink:0 so many categories can extend past the
   viewport without wrapping. A right-edge fade signals "more →" so users
   know to scroll. Scrollbar stays visible but slim. */
.domain-tabs {
    display: flex;
    flex-wrap: nowrap;
    gap: 8px;
    overflow-x: auto;
    overflow-y: hidden;
    padding: 2px 24px 6px 2px;   /* right padding leaves room for fade cue */
    margin-bottom: 20px;
    scrollbar-width: thin;
    -webkit-overflow-scrolling: touch;
    scroll-snap-type: x proximity;
    position: relative;
    -webkit-mask-image: linear-gradient(90deg, #000 0, #000 calc(100% - 24px), transparent 100%);
            mask-image: linear-gradient(90deg, #000 0, #000 calc(100% - 24px), transparent 100%);
}
.domain-tabs::-webkit-scrollbar { height: 6px; }
.domain-tabs::-webkit-scrollbar-thumb {
    background: var(--border-strong, #cbd5e1);
    border-radius: 3px;
}
.domain-tab {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 10px 14px;
    min-height: 44px;
    border-radius: var(--r);
    background: var(--surface);
    border: 1px solid var(--border);
    color: var(--text-2);
    text-decoration: none;
    font-weight: 500;
    font-size: 14px;
    white-space: nowrap;
    flex-shrink: 0;
    scroll-snap-align: start;
    transition: all var(--dur);
}
.domain-tab:hover { border-color: var(--border-strong); text-decoration: none; color: var(--text); }
.domain-tab.active {
    background: var(--tint, var(--brand));
    color: #fff;
    border-color: var(--tint, var(--brand));
}
.domain-tab .dot {
    width: 8px; height: 8px;
    border-radius: 50%;
    display: inline-block;
}
.domain-tab.active .dot { background: rgba(255,255,255,.6) !important; }
.domain-tab .count {
    background: rgba(0,0,0,.08);
    padding: 2px 8px;
    border-radius: var(--r-full);
    font-size: 11px;
    font-weight: 700;
    color: inherit;
}
.domain-tab.active .count { background: rgba(255,255,255,.22); }

.stock-list { display: flex; flex-direction: column; gap: 10px; }
.stock-item[hidden] { display: none !important; }
.stock-item {
    display: grid;
    grid-template-columns: 1fr auto auto;
    gap: 16px;
    align-items: center;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    padding: 14px 16px;
    box-shadow: var(--shadow-xs);
}
.stock-item.is-low  { border-left: 4px solid var(--warning); padding-left: 12px; }
/* Pale-red bg needs a dark text color for children — otherwise dark-mode's
   light --text renders as invisible ghost text on the pale-red background. */
.stock-item.is-empty { border-left: 4px solid var(--danger);  padding-left: 12px; background: #fef7f7; color: #7f1d1d; }
.stock-item.is-empty .stock-info h4,
.stock-item.is-empty .stock-meta,
.stock-item.is-empty .tiny,
.stock-item.is-empty .muted { color: #7f1d1d; }
.stock-info h4 { margin: 0 0 4px; font-size: 15px; font-weight: 600; }
.stock-meta { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
/* Cheapest-vendor price hint shown on the stock viewer under each item. */
.stock-price-hint {
    display: inline-flex; align-items: center; gap: 4px;
    padding: 2px 6px;
    background: var(--brand-50, #eff6ff);
    color: var(--brand, #1e3a8a);
    border: 1px solid var(--brand-light, #bfdbfe);
    border-radius: 4px;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
}
/* "Prices (N)" chip on Admin -> Stock Items */
.price-chip {
    padding: 4px 8px;
    font-size: 12px;
    background: var(--gray-50);
    border: 1px dashed var(--border);
    border-radius: 6px;
    cursor: pointer;
    text-align: left;
    color: var(--text);
    max-width: 220px;
    line-height: 1.3;
}
.price-chip:hover { background: var(--brand-50, #eff6ff); border-color: var(--brand-light, #bfdbfe); }
.pill-warn { background: var(--warning-light); color: #b45309; border: 1px solid #fde68a; padding: 2px 10px; border-radius: 999px; font-size: 11px; font-weight: 600; display: inline-flex; align-items: center; gap: 3px; }
.pill-danger { background: var(--danger-light); color: #b42318; border: 1px solid #fecdca; padding: 2px 10px; border-radius: 999px; font-size: 11px; font-weight: 600; display: inline-flex; align-items: center; gap: 3px; }
.pill-info { background: var(--info-light, #d1e9ff); color: #1e3a8a; border: 1px solid #93c5fd; padding: 2px 10px; border-radius: 999px; font-size: 11px; font-weight: 600; display: inline-flex; align-items: center; gap: 3px; }

/* Finishing & QC pre-dispatch checklist */
.fin-checklist { display: flex; flex-direction: column; gap: 8px; }
.fin-item {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    gap: 4px 12px;
    align-items: center;
    padding: 10px 12px;
    border: 1px solid var(--border);
    border-radius: var(--r);
    background: var(--surface);
    transition: background var(--dur), border-color var(--dur);
}
.fin-item.is-done { background: rgba(16, 185, 129, 0.08); border-color: rgba(16, 185, 129, 0.35); }
.fin-item-check {
    display: inline-flex; align-items: center; gap: 10px;
    cursor: pointer; min-width: 0;
}
.fin-item-check input[type="checkbox"] { width: 20px; height: 20px; margin: 0; flex-shrink: 0; }
.fin-item-check input[type="checkbox"]:disabled { cursor: not-allowed; }
.fin-item-label { font-weight: 500; font-size: 14px; word-break: break-word; }
.fin-item.is-done .fin-item-label { text-decoration: line-through; color: var(--text-muted); }
.fin-item-attribution {
    display: inline-flex; align-items: center; gap: 4px;
    color: #065f46;
    white-space: nowrap;
    justify-self: end;
}
.fin-item-notes {
    grid-column: 1 / -1;
    font-size: 13px;
    min-height: 34px;
}
[data-theme="dark"] .fin-item.is-done { background: rgba(16, 185, 129, 0.14); border-color: rgba(16, 185, 129, 0.4); }
[data-theme="dark"] .fin-item-attribution { color: #6ee7b7; }
@media (max-width: 640px) {
    .fin-item { grid-template-columns: 1fr; }
    .fin-item-attribution { justify-self: start; }
}

/* Disabled BOM tick (worker doesn't hold this row's trade) — muted so the
   contrast between enabled + locked rows is obvious. Tooltip on the input
   itself carries the explanation of who's allowed to tick. */
.check-grid-row .bom-worker-done:disabled { opacity: 0.35; cursor: help; }

/* Inline trade-lock selector inside the Admin BOM template table. Compact
   so it doesn't dominate the row; full-width of its cell for easy tapping. */
.bom-trade-select {
    width: 100%;
    padding: 4px 6px;
    font-size: 13px;
    border: 1px solid var(--border);
    border-radius: 4px;
    background: var(--surface);
    color: var(--text);
    min-height: 30px;
}
.bom-trade-select:focus { border-color: var(--brand); outline: none; box-shadow: 0 0 0 3px var(--brand-50); }
/* One-shot flash on programmatic .flash-attention add — used to nudge the
   eye toward a chip that just changed state (e.g. "Assigned & notified"
   pill after saving specialists). Fires once, then settles. */
@keyframes kmz-pill-flash {
    0%   { transform: scale(1);    box-shadow: 0 0 0 0   rgba(37, 99, 235, 0.5); }
    30%  { transform: scale(1.08); box-shadow: 0 0 0 8px rgba(37, 99, 235, 0); }
    100% { transform: scale(1);    box-shadow: 0 0 0 0   rgba(37, 99, 235, 0); }
}
.flash-attention { animation: kmz-pill-flash 1s var(--ease-out) 1; }
/* Dark theme override — the light-info bg + navy text combo becomes navy-on-navy
   under [data-theme="dark"]'s remapped --info-light, so hardcode a legible pair. */
[data-theme="dark"] .pill-info {
    background: rgba(59, 130, 246, 0.16);
    color: #93c5fd;
    border-color: rgba(59, 130, 246, 0.4);
}
.pill[hidden], .pill-warn[hidden], .pill-danger[hidden], .pill-info[hidden] { display: none !important; }
.stock-qty { text-align: right; }
.stock-qty .qty-value { font-size: 22px; font-weight: 700; font-variant-numeric: tabular-nums; color: var(--text); line-height: 1; }
.stock-qty .qty-unit { font-size: 11px; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.04em; margin-top: 4px; }
.stock-actions { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; }
.stock-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 40px;
    height: 40px;
    padding: 0 10px;
    border-radius: 8px;
    border: 1px solid var(--border);
    background: var(--surface);
    color: var(--text-2);
    font-family: inherit;
    font-size: 20px;
    font-weight: 700;
    line-height: 1;
    cursor: pointer;
    transition: all var(--dur);
}
.stock-btn:hover { border-color: var(--border-strong); background: var(--gray-50); }
.stock-btn:active { transform: translateY(1px); }
.stock-btn.stock-plus  { background: var(--brand-50); color: var(--brand);   border-color: var(--brand-light); }
.stock-btn.stock-plus:hover  { background: var(--brand); color: #fff; }
.stock-btn.stock-minus { background: var(--danger-light); color: var(--danger); border-color: #fecdca; }
.stock-btn.stock-minus:hover { background: var(--danger); color: #fff; }
.stock-btn.stock-edit  { background: var(--surface); }
.stock-btn.stock-del   { color: var(--danger); }
.stock-btn.stock-del:hover { background: var(--danger-light); border-color: #fecdca; }
.qty-value.qty-flash {
    animation: qty-flash 600ms ease-out;
}
@keyframes qty-flash {
    0%   { background: var(--brand-50); color: var(--brand); }
    100% { background: transparent; color: var(--text); }
}

/* =========================================================
   Stage pages (Phase 2) — each column has its own dedicated
   full-page view served by stage.php.
   ========================================================= */
.stage-page { background: var(--gray-100); min-height: 100vh; padding-bottom: calc(80px + env(safe-area-inset-bottom, 0)); }
.stage-bar {
    background: var(--surface);
    border-bottom: 1px solid var(--border);
    position: sticky; top: 53px; z-index: 20;
    padding: 12px 16px 0;
}
.stage-bar-inner { max-width: 1100px; margin: 0 auto; }
.stage-header-top { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.stage-header-top h1 { font-size: 20px; margin: 0; }
/* Push the stage-due chip to the far right of the header row */
.stage-header-due { margin-left: auto; font-size: 13px; padding: 4px 12px; }

/* Worker "✓ Complete" pill — shown on job rows and my-rows when the worker
   viewer has finished every trade role they hold on that card. */
.worker-done-pill {
    display: inline-flex;
    align-items: center;
    gap: 3px;
    padding: 3px 8px;
    border-radius: var(--r-full);
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.02em;
    background: #d1fae5;
    color: #065f46;
    border: 1px solid #6ee7b7;
    text-transform: uppercase;
    flex-shrink: 0;
    /* The pill drops into an auto-placed grid cell inside .job-row / .my-row
       (both are display:grid). Without justify-self it stretches to fill the
       1fr column, leaving a huge blank strip to the right of the text. */
    justify-self: start;
}
/* Trade-status variants — matches ms_status/painting_status/etc. enum values.
   'complete' inherits the default green look above; the other three restyle. */
.worker-done-pill.trade-status-in_progress {
    background: #dbeafe; color: #1e3a8a; border-color: #93c5fd;
}
.worker-done-pill.trade-status-not_started {
    background: var(--gray-50); color: var(--text-muted); border-color: var(--border);
}
.worker-done-pill.trade-status-blocked {
    background: #fef2f2; color: #991b1b; border-color: #fca5a5;
}
.job-row.is-worker-done,
.my-row.is-worker-done {
    background: #f0fdf4 !important;
    border-color: #a7f3d0 !important;
}
.job-row.is-worker-done .job-row-title,
.my-row.is-worker-done > div > div:first-child { opacity: 0.75; }
.stage-header-sub {
    display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
    margin: 8px 0 12px; color: var(--text-2); font-size: 14px;
}
.job-id-badge {
    color: var(--brand);
    font-size: 11px;
    font-weight: 600;
    background: transparent;
    padding: 1px 6px;
    border: 1px solid var(--brand-100, #dbeafe);
    border-radius: 4px;
    letter-spacing: 0.02em;
    line-height: 1.5;
    white-space: nowrap;
    display: inline-flex;
    align-items: center;
    flex-shrink: 0;
}
.job-name { font-size: 15px; font-weight: 600; color: var(--text); }
.stage-banner {
    padding: 10px 12px; border-radius: 8px; font-size: 13px;
    display: flex; align-items: center; gap: 8px; margin-bottom: 12px;
}
.stage-banner-warn { background: var(--warning-light); color: #92400e; border: 1px solid #fde68a; }
.stage-banner-info { background: var(--info-light); color: #075985; border: 1px solid #b2ddff; }
.stage-banner-ok   { background: var(--success-light); color: var(--success-dark); border: 1px solid #a7f3d0; }
/* Force every text descendant (p.muted, .tiny, etc.) inside a banner to
   inherit the banner's own strong-contrast colour so pale-blue / pale-amber
   backgrounds don't render body copy in the global grey `--text-muted`
   (which becomes invisible against these low-contrast backgrounds and
   near-invisible in dark mode). */
.stage-banner,
.stage-banner p,
.stage-banner .muted,
.stage-banner .tiny,
.stage-banner .small,
.stage-banner strong { color: inherit !important; }
.stage-banner p { margin: 4px 0 0; }
/* ON HOLD. Deliberately a different colour family from -warn (amber = "watch
   out") and -danger (red = "cancelled"): slate/indigo reads as "parked", which
   is what a hold is — the job is fine, it just isn't moving. The explanatory
   <p> wraps onto its own line because .stage-banner is a flex ROW. */
.stage-banner-hold {
    background: #eef2ff; color: #3730a3; border: 1px solid #c7d2fe;
    flex-wrap: wrap;
}
.stage-banner-hold p { flex-basis: 100%; }
[data-theme="dark"] .stage-banner-hold {
    background: #1e1b4b; color: #c7d2fe; border-color: #3730a3;
}

/* "On Hold" pill for card previews — job list rows, batch rows, global search.
   Sized to sit inline beside a job title without changing the row's height. */
.hold-badge {
    display: inline-flex; align-items: center; gap: 3px;
    margin-left: 6px; padding: 1px 8px;
    background: #eef2ff; color: #3730a3; border: 1px solid #c7d2fe;
    border-radius: 999px; font-size: 11px; font-weight: 700;
    letter-spacing: 0.01em; vertical-align: middle; white-space: nowrap;
}
[data-theme="dark"] .hold-badge {
    background: #1e1b4b; color: #c7d2fe; border-color: #3730a3;
}

.stage-breadcrumbs {
    display: flex; gap: 4px; overflow-x: auto; padding: 6px 0 10px;
    max-width: 1100px; margin: 0 auto;
    scrollbar-width: thin;
}
.stage-breadcrumbs::-webkit-scrollbar { height: 4px; }
.crumb {
    display: inline-flex; align-items: center; justify-content: center;
    min-width: 28px; height: 28px; padding: 0 8px;
    border-radius: var(--r-full); font-size: 12px; font-weight: 700;
    text-decoration: none; flex-shrink: 0;
    font-variant-numeric: tabular-nums;
    transition: transform var(--dur);
}
.crumb-done    { background: var(--brand); color: #fff; }
.crumb-current { background: var(--accent); color: #fff; box-shadow: 0 0 0 3px rgba(180, 83, 9, 0.2); }
.crumb-future  { background: var(--gray-100); color: var(--text-muted); border: 1px solid var(--border); }
.crumb-done:hover, .crumb-future:hover { transform: translateY(-1px); }

.stage-canvas {
    max-width: 1100px; margin: 0 auto; padding: 24px 16px;
    display: grid; grid-template-columns: 1fr 320px; gap: 20px;
}
@media (max-width: 900px) {
    .stage-canvas { grid-template-columns: 1fr; padding: 16px 12px; }
    .stage-bar { padding: 10px 12px 0; top: 49px; }
    .stage-header-top h1 { font-size: 17px; }
}
.stage-main { display: flex; flex-direction: column; gap: 16px; min-width: 0; }
.stage-side { display: flex; flex-direction: column; gap: 12px; }

.stage-card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    padding: 18px 20px;
}
/* Collapsible section inside a stage card. Used on downstream stages to keep
   BOM / procurement lists reachable without dominating the layout. */
.stage-collapse > summary {
    list-style: none;
    cursor: pointer;
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 4px 0;
    margin: -4px 0;
    font-weight: 600;
    color: var(--text-2);
    font-size: 15px;
    user-select: none;
}
.stage-collapse > summary::-webkit-details-marker { display: none; }
.stage-collapse > summary::after {
    content: '▾';
    margin-left: auto;
    color: var(--text-muted);
    font-size: 11px;
    transition: transform .15s;
}
.stage-collapse[open] > summary::after { transform: rotate(180deg); }
.stage-collapse[open] > summary { margin-bottom: 12px; }
.stage-card > h3 {
    margin: 0 0 12px;
    font-size: 15px;
    font-weight: 600;
    color: var(--text);
    display: flex; align-items: center; gap: 8px;
}
.stage-card > h3 svg { color: var(--brand); }
.stage-card .row { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
@media (max-width: 640px) { .stage-card .row { grid-template-columns: 1fr; } }

.stage-actions {
    grid-column: 1 / -1;
    display: flex; justify-content: space-between; gap: 10px;
    padding-top: 12px; border-top: 1px solid var(--border);
    flex-wrap: wrap;
}
.stage-actions .stage-advance-btn { margin-left: auto; }
/* Mobile: stack Move-back + Advance vertically, both full-width, primary
   Advance on top so the intended action is thumb-first. Kills the
   margin-left:auto overflow that pushed the advance button out on narrow
   drawers. */
@media (max-width: 640px) {
    .stage-actions {
        flex-direction: column-reverse;   /* advance rendered second in DOM, but first visually */
        align-items: stretch;
    }
    .stage-actions .btn,
    .stage-actions .stage-move-btn { width: 100%; margin-left: 0; }
    .stage-actions .stage-advance-btn { margin-left: 0; }
}

/* Status pills (In Progress / Blocked / Complete) */
.status-picker {
    display: flex; gap: 6px; flex-wrap: wrap;
}
.status-opt {
    padding: 8px 14px;
    border: 1px solid var(--border);
    border-radius: var(--r);
    background: var(--surface);
    color: var(--text-2);
    font-size: 13px;
    font-weight: 600;
    cursor: pointer;
    transition: all var(--dur);
    font-family: inherit;
}
.status-opt:hover { border-color: var(--border-strong); }
.status-opt.on[data-val="not_started"] { background: var(--gray-100);   color: var(--gray-700); border-color: var(--gray-300); }
.status-opt.on[data-val="in_progress"] { background: var(--info-light); color: #075985;         border-color: #7dd3fc; }
.status-opt.on[data-val="blocked"]     { background: var(--danger-light); color: var(--danger); border-color: #fecdca; }
.status-opt.on[data-val="complete"]    { background: var(--success-light); color: var(--success-dark); border-color: #a7f3d0; }

/* QC approve widget */
.qc-approval {
    display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
    padding: 12px 14px;
    background: var(--gray-50);
    border-radius: var(--r);
    border: 1px solid var(--border);
}
.qc-approval.qc-approved {
    background: var(--success-light);
    border-color: #a6f4c5;
}
.qc-approval .qc-when { font-size: 12px; color: var(--text-muted); }

/* Call log */
.call-log { display: flex; flex-direction: column; gap: 8px; }
.call-entry {
    display: flex; gap: 12px;
    padding: 12px;
    background: var(--gray-50);
    border-radius: var(--r);
    border-left: 3px solid var(--info);
}
.call-entry.call-out { border-left-color: var(--accent); }
.call-entry.call-followup { border-left-color: var(--brand-light); }
.call-icon {
    width: 28px; height: 28px; flex-shrink: 0;
    display: flex; align-items: center; justify-content: center;
    background: var(--surface); border-radius: 50%;
    font-size: 14px;
}
.call-body { flex: 1; }
.call-meta {
    font-size: 12px; color: var(--text-muted);
    display: flex; gap: 8px; flex-wrap: wrap;
    margin-bottom: 4px;
}
.call-remarks {
    /* Message-bubble treatment so the actual conversation content stands
       apart from the surrounding grey call-entry chrome. Client wants to
       be able to skim call logs quickly, so remarks need to be the first
       thing the eye lands on inside an entry — hence white surface + soft
       border + a hair of extra breathing room. */
    display: block;
    margin-top: 6px;
    padding: 8px 10px;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r-sm, 6px);
    font-size: 13px;
    color: var(--text);
    line-height: 1.5;
    white-space: pre-wrap;
    word-break: break-word;
}
[data-theme="dark"] .call-remarks {
    /* On dark bg the entry is already very dark; a lighter card-in-card
       reverses correctly using --surface-2 if defined, else falls back
       to a slightly-lifted gray. */
    background: var(--surface-2, #1e293b);
    border-color: var(--border-strong);
}

/* Mobile: the call-entry loses ~52px horizontally to icon+gap+padding,
   which squeezes the remark bubble into a narrow vertical strip once the
   text runs to more than a few words. Reclaim as much width as possible:
   • Hide the icon column entirely — direction ("Made" / "Received") is
     already spelled out in the meta line, so the arrow chip is redundant
     on a narrow viewport and eats ~28px + 12px gap.
   • Trim entry side padding to 8px so the bubble hugs the entry edges.
   • Trim the bubble's own padding + drop its border to just a bottom
     tint so it doesn't stack inset lines on top of the entry border.
   • Bump the remark to 14px — 13px in a narrow column reads cramped.
   • .call-body { min-width: 0 } keeps long unbroken strings from
     pushing the layout wider than the entry. */
@media (max-width: 640px) {
    .call-entry {
        gap: 8px;
        padding: 10px 8px;
        position: relative;   /* anchor for the absolutely-positioned delete btn */
    }
    .call-icon { display: none; }
    .call-body {
        min-width: 0;
        /* Reserve just enough right room for the delete icon so the meta text
           doesn't run under it; the remark bubble below breaks out with a
           negative right margin to reclaim that space visually. */
        padding-right: 28px;
    }
    /* Lift the delete button out of the flex row and pin it top-right of
       the entry. This frees the ~30px column it was holding open so the
       remark bubble can span the full inner width of .call-entry. */
    .call-entry > .icon-btn.danger {
        position: absolute;
        top: 6px;
        right: 6px;
    }
    .call-remarks {
        margin-top: 8px;
        margin-left: -2px;
        margin-right: -30px;   /* cancel the padding-right on .call-body */
        padding: 10px 12px;
        font-size: 14px;
    }
}

/* Toggle segments (in/out) */
.seg-picker { display: inline-flex; border: 1px solid var(--border); border-radius: var(--r); overflow: hidden; }
.seg-opt {
    padding: 8px 16px;
    border: none;
    background: var(--surface);
    color: var(--text-2);
    font-size: 13px;
    font-weight: 600;
    cursor: pointer;
    font-family: inherit;
    display: inline-flex;
    align-items: center;
    gap: 6px;
}
.seg-opt.on {
    background: var(--brand);
    color: #fff;
}
.seg-opt + .seg-opt { border-left: 1px solid var(--border); }
.seg-opt.on + .seg-opt { border-left-color: var(--brand-dark); }

/* --------------------------------------------------------------------- */
/*  Check-grid — replaces the old <table>.bom-check-table.                 */
/*                                                                         */
/*  Used by three lists:                                                   */
/*     .check-grid-bom     (BOM: tick? #, particulars, size, mat, thick,   */
/*                          qty, actions?)                                  */
/*     .check-grid-simple  (Basic materials + procurement fittings:        */
/*                          tick, item, qty, unit, actions?)                */
/*                                                                         */
/*  Layout uses CSS Grid via named areas so the same markup reflows        */
/*  cleanly on mobile (item text takes full width, other fields wrap       */
/*  underneath). No horizontal scroll needed at any viewport.              */
/* --------------------------------------------------------------------- */
.check-grid {
    display: flex;
    flex-direction: column;
    gap: 6px;
    font-size: 13px;
}
/* Header row + data row share the same grid template. Modifier classes
   (.check-grid-bom / .check-grid-simple, .has-tick / .has-actions) tweak the
   template columns — see the modifier blocks below. */
.check-grid-head,
.check-grid-row {
    display: grid;
    gap: 6px 10px;
    align-items: center;
}
/* Batch child rows that mirror a parent-owned entry — soft blue tint so the
   viewer knows the fields are locked and edits must happen on the parent. */
.check-grid-row.is-batch-mirror {
    background: rgba(59, 130, 246, 0.04);
    border-left: 2px solid #bfdbfe;
}
[data-theme="dark"] .check-grid-row.is-batch-mirror {
    background: rgba(96, 165, 250, 0.06);
    border-left-color: #1e40af;
}

.check-grid-head {
    padding: 6px 10px 4px;
    font-size: 10px; font-weight: 700; letter-spacing: 0.05em;
    text-transform: uppercase; color: var(--text-muted);
    border-bottom: 1px solid var(--border);
}
.check-grid-row {
    /* 2px border matches the .job-row treatment shipped earlier — enough
       weight that each card is clearly separated at a glance. Padding
       trimmed 1px per axis to compensate for the extra border width. */
    padding: 7px 11px;
    background: var(--surface);
    border: 2px solid var(--border-strong);
    border-radius: 8px;
    box-shadow: 0 1px 2px rgba(0,0,0,0.04);
    transition: border-color .12s ease, box-shadow .12s ease;
}
.check-grid-row:hover {
    border-color: var(--gray-400);
    box-shadow: 0 2px 6px rgba(0,0,0,0.06);
}
/* Compact input sizing inside check-grid cards. */
.check-grid-row .cg-input,
.check-grid-row .cg-multi {
    min-height: 28px;
    padding: 3px 8px;
    font-size: 13px;
    line-height: 1.3;
}
/* Mobile only — 16px prevents iOS focus-zoom, but padding stays tight. */
@media (max-width: 720px) {
    .check-grid-row .cg-input,
    .check-grid-row .cg-multi {
        min-height: 34px;
        font-size: 16px;
        padding: 4px 10px;
    }
}
.check-grid-row.is-done {
    background: rgba(16, 185, 129, 0.08);
    border-color: rgba(16, 185, 129, 0.35);
}
.check-grid-row.is-done .cg-input,
.check-grid-row.is-done .cg-input:read-only,
.check-grid-row.is-done .cg-multi { color: var(--text-muted); text-decoration: line-through; text-decoration-thickness: 1px; }
.check-grid-empty {
    padding: 14px; text-align: center; color: var(--text-muted); font-style: italic;
    background: var(--gray-50); border: 1px dashed var(--border); border-radius: var(--r);
}

/* Cell defaults + label chip shown on mobile */
.cg-cell {
    min-width: 0;
    display: flex;
    align-items: center;
    grid-area: auto;
    /* Explicitly zero any UA label-element defaults — .cg-cell is applied to
       <label> nodes so browser padding/margins would otherwise sneak in. */
    margin: 0;
    padding: 0;
}
.cg-label {
    /* Small caps label above each input on mobile / stacked layout.
       Desktop's single-row card hides labels via the media block below
       (they'd blow up the row height for no gain — the input positions
       are self-explanatory + placeholders cover empty state). */
    display: block;
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    color: var(--text-muted);
    margin: 0 0 2px;
    line-height: 1.2;
}
.cg-cell.cg-num { color: var(--text-muted); font-variant-numeric: tabular-nums; }
.cg-cell.cg-tick { justify-content: center; }
.cg-cell.cg-tick input[type="checkbox"] { width: 20px; height: 20px; margin: 0; cursor: pointer; }
.cg-cell.cg-actions { justify-content: flex-end; }
.cg-cell.cg-qty .cg-input,
.cg-cell.cg-qty { text-align: right; }

/* Editable input styling — shared across every field. Border deliberately
   dark (var(--gray-500) ≈ #667085 in light, #94a3b8 in dark) so each field
   reads as a clearly bounded box, not a faint hairline against the card
   background. Meets WCAG 3:1 for UI-component boundaries. Read-only ticks
   still get a lighter border so mgmt can tell "you can't edit this row"
   at a glance, but they're no longer invisible either. */
.cg-input {
    width: 100%;
    min-width: 0;
    padding: 6px 8px;
    min-height: 34px;
    font-size: 13px;
    font-family: inherit;
    color: var(--text);
    background: var(--surface);
    border: 1px solid var(--gray-500);
    border-radius: var(--r-sm);
    box-sizing: border-box;
}
.cg-input:hover:not([readonly]) { border-color: var(--brand); }
.cg-input:focus { border-color: var(--brand); outline: none; box-shadow: 0 0 0 3px var(--brand-50); }
.cg-input[readonly] { border-color: var(--border-strong); background: var(--gray-50); cursor: default; }
.cg-multi {
    resize: vertical;
    line-height: 1.4;
    white-space: pre-wrap;
    word-break: break-word;
    overflow: hidden;   /* auto-grown by JS */
}

/* Container guard — the grid must never grow wider than its parent.
   Without min-width:0 the intrinsic input widths bubble up through the grid
   and blow the drawer's right border. See CSS-tricks "The mysterious flex
   min-width" — the same trap applies to grid items. */
.check-grid { width: 100%; min-width: 0; box-sizing: border-box; }
.check-grid-row, .check-grid-head { min-width: 0; }

/* --------------------------------------------------------------------- */
/*  Card layout — used at every viewport for BOM + Basic materials +      */
/*  Additional fittings. Same visual language as .bill-cards .bc:         */
/*  self-contained card with a title row + meta row, subtle border and    */
/*  hover-lift shadow. Fields keep their inline-edit inputs (autosave on  */
/*  blur) — no click-to-edit dance, just a friendlier layout than the     */
/*  spreadsheet-style table this replaced.                                */
/* --------------------------------------------------------------------- */
.check-grid-head { display: none; }   /* card mode labels its own fields */

.check-grid-row {
    /* Two-column grid inside each card. Cells auto-place in DOM order:
       tick (col 1) → main (spans both) → qty (col 1) → unit (col 2) …
       Delete floats to the top-right via position:absolute.
       Row-gap is 2px — labels are hidden so each cell is just its input
       and needs almost no vertical breathing room. */
    grid-template-columns: 1fr 1fr;
    gap: 2px 8px;
    padding: 6px 10px;
    position: relative;
    /* align-items: start so cells stick to the top of the grid row instead
       of vertically-centering. Without this, a wrapping Size value pushes
       Material's label down to the middle of the (now taller) row, breaking
       label alignment across side-by-side cells. Base .cg-cell rule sets
       align-items:center which was leaking here via grid alignment defaults. */
    align-items: start;
}
.cg-cell.cg-num { display: none; }    /* # kept in DOM for exports, hidden visually */
.cg-cell.cg-tick {
    /* Sit inline with the item field, not on its own full-width row.
       On mobile the tick lives on the same row as the DONE pill; the
       item field wraps to the next line via the 2-col grid below. */
    grid-column: 1;
    justify-self: start;
    align-self: center;
    gap: 6px;
    min-height: 0;
    padding: 0;
}
.cg-cell.cg-tick input[type="checkbox"] { width: 18px; height: 18px; }
.cg-cell.cg-main {
    grid-column: 1 / -1;
    flex-direction: column;
    align-items: stretch;
    gap: 2px;
}
.cg-cell.cg-main .cg-input,
.cg-cell.cg-main .cg-multi { font-weight: 500; }

.cg-cell.cg-size,
.cg-cell.cg-mat,
.cg-cell.cg-thick,
.cg-cell.cg-qty,
.cg-cell.cg-unit {
    flex-direction: column;
    align-items: stretch;
    gap: 2px;   /* small gap between label + input */
    /* No explicit grid-column — let auto-placement fill the 2-col grid in
       DOM order (Size→Mat, Thick→Qty→Unit or Qty→Unit for basic lists). */
}
.cg-cell.cg-qty .cg-input { text-align: right; }

/* Read-only value display — used when a worker (or any non-editor) sees a
   BOM row. Same footprint an <input> would occupy, with a subtle border so
   each field is still visually a bounded box (workers need to see WHERE
   the data ends without any ambiguity). Border is lighter than the
   editable input's --gray-500 so the "you can't edit this" cue survives.
   cg-value-num right-aligns for qty columns to match input. */
.cg-value {
    display: block;
    padding: 4px 8px;
    min-height: 28px;
    font-size: 13px;
    line-height: 1.3;
    color: var(--text);
    background: var(--gray-50);
    border: 1px solid var(--border-strong);
    border-radius: var(--r-sm);
    box-sizing: border-box;
    word-break: break-word;
    white-space: pre-wrap;
}
.cg-value-num { text-align: right; font-variant-numeric: tabular-nums; }
.cg-value-main { font-weight: 500; }
.cg-empty { color: var(--text-muted); font-style: italic; }

/* Fresh-add flash — highlight a BOM row briefly after the user scrolls
   to it via the #bom-row-<id> deep-link. Fades cleanly so it doesn't stay
   distracting once mgmt has seen where their new entry landed. */
.cg-just-added {
    animation: cg-flash 2.2s ease-out;
}
@keyframes cg-flash {
    0%   { background: rgba(59, 130, 246, 0.18); box-shadow: 0 0 0 3px rgba(59,130,246,0.25); }
    60%  { background: rgba(59, 130, 246, 0.08); box-shadow: 0 0 0 3px rgba(59,130,246,0.10); }
    100% { background: transparent;              box-shadow: none; }
}
@media (max-width: 720px) {
    .cg-value { min-height: 34px; font-size: 16px; padding: 4px 10px; }
}

.cg-cell.cg-actions {
    position: absolute;
    top: 8px; right: 8px;
    padding: 0;
}

/* Wider viewports: horizontal card layout — tick + main text + secondary
   fields + delete land on one wrapping flex row. Each field cell now stacks
   its label above its input (matches mobile layout) so mgmt gets the same
   "PARTICULARS / SIZE / MATERIAL / THICKNESS / QTY" caps under which the
   value or input sits. Row aligns to top so tall wrapping main fields don't
   push short qty cells' labels off-center. */
@media (min-width: 720px) {
    .check-grid-row {
        display: flex;
        flex-wrap: wrap;
        align-items: flex-start;   /* labels of every cell line up on the top */
        gap: 8px 12px;
        padding: 8px 12px;
    }
    .check-grid-row .cg-cell {
        display: flex;
        flex-direction: column;    /* label above input */
        align-items: stretch;
        gap: 2px;
        min-width: 0;
    }
    /* Tick + actions have no label — keep them centered vertically with
       the top row of field boxes so they don't float in whitespace. */
    .cg-cell.cg-tick,
    .cg-cell.cg-actions {
        flex-direction: row;
        align-items: center;
        align-self: center;
    }
    .cg-cell.cg-tick { flex: 0 0 auto; }
    .cg-cell.cg-main {
        flex: 1 1 240px;
        min-width: 200px;
    }
    /* Size / Material / Thickness — mid-width fields, comfortable but compact */
    .cg-cell.cg-size,
    .cg-cell.cg-mat,
    .cg-cell.cg-thick { flex: 0 1 140px; min-width: 100px; }
    .cg-cell.cg-trade { flex: 0 1 150px; min-width: 120px; }
    .cg-cell.cg-qty   { flex: 0 0 88px; }
    .cg-cell.cg-unit  { flex: 0 0 100px; }
    .cg-cell.cg-actions {
        position: static;              /* undo the absolute-in-corner treatment */
        flex: 0 0 auto;
        margin-left: auto;             /* push to far right */
    }
    /* Done state on desktop — chip sits inline top-right of the row, not
       eating an extra row. */
    .check-grid-row.is-done::before {
        position: static;
        margin-left: 4px;
        order: -1;                     /* place it before other cells visually */
        align-self: center;
    }
    .check-grid-row.is-done .cg-cell.cg-tick { padding-top: 0; }
}

/* Done pill — inline chip that sits inside the tick cell next to the
   checkbox, so a done row reads "[✓] Done  |  Item..." on one line
   rather than stacking a full-width "DONE" banner above the tick. */
.cg-cell.cg-tick::after {
    content: 'Done';
    display: none;
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: #065f46;
    padding: 1px 6px;
    border-radius: 4px;
    background: rgba(16, 185, 129, 0.14);
    line-height: 1.4;
}
.check-grid-row.is-done .cg-cell.cg-tick::after {
    display: inline-block;
}
[data-theme="dark"] .cg-cell.cg-tick::after { color: #6ee7b7; background: rgba(16, 185, 129, 0.22); }

/* Mobile input sizing is now handled by the .check-grid-row-scoped block
   above — this section intentionally left blank so any global .cg-input
   consumers (there are none currently) fall through to defaults. */

/* Payment cards */
.payment-list { display: flex; flex-direction: column; gap: 8px; }
.payment-card {
    display: flex; gap: 12px; align-items: center;
    padding: 12px 14px;
    background: var(--gray-50);
    border: 1px solid var(--border);
    border-radius: var(--r);
}
.payment-mode {
    padding: 4px 10px;
    border-radius: var(--r-full);
    font-size: 11px; font-weight: 700; text-transform: uppercase;
    letter-spacing: 0.04em;
    background: var(--brand-50); color: var(--brand);
    flex-shrink: 0;
}
.payment-amount { font-size: 17px; font-weight: 700; color: var(--text); font-variant-numeric: tabular-nums; }
.payment-meta { font-size: 12px; color: var(--text-muted); }

/* Specialist assignment picker */
.spec-group { margin-bottom: 14px; }
.spec-group > label {
    font-size: 12px; text-transform: uppercase; letter-spacing: 0.05em;
    color: var(--text-muted); font-weight: 600; margin-bottom: 6px; display: block;
}
.spec-picker {
    display: flex; gap: 6px; flex-wrap: wrap;
}

/* Additional fittings + BOM row add form */
.mini-form {
    display: flex; gap: 8px; align-items: center; margin-top: 8px;
    flex-wrap: wrap;
}
.mini-form input { flex: 1; min-height: 36px; font-size: 14px; }

/* BOM add-form has 7 fields (Particulars / Size / Material / Thickness /
   Qty / Trade / Add-btn). A single-row layout crunches Size/Material/
   Thickness to ~100px on the typical stage-card width. Instead: 3 rows
   on desktop —
     Row 1: Particulars (full width)
     Row 2: Size · Material · Thickness (3 equal columns)
     Row 3: Qty · Trade · Add-btn
   Each field gets meaningful width without the whole form flowing off. */
@media (min-width: 720px) {
    #new-bom-form.mini-form {
        display: grid;
        grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) minmax(0, 1fr);
        gap: 8px 10px;
        align-items: stretch;
    }
    #new-bom-form.mini-form input[name="particulars"] {
        grid-column: 1 / -1;      /* full-width first line */
        flex: initial;
        min-width: 0;
    }
    /* Row 2: three equal-width text fields */
    #new-bom-form.mini-form input[name="size"],
    #new-bom-form.mini-form input[name="material"],
    #new-bom-form.mini-form input[name="thickness"] {
        max-width: none !important;
        flex: initial;
        min-width: 0;
        width: 100%;
    }
    /* Row 3: qty (compact) + trade (mid) + button (auto right). Uses
       grid-column overrides so all three land on the same row. */
    #new-bom-form.mini-form input[name="qty"] {
        grid-column: 1;
        max-width: none !important;
        min-width: 0;
        width: 100%;
    }
    #new-bom-form.mini-form select[name="assigned_trade"] {
        grid-column: 2;
        max-width: none !important;
        min-width: 0;
        width: 100%;
    }
    #new-bom-form.mini-form .btn {
        grid-column: 3;
        max-width: none !important;
        flex: initial;
        min-width: 0;
        width: 100%;
        white-space: nowrap;
        padding-left: 12px;
        padding-right: 14px;
    }
}
@media (max-width: 640px) {
    /* On phones the mini-form wraps: the "Add material" text field gets full
       width and qty/unit chips go on their own row, each 44 px min-height
       for a usable tap target. */
    .mini-form { gap: 6px; }
    .mini-form input {
        flex: 1 1 100%;
        min-height: 44px;
        font-size: 16px;   /* prevents iOS zoom on focus */
    }
    .mini-form input[style*="max-width"] { flex: 1 1 auto; }
    .mini-form .btn { flex: 1 1 100%; }
}

/* Dispatch checklist rows */
.disp-check {
    display: flex; align-items: center; gap: 10px;
    padding: 10px 12px;
    background: var(--gray-50);
    border: 1px solid var(--border);
    border-radius: var(--r);
    cursor: pointer;
}
.disp-check.done { background: var(--success-light); border-color: #a6f4c5; }
.disp-check input { width: 20px; height: 20px; }
.disp-check-label { flex: 1; font-size: 14px; color: var(--text); }

.stage-empty { color: var(--text-muted); font-size: 13px; padding: 8px 0; }

/* Activity timeline shown inside every stage detail view */
.activity-card details summary {
    list-style: none;
    cursor: pointer;
    display: flex;
    align-items: center;
    gap: 8px;
    font-weight: 600;
    color: var(--text);
    padding: 4px 0;
    user-select: none;
}
.activity-card details summary::-webkit-details-marker { display: none; }
.activity-card details summary::after {
    content: '▸';
    margin-left: auto;
    color: var(--text-muted);
    font-size: 12px;
    transition: transform var(--dur);
}
.activity-card details[open] summary::after { transform: rotate(90deg); }
.activity-list-stage {
    list-style: none;
    padding: 12px 0 4px;
    margin: 0;
    display: flex;
    flex-direction: column;
    gap: 10px;
    max-height: 340px;
    overflow-y: auto;
    border-top: 1px solid var(--border);
}
.activity-row-stage {
    display: flex;
    gap: 10px;
    align-items: flex-start;
}
.activity-body-stage { flex: 1; min-width: 0; }
.activity-line-stage {
    font-size: 13px;
    line-height: 1.45;
    color: var(--text-2);
    word-break: break-word;
}
.activity-line-stage strong { color: var(--text); font-weight: 600; }
.activity-line-stage .activity-detail {
    color: var(--text);
    display: inline;
    margin-left: 2px;
}
.activity-ts { margin-top: 2px; cursor: help; }

/* Compact label used in the sidebar Job Schedule block */
.mini-label {
    display: block;
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--text-muted);
    font-weight: 600;
    margin: 8px 0 0;
}
.mini-label input {
    margin-top: 4px;
    padding: 6px 8px;
    min-height: 34px;
    font-size: 13px;
    width: 100%;
}

/* Previous-stages accordion — sits in the sidebar under Job Summary. */
.hist-card > details > summary {
    list-style: none;
    cursor: pointer;
    display: flex;
    align-items: center;
    gap: 8px;
    font-weight: 600;
    color: var(--text);
    padding: 4px 0;
    user-select: none;
}
.hist-card > details > summary::-webkit-details-marker { display: none; }
.hist-card > details > summary::after {
    content: '▸';
    margin-left: auto;
    color: var(--text-muted);
    font-size: 12px;
    transition: transform var(--dur);
}
.hist-card > details[open] > summary::after { transform: rotate(90deg); }
.hist-body {
    padding-top: 12px;
    border-top: 1px solid var(--border);
    margin-top: 4px;
    display: flex;
    flex-direction: column;
    gap: 6px;
    max-height: 480px;
    overflow-y: auto;
}
.hist-stage {
    border: 1px solid var(--border);
    border-radius: var(--r);
    background: var(--gray-50);
}
.hist-stage > summary {
    list-style: none;
    cursor: pointer;
    padding: 8px 12px;
    font-size: 13px;
    font-weight: 600;
    color: var(--text-2);
    display: flex;
    align-items: center;
    gap: 6px;
}
.hist-stage > summary::-webkit-details-marker { display: none; }
.hist-stage > summary::before {
    content: '▸';
    color: var(--text-muted);
    font-size: 10px;
    transition: transform var(--dur);
}
.hist-stage[open] > summary::before { transform: rotate(90deg); }
.hist-stage-body {
    padding: 0 12px 12px;
    display: flex;
    flex-direction: column;
    gap: 8px;
    font-size: 13px;
}
.hist-row {
    display: flex;
    flex-direction: column;
    gap: 3px;
}
.hist-label {
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--text-muted);
    font-weight: 600;
}
.hist-val {
    color: var(--text-2);
    line-height: 1.4;
    word-break: break-word;
}
.hist-val ul.hist-list {
    margin: 0;
    padding-left: 16px;
    display: flex;
    flex-direction: column;
    gap: 4px;
}
.hist-thumb img {
    max-width: 100%;
    max-height: 160px;
    border-radius: 6px;
    border: 1px solid var(--border);
}
.hist-pill {
    display: inline-block;
    padding: 2px 8px;
    border-radius: var(--r-full);
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.03em;
    text-transform: uppercase;
}
.hist-pill-gray    { background: var(--gray-100); color: var(--gray-700); }
.hist-pill-info    { background: var(--info-light); color: #075985; }
.hist-pill-danger  { background: var(--danger-light); color: var(--danger); }

/* Signed marker: "Approved by <name> · <date>" line in previous-stages block */
.hist-sign {
    display: block;
    font-size: 12px;
    line-height: 1.45;
    padding: 6px 10px;
    border-radius: var(--r);
    border-left: 3px solid transparent;
    margin-top: 4px;
    background: var(--gray-50);
}
.hist-sign strong { font-weight: 600; }
.hist-sign-verb { font-weight: 600; }
.hist-sign-success { background: #ecfdf5; border-left-color: #10b981; color: #065f46; }
.hist-sign-info    { background: #eff6ff; border-left-color: #3b82f6; color: #1e3a8a; }
.hist-sign-warn    { background: #fefce8; border-left-color: #eab308; color: #713f12; }
.hist-sign-detail { margin-top: 2px; }

/* Field-change list inside a stage block: "Field: old → new" + actor line */
.hist-edits {
    display: flex;
    flex-direction: column;
    gap: 6px;
}
.hist-edit {
    padding: 6px 8px;
    background: var(--gray-50);
    border-radius: var(--r);
    border-left: 2px solid var(--gray-300, #d1d5db);
}
.hist-edit-body {
    font-size: 12px;
    line-height: 1.4;
    color: var(--text-2);
    word-break: break-word;
}
.hist-edit-meta {
    margin-top: 2px;
    font-size: 11px;
}
.hist-pill-success { background: var(--success-light); color: var(--success-dark); }
.hist-bom {
    display: flex;
    flex-direction: column;
    gap: 2px;
    max-height: 200px;
    overflow-y: auto;
    padding-right: 4px;
}
.hist-bom-row {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 3px 0;
    font-size: 12px;
    border-bottom: 1px dashed var(--border);
}
.hist-bom-row:last-child { border-bottom: none; }
.hist-bom-row.done { color: var(--text-muted); text-decoration: line-through; }
.hist-bom-check { width: 12px; font-weight: 700; }

/* Service-worker update prompt bar (bottom of screen) */
#kmz-update-bar {
    position: fixed;
    left: 12px; right: 12px;
    bottom: calc(72px + env(safe-area-inset-bottom));
    background: var(--brand-dark);
    color: #fff;
    padding: 12px 16px;
    border-radius: var(--r);
    box-shadow: var(--shadow-lg);
    display: flex;
    align-items: center;
    gap: 12px;
    z-index: 500;
    font-size: 14px;
    max-width: 640px;
    margin: 0 auto;
    animation: slideUpBar 240ms var(--ease-out);
}
#kmz-update-bar span { flex: 1; }
#kmz-update-bar button {
    background: #fff;
    color: var(--brand-dark);
    border: none;
    padding: 6px 14px;
    border-radius: var(--r-sm);
    font-family: inherit;
    font-weight: 600;
    font-size: 13px;
    cursor: pointer;
}
#kmz-update-bar #kmz-update-close {
    background: transparent;
    color: rgba(255,255,255,0.7);
    font-size: 20px;
    padding: 0 6px;
    line-height: 1;
}
#kmz-update-bar #kmz-update-close:hover { color: #fff; }
@keyframes slideUpBar {
    from { opacity: 0; transform: translateY(20px); }
    to   { opacity: 1; transform: translateY(0); }
}

/* Toasts (used by stage.js) */
/* Toast — floating status pill.
 * Theme-independent by design: notifications should punch above the page,
 * not blend with the surrounding surface. Fixed dark bg + white text reads
 * cleanly on either theme. Previously bound to var(--gray-900), which
 * inverts under [data-theme="dark"] to near-white and clashed with the
 * white text — the "Saved" toast became invisible in dark mode. */
.toast {
    position: fixed; left: 50%; bottom: 84px;
    transform: translate(-50%, 20px);
    background: #1f2937; color: #fff;
    padding: 10px 18px; border-radius: var(--r-full);
    font-size: 13px; font-weight: 500;
    box-shadow: 0 10px 30px -8px rgba(0,0,0,0.35), 0 4px 6px -2px rgba(0,0,0,0.25);
    opacity: 0; z-index: 999;
    transition: transform var(--dur), opacity var(--dur);
    pointer-events: none;
}
.toast-show { opacity: 1; transform: translate(-50%, 0); }
.toast-danger, .toast-error { background: var(--danger); color: #fff; }
.toast-success { background: var(--brand); color: #fff; }

/* Sticky offline banner — persists until connectivity returns */
.offline-banner {
    position: fixed; left: 0; right: 0;
    top: env(safe-area-inset-top, 0);
    background: var(--danger); color: #fff;
    padding: 8px 16px;
    font-size: 13px; font-weight: 500;
    text-align: center;
    z-index: 10000;
    box-shadow: 0 2px 6px rgba(0,0,0,.15);
}
.offline-banner[hidden] { display: none; }

/* Web-push soft-ask banner + iOS hint. Pinned bottom on all screens so
   they don't push page content around. Wraps on narrow mobile. */
.push-banner {
    position: fixed;
    left: 12px; right: 12px;
    bottom: calc(80px + env(safe-area-inset-bottom, 0));  /* above bottom-nav */
    z-index: 9500;
    background: var(--surface);
    border: 1px solid var(--border-strong);
    box-shadow: 0 6px 24px rgba(0,0,0,.12);
    border-radius: var(--r-lg);
    padding: 14px 16px;
    display: flex;
    align-items: center;
    gap: 14px;
    flex-wrap: wrap;
    max-width: 640px;
    margin-left: auto;
    margin-right: auto;
}
.push-banner-body { flex: 1; min-width: 200px; display: flex; flex-direction: column; gap: 2px; }
.push-banner-body strong { font-size: 14px; color: var(--text); }
.push-banner-actions { display: flex; gap: 8px; flex-shrink: 0; }
.push-banner-ios { border-left: 4px solid var(--brand); }
@media (min-width: 901px) {
    .push-banner { bottom: 20px; }
}

/* =========================================================
   Tab-strip landing (index.php Job Cards home)
   ========================================================= */
.tabs-page { background: var(--gray-100); min-height: 100vh; padding-bottom: 80px; }
.tabs-shell {
    background: var(--surface);
    border-bottom: 1px solid var(--border);
    position: sticky; top: 53px; z-index: 20;
}
.tabs-title-row {
    display: flex; justify-content: space-between; align-items: flex-end; gap: 12px;
    padding: 14px 20px 10px;
    max-width: 1400px; margin: 0 auto;
    flex-wrap: wrap;
}
.tabs-title-row h1 { font-size: 22px; margin: 0 0 2px; }

/* Wrap holds the scroll strip + the two chevron arrows + the edge fades.
   The strip is the ONLY scrolling child; arrows sit above it (position:absolute)
   so they never shift as tabs re-flow, and edge-fade pseudo-elements hint at
   more content off-screen. */
.stage-tabs-wrap {
    position: relative;
    max-width: 1400px;
    margin: 0 auto;
}
.stage-tabs-wrap::before,
.stage-tabs-wrap::after {
    content: "";
    position: absolute;
    top: 0; bottom: 6px;   /* leave the scrollbar area unmasked */
    width: 44px;
    pointer-events: none;
    opacity: 0;
    transition: opacity var(--dur);
    z-index: 1;
}
.stage-tabs-wrap::before {
    left: 0;
    background: linear-gradient(to right, var(--surface) 20%, rgba(255,255,255,0));
}
.stage-tabs-wrap::after {
    right: 0;
    background: linear-gradient(to left,  var(--surface) 20%, rgba(255,255,255,0));
}
.stage-tabs-wrap.has-left::before  { opacity: 1; }
.stage-tabs-wrap.has-right::after  { opacity: 1; }

.stage-tabs-arrow {
    position: absolute;
    top: 50%;
    transform: translateY(calc(-50% - 2px));
    width: 30px; height: 30px;
    display: inline-flex;
    align-items: center; justify-content: center;
    padding: 0;
    background: var(--surface);
    color: var(--text-muted);
    border: 1px solid var(--border);
    border-radius: 50%;
    box-shadow: 0 2px 6px rgba(15, 23, 42, 0.08);
    cursor: pointer;
    opacity: 0;
    visibility: hidden;
    transition: opacity var(--dur), transform var(--dur), background var(--dur), color var(--dur), box-shadow var(--dur);
    z-index: 2;
}
.stage-tabs-arrow:hover {
    color: var(--brand);
    background: var(--surface);
    box-shadow: 0 3px 10px rgba(15, 23, 42, 0.14);
    transform: translateY(calc(-50% - 2px)) scale(1.06);
}
.stage-tabs-arrow:active {
    transform: translateY(calc(-50% - 2px)) scale(0.96);
}
.stage-tabs-arrow-left  { left: 4px; }
.stage-tabs-arrow-right { right: 4px; }
.stage-tabs-wrap.has-left  .stage-tabs-arrow-left,
.stage-tabs-wrap.has-right .stage-tabs-arrow-right {
    opacity: 1;
    visibility: visible;
}

.stage-tabs {
    display: flex; gap: 4px;
    overflow-x: auto; overflow-y: hidden;
    padding: 4px 16px 8px;
    scrollbar-width: thin;
    /* NB: scroll-behavior deliberately left as `auto`. The inline script that
       centers the active tab runs before first paint — a smooth-scroll would
       cause a visible slide from tab 1 on every page load. */
}
.stage-tabs::-webkit-scrollbar { height: 4px; }
.stage-tabs::-webkit-scrollbar-thumb { background: var(--gray-300); border-radius: 2px; }
.stage-tab {
    display: inline-flex; align-items: center; gap: 8px;
    padding: 10px 14px;
    background: transparent;
    border: none;
    border-bottom: 2px solid transparent;
    border-radius: 0;
    color: var(--text-muted);
    text-decoration: none;
    font-size: 13px;
    font-weight: 600;
    flex-shrink: 0;
    transition: color var(--dur), border-color var(--dur), background var(--dur);
    position: relative;
    max-width: 240px;
}
.stage-tab:hover { color: var(--text); text-decoration: none; background: var(--gray-50); }
.stage-tab.active {
    color: var(--brand);
    border-bottom-color: var(--brand);
    background: transparent;
    box-shadow: none;
}
/* Kill any per-stage hue overrides that were bleeding into the tabs. */
.stage-tab[class*="stage-idx-"] { background: transparent; color: var(--text-muted); }
.stage-tab[class*="stage-idx-"]:hover { color: var(--text); background: var(--gray-50); }
.stage-tab[class*="stage-idx-"].active { color: var(--brand); border-bottom-color: var(--brand); }

.stage-tab-num {
    display: inline-flex;
    width: 20px; height: 20px;
    align-items: center; justify-content: center;
    border-radius: 50%;
    background: var(--gray-100);
    color: var(--text-muted);
    font-size: 11px;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    flex-shrink: 0;
    border: 1px solid var(--border);
}
.stage-tab.active .stage-tab-num {
    background: var(--brand); color: #fff; border-color: var(--brand);
}
.stage-tab-name {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 160px;
}
.stage-tab-count {
    background: var(--gray-100);
    color: var(--text-muted);
    padding: 1px 8px;
    border-radius: var(--r-full);
    font-size: 11px;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    min-width: 20px;
    text-align: center;
    border: 1px solid var(--border);
}
.stage-tab.active .stage-tab-count {
    background: var(--brand-50);
    color: var(--brand);
    border-color: var(--brand-light);
}

.tabs-canvas {
    max-width: 1400px;
    margin: 0 auto;
    padding: 20px 16px;
    display: grid;
    grid-template-columns: 1fr;
    gap: 20px;
    position: relative;
}
/* Detail-open: narrow the list pane so the detail always gets ≥ 720 px on
   desktop. Below 1200 px the list pane collapses to just the card titles
   (see .tabs-canvas.has-detail .job-row-* rules further down). */
.tabs-canvas.has-detail {
    grid-template-columns: minmax(240px, 320px) minmax(0, 1fr);
}
/* Expanded / focus mode — click the ⤢ button in the drawer header to hide
   the list pane and let the detail take the full canvas width. Toggles the
   `is-detail-expanded` class on .tabs-canvas via JS.
   NOTE: we use a SINGLE-column grid here instead of `0 1fr` — the previous
   two-track approach with a zero first track caused some browsers to round
   the whole grid down and render the drawer as a narrow strip. */
.tabs-canvas.has-detail.is-detail-expanded {
    grid-template-columns: minmax(0, 1fr);
}
.tabs-canvas.has-detail.is-detail-expanded .job-list-pane {
    display: none;
}
.tabs-canvas.has-detail.is-detail-expanded .job-detail-pane {
    /* Explicit — we want the detail to occupy the whole (only) column. */
    grid-column: 1 / -1;
    width: 100%;
}
@media (max-width: 900px) {
    .tabs-canvas.has-detail { grid-template-columns: 1fr; }
    .tabs-canvas.has-detail.is-detail-expanded { grid-template-columns: 1fr; }
    /* Drawer already fills the viewport on mobile — no point in an expand toggle. */
    .drawer-expand-btn { display: none; }
}
.job-list-pane { min-width: 0; }

/* Selected row while a detail is open */
.job-row.is-selected {
    border-left-color: var(--brand);
    border-color: var(--brand-light);
    background: var(--brand-50);
}

/* Right-hand detail panel (desktop) / bottom-drawer overlay (mobile) */
.job-detail-pane {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    padding: 20px;
    min-width: 0;
    position: relative;
}
@media (max-width: 900px) {
    .job-detail-pane {
        position: fixed;
        inset: 0;
        z-index: 100;
        border-radius: 0;
        border: none;
        overflow-y: auto;
        padding: 16px;
        padding-top: calc(16px + env(safe-area-inset-top));
        padding-bottom: calc(80px + env(safe-area-inset-bottom));
        animation: slideUp 240ms var(--ease-out);
    }
}
.job-detail-scrim { display: none; }
@media (max-width: 900px) {
    .job-detail-scrim {
        display: block;
        position: fixed;
        inset: 0;
        background: rgba(15, 23, 42, 0.55);
        z-index: 99;
        animation: fadeIn 200ms var(--ease-out);
    }
}

.job-detail-header {
    display: flex; align-items: flex-start; justify-content: space-between;
    gap: 10px;
    padding-bottom: 10px;
    margin-bottom: 12px;
    border-bottom: 1px solid var(--border);
}
/* Badge sits on its own line above the crumb + title on mobile to avoid the
   cramped "260714-405 [gap] Stage 5 / 17 · MS Materials Procurement" mash. */
.job-detail-header-left {
    display: flex; gap: 8px; align-items: flex-start; flex: 1; min-width: 0;
    flex-wrap: wrap;
}
.job-detail-header-left > div { min-width: 0; flex: 1 1 240px; }
.job-detail-title { font-size: 17px; margin: 2px 0 0; word-break: break-word; line-height: 1.3; }
.job-detail-crumb {
    font-size: 10px;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.04em;
    font-weight: 600;
    white-space: normal;
}
@media (max-width: 640px) {
    .job-detail-header { padding-bottom: 8px; margin-bottom: 10px; gap: 6px; }
    .job-detail-header-left { gap: 6px; }
    .job-detail-title { font-size: 15px; }
    .job-detail-crumb { font-size: 10px; }
    /* On phones, drop the badge onto its own row so it doesn't fight the title
       for horizontal space. */
    .job-detail-header-left .job-id-badge { flex-basis: auto; }
    .job-detail-header-left > div { flex-basis: 100%; }
}

/* Drawer view: two-column grid on desktop (main pane + right rail),
   single-column stack on mobile. Right rail = summary, due-date editor,
   assigned people, previous stages accordion, activity log. */
.job-detail-canvas {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 320px;
    gap: 16px;
    padding: 0;
    max-width: none;
    margin: 0;
}
.job-detail-canvas .stage-main { display: flex; flex-direction: column; gap: 14px; min-width: 0; }
.job-detail-canvas .stage-side { display: flex; flex-direction: column; gap: 12px; min-width: 0; }
.job-detail-canvas .stage-actions { grid-column: 1 / -1; }
@media (max-width: 900px) {
    .job-detail-canvas { grid-template-columns: 1fr; }
}
.stage-header-bar {
    display: flex; justify-content: space-between; align-items: center;
    margin-bottom: 14px;
    padding: 0 4px;
}
.stage-header-crumb {
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--text-muted);
    font-weight: 600;
    margin-bottom: 2px;
}
.stage-header-title { font-size: 20px; font-weight: 700; margin: 0; color: var(--text); }

/* Live search above each tab's job list */
.job-search-wrap {
    position: relative;
    margin-bottom: 12px;
}
.job-search-icon {
    position: absolute;
    left: 12px; top: 50%; transform: translateY(-50%);
    color: var(--text-muted);
    pointer-events: none;
    display: inline-flex;
}
.job-search-input {
    width: 100%;
    padding: 10px 40px 10px 36px;
    border: 1px solid var(--border);
    border-radius: var(--r);
    background: var(--surface);
    color: var(--text);
    font-size: 14px;
    font-family: inherit;
    transition: border-color var(--dur), box-shadow var(--dur);
    min-height: 40px;
}
.job-search-input:focus {
    outline: none;
    border-color: var(--brand-light);
    box-shadow: 0 0 0 3px rgba(82, 183, 136, 0.15);
}
.job-search-input::placeholder { color: var(--text-muted); }
.job-search-clear {
    position: absolute;
    right: 6px; top: 50%; transform: translateY(-50%);
    width: 28px; height: 28px;
    display: inline-flex; align-items: center; justify-content: center;
    background: transparent; border: none;
    color: var(--text-muted); cursor: pointer;
    border-radius: 50%;
}
.job-search-clear:hover { background: var(--gray-100); color: var(--text); }

.job-search-empty {
    padding: 32px 16px;
    background: var(--surface);
    border: 1px dashed var(--border-strong);
    border-radius: var(--r-lg);
    text-align: center;
    color: var(--text-muted);
    margin-bottom: 12px;
}
.job-search-empty svg { color: var(--text-subtle); margin-bottom: 8px; }
.job-search-empty h4 { margin: 4px 0; font-size: 15px; color: var(--text-2); }

.job-list {
    display: flex; flex-direction: column; gap: 8px;
}
.job-row {
    display: grid;
    grid-template-columns: 1fr auto auto auto;
    align-items: center;
    gap: 14px;
    /* Padding trimmed by 1px on each axis to compensate for the 1px wider
       border, so the interior content box stays visually identical and
       the list density doesn't shift. */
    padding: 13px 15px;
    background: var(--surface);
    /* Border upgraded twice: (1) width 1px → 2px so cards are clearly
       separated from the page background — client feedback that 1px felt
       invisible even after the color bump; (2) color --border-strong so
       it reads as a solid frame rather than a hairline. The 2px still
       feels lighter than the 3px priority left-bar so the priority accent
       stays the dominant edge — cards feel framed, not caged. */
    border: 2px solid var(--border-strong);
    border-left: 3px solid var(--brand-light);
    border-radius: var(--r-lg);
    text-decoration: none;
    color: var(--text);
    transition: all var(--dur);
}
.job-row-prio { justify-self: end; align-self: center; }
@media (max-width: 640px) {
    /* On phones the row wraps: priority chip pins to the right of the title
       row so it stays visible instead of getting pushed onto a fourth line. */
    .job-row { grid-template-columns: 1fr auto; grid-auto-rows: auto; }
    .job-row-prio { grid-column: 2; grid-row: 1; align-self: start; }
    .job-row-lead { grid-column: 1; grid-row: 1; }
    .job-row-meta { grid-column: 1 / -1; grid-row: 2; }
    .job-row-people { grid-column: 1 / -1; grid-row: 3; justify-self: start; }
}
.job-row:hover {
    /* Rest already sits on --border-strong; step to --gray-400 (~2.6:1 vs
       white) so hover is still clearly promoted. Dark mode uses --gray-300
       which reads as a lighter, obviously-brighter border on the dark bg. */
    border-color: var(--gray-400);
    box-shadow: var(--shadow-sm);
    transform: translateY(-1px);
    text-decoration: none;
    color: var(--text);
}
[data-theme="dark"] .job-row:hover { border-color: var(--gray-300); }
/* Unit chips in a collapsed batch row are their own click target (they open the
   individual unit, not the batch), so they need to look separately hoverable
   from the row behind them. The chip's colours are inline in index.php; only the
   states that inline styles can't express live here. */
.job-row [data-unit-href]:hover { background: #dbeafe !important; border-color: #93c5fd !important; }
.job-row [data-unit-href]:focus-visible { outline: 2px solid var(--primary); outline-offset: 1px; }
[data-theme="dark"] .job-row [data-unit-href]:hover { background: #1e3a8a !important; border-color: #3b82f6 !important; }
.job-row-lead { min-width: 0; }
.job-row-title { font-size: 15px; font-weight: 600; margin-top: 4px; color: var(--text); word-break: break-word; }
.job-row-sub {
    font-size: 12px;
    color: var(--text-muted);
    margin-top: 4px;
    display: inline-flex;
    align-items: center;
    gap: 4px;
    word-break: break-word;
}
.job-row-meta {
    display: flex; align-items: center; gap: 8px;
    color: var(--text-muted);
    font-size: 12px;
    flex-wrap: wrap;
    justify-content: flex-end;
}
.job-row-meta .meta-item { display: inline-flex; align-items: center; gap: 3px; }
.job-row-meta .meta-item.danger { color: var(--danger); font-weight: 600; }
.job-row-meta .meta-item.warning { color: var(--warning); font-weight: 600; }
.job-row-people {
    display: flex; align-items: center;
    flex-shrink: 0;
}
.job-row-people .avatar-sm { margin-left: -6px; border: 2px solid var(--surface); }
.job-row-people .avatar-sm:first-child { margin-left: 0; }
.job-row-people .avatar.more { background: var(--gray-400); }

/* When the detail drawer is open, the job list pane is a narrow column
   (≤400px). Force single-column card layout so title/meta/people stack
   vertically like on mobile — otherwise they cram sideways and wrap ugly. */
.tabs-canvas.has-detail .job-row {
    grid-template-columns: 1fr;
    gap: 8px;
    padding: 12px 14px;
}
.tabs-canvas.has-detail .job-row-meta { justify-content: flex-start; }
.tabs-canvas.has-detail .job-row-people { justify-content: flex-start; }

@media (max-width: 720px) {
    .job-row { grid-template-columns: 1fr; gap: 8px; }
    .job-row-meta { justify-content: flex-start; }
    .stage-header-title { font-size: 17px; }
    .tabs-title-row { padding: 12px 12px 8px; }
    .stage-tabs { padding: 4px 8px 8px; }
    .tabs-canvas { padding: 14px 12px; }
    .stage-tab-name { max-width: 120px; }
    /* Touch devices scroll natively — the fade edges alone communicate overflow. */
    .stage-tabs-arrow { display: none; }
    .stage-tabs-wrap::before,
    .stage-tabs-wrap::after { width: 32px; }
}

/* Info summary block on stage pages (client + cabin snapshot) */
.stage-summary { display: flex; flex-direction: column; gap: 8px; }
.stage-summary .row-item {
    display: flex; gap: 8px;
    padding: 8px 10px;
    background: var(--gray-50);
    border-radius: var(--r-sm);
    font-size: 13px;
}
.stage-summary .row-label {
    color: var(--text-muted);
    min-width: 80px;
    font-size: 12px;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    font-weight: 600;
}
.stage-summary .row-value {
    color: var(--text);
    flex: 1;
    word-break: break-word;
}

/* Job card status pill (top of card) */
.status-strip {
    margin: -12px -14px 10px;
    padding: 6px 14px;
    font-size: 11px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: #fff;
    border-radius: var(--r-md) var(--r-md) 0 0;
    display: flex;
    align-items: center;
    gap: 6px;
}
.status-order_confirmed { background: var(--info); }
.status-in_progress     { background: var(--accent); }
.status-ready_to_load   { background: #6366f1; }
.status-delivered       { background: var(--success); }

/* Stage-index colour ramp — used on card tiles and stage badges.
   Cycles through hues so consecutive columns stand apart visually. */
.stage-idx-0  { background: #64748b; }  /* Enquiries       - slate */
.stage-idx-1  { background: #0ea5e9; }  /* Site Visit      - sky */
.stage-idx-2  { background: #2563eb; }  /* Order Confirmed - blue */
.stage-idx-3  { background: #7c3aed; }  /* Design & Approval - violet */
.stage-idx-4  { background: #a16207; }  /* Materials       - amber-brown */
.stage-idx-5  { background: #b45309; }  /* Fabrication     - orange */
.stage-idx-6  { background: #d97706; }  /* Finishing & QC  - amber */
.stage-idx-7  { background: #059669; }  /* Ready for Dispatch - green */
.stage-idx-8  { background: #16a34a; }  /* Installed On-Site - green */
.stage-idx-9  { background: #14532d; }  /* Aftersales      - dark green */

/* Stage block inside the card detail modal */
.stage-block {
    background: var(--gray-50);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    padding: 14px 16px;
    margin-bottom: 20px;
    display: flex;
    flex-direction: column;
    gap: 10px;
}
.stage-label {
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--text-muted);
    font-weight: 600;
}
.stage-row {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
}
.stage-pill {
    color: #fff;
    padding: 4px 10px;
    border-radius: var(--r-full);
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.02em;
    font-variant-numeric: tabular-nums;
}
.stage-name { font-size: 16px; }
.stage-advance {
    align-self: flex-start;
    max-width: 100%;
}
.stage-advance strong { font-weight: 700; }

.job-fields { display: flex; flex-direction: column; gap: 12px; }
.job-fields .row { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.job-fields label { margin: 0; display: flex; flex-direction: column; gap: 4px; }
/* Force every field control in a job-fields row to the same height so
   date-inputs, selects and phone-group flexes line up at the baseline.
   Without this, browsers pick each control's intrinsic height and the
   two "columns" of a .row visibly desync by 1–3px. */
.job-fields .row > label > input:not([type="checkbox"]):not([type="radio"]),
.job-fields .row > label > select,
.job-fields .row > label > textarea,
.job-fields .row > label > .phone-group { height: 40px; box-sizing: border-box; }
.job-fields .row > label > .phone-group > input,
.job-fields .row > label > .phone-group > select { height: 100%; box-sizing: border-box; }

/* Mobile: only stack rows explicitly marked .row-stack-mobile (e.g. Client
   name + phone — the phone-group's CC dropdown crowds the row on narrow
   screens). Rows that hold two small controls (like date + select) keep
   sharing a line to save vertical space. */
@media (max-width: 560px) {
    .job-fields .row.row-stack-mobile { grid-template-columns: 1fr; gap: 12px; }
}

/* Job ID strip inside the card modal header and on the tile */
.card-title-wrap {
    flex: 1;
    display: flex;
    flex-direction: column;
    gap: 4px;
    min-width: 0;
}
.job-id-strip {
    display: inline-flex;
    align-items: center;
    padding: 0 8px 0 0;
    color: var(--brand);
    font-family: 'SF Mono', Menlo, Consolas, monospace;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.03em;
}
.job-id-strip .mono { font-size: 11px; }
.tile-job-id {
    display: inline-block;
    padding: 2px 6px;
    margin-bottom: 6px;
    background: var(--gray-100);
    color: var(--gray-700);
    border-radius: 4px;
    font-size: 10.5px;
    font-weight: 700;
    letter-spacing: 0.04em;
}

/* Phone: country code select + local number input side by side */
.phone-group {
    display: flex;
    gap: 6px;
    align-items: stretch;
}
.phone-group .phone-cc {
    min-width: 96px;
    max-width: 108px;
    padding: 8px 6px;
    font-variant-numeric: tabular-nums;
}
.phone-group .phone-num { flex: 1; font-variant-numeric: tabular-nums; }

/* Structured cabin size (W × L × H unit) */
.dim-block {
    display: flex;
    flex-direction: column;
    gap: 6px;
}
.dim-label {
    font-size: 12px;
    color: var(--text-2);
    font-weight: 500;
}
.dim-row {
    display: flex;
    align-items: flex-end;
    gap: 6px;
    flex-wrap: wrap;
}
.dim-field {
    flex: 1 1 60px;
    min-width: 60px;
    display: flex;
    flex-direction: column;
    gap: 2px;
}
.dim-field input {
    text-align: center;
    font-variant-numeric: tabular-nums;
}
.dim-caption {
    text-align: center;
    font-size: 10px;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.06em;
}
.dim-x {
    font-size: 18px;
    color: var(--text-muted);
    align-self: center;
    padding-bottom: 18px;
}
.dim-unit {
    max-width: 90px;
    align-self: flex-start;
    margin-bottom: 18px;
}
@media (max-width: 640px) {
    .dim-row { gap: 8px; }
    .dim-field { flex-basis: 45%; }
    .dim-x { display: none; }
    .dim-unit { margin-bottom: 0; align-self: stretch; max-width: 100%; }
}

/* Batch-split wizard: per-unit size chip + collapsible dim sub-row */
.bsp-size-chip {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    max-width: 100%;
    padding: 6px 10px;
    font-size: 12px;
    border: 1px solid var(--border);
    border-radius: 999px;
    background: var(--gray-50, #f9fafb);
    color: var(--text-2);
    cursor: pointer;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.bsp-size-chip:hover { border-color: #b45309; color: #b45309; }
.bsp-size-chip.is-custom {
    background: #fef3c7;
    border-color: #fcd34d;
    color: #b45309;
    font-weight: 600;
}
.bsp-size-chip .bsp-chip-text { overflow: hidden; text-overflow: ellipsis; }
.bsp-size-sub {
    margin-top: 4px;
    padding: 10px 12px;
    background: var(--gray-50, #f9fafb);
    border: 1px dashed var(--border);
    border-radius: 8px;
}

/* Assignee chip enhancements */
.chip-name-block {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    line-height: 1.2;
    gap: 1px;
}
.chip-desig {
    font-size: 10.5px;
    color: var(--text-muted);
    font-weight: 500;
    text-transform: none;
    letter-spacing: 0;
}
.chip.chip-on .chip-desig { color: rgba(20, 83, 45, 0.7); }
.chip-locked {
    opacity: 0.55;
    cursor: not-allowed !important;
}
.chip-locked:hover { background: transparent !important; }
.chip-lock {
    margin-left: auto;
    font-size: 12px;
    opacity: 0.7;
}
.assign-hint {
    margin-top: 6px;
    padding: 6px 10px;
    background: var(--warning-light);
    border: 1px solid #fde68a;
    border-radius: var(--r-sm);
    color: #92400e;
    font-weight: 500;
}

/* Created-by block on the modal sidebar */
.created-block {
    background: var(--gray-50);
    border-radius: var(--r);
    padding: 10px 12px;
}
.created-line {
    font-size: 13px;
    color: var(--text);
    margin: 4px 0 4px;
    line-height: 1.4;
}
.created-line strong { font-weight: 600; }

/* Activity timeline inside the card modal */
.activity-list {
    display: flex;
    flex-direction: column;
    gap: 4px;
    max-height: 320px;
    overflow-y: auto;
    padding: 4px 0;
}
.activity-row {
    display: flex;
    gap: 10px;
    padding: 8px 4px;
    border-bottom: 1px dashed var(--border);
    align-items: flex-start;
}
.activity-row:last-child { border-bottom: none; }
.activity-icon {
    width: 26px;
    height: 26px;
    flex-shrink: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: var(--gray-100);
    border-radius: 50%;
    font-size: 13px;
    line-height: 1;
}
.activity-body { flex: 1; min-width: 0; }
.activity-line {
    font-size: 13px;
    line-height: 1.4;
    color: var(--text-2);
    word-break: break-word;
}
.activity-line strong { color: var(--text); }
.activity-detail {
    color: var(--text);
    background: var(--gray-100);
    padding: 1px 6px;
    border-radius: 4px;
    font-size: 12px;
    display: inline-block;
    margin-left: 2px;
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    vertical-align: bottom;
}
.activity-time { margin-top: 2px; cursor: default; }

/* Safe area (notches, home indicators) */
body { padding-top: env(safe-area-inset-top); padding-bottom: env(safe-area-inset-bottom); }
.topbar { padding-top: env(safe-area-inset-top); }

/* Install button (only shown when installable) */
.install-btn { animation: fadeIn 300ms ease-out; }

/* Install-hint bottom sheet (iOS Add-to-Home-Screen instructions, etc.) */
.install-sheet .modal-body { max-width: 460px; }
.install-hero {
    display: flex;
    align-items: center;
    gap: 14px;
    margin-bottom: 16px;
    padding-bottom: 16px;
    border-bottom: 1px solid var(--border);
}
.install-hero strong { display: block; font-size: 16px; margin-bottom: 2px; }
.install-steps {
    padding-left: 20px;
    margin: 0 0 4px;
    display: flex;
    flex-direction: column;
    gap: 10px;
}
.install-steps li { font-size: 14px; line-height: 1.5; color: var(--text-2); }
.install-steps li strong { color: var(--text); }

/* The login-screen install button gets a subtle style so it doesn't compete with Sign in */
.install-hint-btn {
    background: transparent;
    border-color: var(--border);
    color: var(--text-2);
}
.install-hint-btn:hover { background: var(--gray-50); }

/* ===== Top bar ===== */
.topbar {
    background: var(--surface);
    border-bottom: 1px solid var(--border);
    position: sticky;
    top: 0;
    z-index: 40;
}
.topbar-inner {
    max-width: 1400px;
    margin: 0 auto;
    padding: 10px 24px;
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 24px;
}
.brand-link {
    display: flex;
    align-items: center;
    gap: 10px;
    color: var(--text);
    font-weight: 700;
    text-decoration: none;
}
.brand-link:hover { color: var(--text); text-decoration: none; }
.brand-logo-img {
    /* Height-first sizing kept for legibility; but cap the rendered width so
       the header nav (Home / Works / Stock ...) doesn't get pushed right
       on desktop. The horizontal SVG is ~7.8:1 after trim; at 52px height
       the natural width is ~405px which is too much. 170px lets the mark
       + wordmark still read cleanly. */
    height: 52px;
    width: auto;
    max-width: 170px;
    display: block;
    object-fit: contain;
}
.brand-name {
    font-size: 13px;
    font-weight: 600;
    color: var(--text-muted);
    padding-left: 10px;
    margin-left: 6px;
    border-left: 1px solid var(--border);
    text-transform: uppercase;
    letter-spacing: 0.06em;
}

.topbar-nav {
    display: flex;
    align-items: center;
    gap: 4px;
    margin-left: 24px;
    flex: 1;
}
.nav-link {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 8px 12px;
    color: var(--text-muted);
    font-weight: 500;
    font-size: 14px;
    border-radius: var(--r-sm);
    transition: all var(--dur) var(--ease-out);
    text-decoration: none;
}
.nav-link:hover { background: var(--gray-100); color: var(--text); text-decoration: none; }
.nav-link.active { background: var(--brand-50); color: var(--brand); }

.topbar-actions { display: flex; align-items: center; gap: 6px; }
.user-menu { display: flex; align-items: center; gap: 10px; }
.user-name { font-weight: 500; font-size: 14px; color: var(--text-2); }

/* Bell + notifications */
.bell-btn { position: relative; }
.bell-badge {
    position: absolute;
    top: 4px; right: 4px;
    min-width: 16px;
    height: 16px;
    padding: 0 4px;
    background: var(--danger);
    color: #fff;
    border-radius: var(--r-full);
    font-size: 10px;
    font-weight: 700;
    display: flex;
    align-items: center;
    justify-content: center;
    line-height: 1;
    border: 2px solid var(--surface);
}
/* The hidden attribute has to beat display:flex above.
   Without this, the empty red dot stays visible when there are no unread notifications. */
.bell-badge[hidden] { display: none !important; }
.notif-panel {
    position: absolute;
    top: calc(100% + 6px);
    right: 24px;
    width: 380px;
    max-height: 480px;
    overflow-y: auto;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    box-shadow: var(--shadow-xl);
    z-index: 50;
    display: none;
}
.notif-panel[data-open="1"] { display: block; }
.notif-header {
    padding: 12px 16px;
    display: flex;
    justify-content: space-between;
    align-items: center;
    border-bottom: 1px solid var(--border);
    position: sticky;
    top: 0;
    background: var(--surface);
}
.notif-header strong { font-size: 14px; }
.notif-header-actions { display: flex; align-items: center; gap: 4px; }
.icon-btn-sm {
    width: 32px; height: 32px;
    border-radius: var(--r-full);
    color: var(--text-muted);
    background: transparent;
    border: 1px solid transparent;
    padding: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    position: relative;
}
.icon-btn-sm:hover { background: var(--gray-100); color: var(--text-2); }
#notif-mute-btn.is-muted { color: var(--danger); }
#notif-mute-btn .notif-mute-on,
#notif-mute-btn .notif-mute-off {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}
/* `hidden` attribute must win over the inline-flex above. */
#notif-mute-btn [hidden] { display: none !important; }
.notif-mute-slash {
    position: absolute;
    top: 50%; left: 50%;
    transform: translate(-50%, -50%) rotate(-25deg);
    font-weight: 700;
    font-size: 22px;
    line-height: 1;
    color: var(--danger);
    pointer-events: none;
}
#notif-os-btn.is-on { color: var(--brand); background: var(--brand-50); }
.notif-item {
    display: flex;
    gap: 10px;
    padding: 12px 16px;
    border-bottom: 1px solid var(--border);
    cursor: pointer;
    transition: background var(--dur);
    text-decoration: none;
    color: var(--text);
}
.notif-item:hover { background: var(--gray-50); text-decoration: none; }
.notif-item.unread { background: rgba(20, 83, 45, 0.03); }
.notif-item.unread::before {
    content: '';
    width: 6px; height: 6px;
    background: var(--brand);
    border-radius: 50%;
    margin-top: 8px;
    flex-shrink: 0;
}
.notif-body { flex: 1; min-width: 0; }
.notif-text { font-size: 13px; line-height: 1.4; color: var(--text-2); }
.notif-meta { font-size: 11px; color: var(--text-muted); margin-top: 2px; }
.notif-empty { padding: 32px 16px; text-align: center; color: var(--text-muted); font-size: 13px; }
/* Admin/mgmt-only link that always sits at the bottom of the bell panel so
   they can jump into the persistent activity log even when the panel is
   otherwise empty (the common "all caught up" case). */
.notif-footer-link {
    display: block;
    padding: 10px 16px;
    border-top: 1px solid var(--border);
    background: var(--gray-50, #f8fafc);
    text-align: center;
    font-size: 13px;
    font-weight: 600;
    color: var(--primary, #2563eb);
    text-decoration: none;
}
.notif-footer-link:hover { background: var(--gray-100, #f1f5f9); text-decoration: underline; }

/* ===== Avatars ===== */
.avatar {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    border-radius: 50%;
    color: #fff;
    font-weight: 600;
    font-size: 12px;
    flex-shrink: 0;
    box-shadow: inset 0 0 0 1px rgba(255,255,255,.2);
    text-transform: uppercase;
    letter-spacing: 0;
}
.avatar-xs { width: 18px; height: 18px; font-size: 9px; }
.avatar-sm { width: 24px; height: 24px; font-size: 10px; border: 2px solid var(--surface); }
.avatar-md { width: 32px; height: 32px; font-size: 12px; }
.avatar-lg { width: 40px; height: 40px; font-size: 14px; }
.avatar.more { background: var(--gray-400); }
.avatar-stack { display: inline-flex; }
.avatar-stack .avatar-sm { margin-left: -6px; }
.avatar-stack .avatar-sm:first-child { margin-left: 0; }

/* Avatar with an actual photo — clip the image to the circle */
.avatar-has-photo {
    overflow: hidden;
    background: var(--gray-100);
    box-shadow: none;
}
.avatar-has-photo img {
    width: 100%; height: 100%;
    object-fit: cover;
    display: block;
}

/* Profile page — big circular photo */
.profile-photo-wrap {
    width: 96px;
    height: 96px;
    border-radius: 50%;
    overflow: hidden;
    background: var(--gray-100);
    border: 3px solid var(--surface);
    box-shadow: var(--shadow-sm);
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
}
.profile-photo-wrap img { width: 100%; height: 100%; object-fit: cover; }
.profile-photo-initial {
    width: 100%; height: 100%;
    display: flex; align-items: center; justify-content: center;
    color: #fff; font-size: 34px; font-weight: 700; text-transform: uppercase;
}

/* Header avatar is clickable → /profile.php */
.user-avatar-link {
    display: inline-flex;
    text-decoration: none;
    border-radius: 50%;
}
.user-avatar-link:hover .avatar { transform: scale(1.05); transition: transform 120ms; }
.user-name { text-decoration: none; color: inherit; }
.user-name:hover { text-decoration: underline; }

/* ===== Container / dashboard ===== */
.container { max-width: 1400px; margin: 0 auto; padding: 32px 24px; }
.page-head {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    margin-bottom: 24px;
    gap: 16px;
    flex-wrap: wrap;
}
.page-head h1 { margin: 0 0 4px; }
.page-head .lead { color: var(--text-muted); font-size: 14px; }

.board-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
    gap: 16px;
}
.board-tile {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    overflow: hidden;
    color: var(--text);
    text-decoration: none;
    transition: all var(--dur) var(--ease-out);
    display: flex;
    flex-direction: column;
    min-height: 170px;
}
.board-tile:hover {
    transform: translateY(-2px);
    box-shadow: var(--shadow-lg);
    border-color: var(--border-strong);
    text-decoration: none;
    color: var(--text);
}
.board-tile:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.board-tile-strip { height: 6px; background: var(--tile-color, var(--brand)); }
.board-tile-body { padding: 20px; flex: 1; display: flex; flex-direction: column; }
.board-tile-body h3 { margin: 0 0 6px; }
.board-tile-body p { color: var(--text-muted); margin: 0 0 auto; font-size: 13px; line-height: 1.5; }
.board-tile-meta {
    display: flex;
    gap: 12px;
    align-items: center;
    margin-top: 20px;
    font-size: 12px;
    color: var(--text-muted);
}
.board-tile-meta .stat { display: inline-flex; align-items: center; gap: 4px; }
.role-pill {
    padding: 2px 8px;
    border-radius: var(--r-full);
    font-size: 11px;
    font-weight: 600;
    text-transform: capitalize;
    background: var(--gray-100);
    color: var(--gray-700);
    margin-left: auto;
    border: 1px solid var(--gray-200);
}
.role-owner  { background: var(--accent-light); color: var(--accent); border-color: #fde68a; }
.role-admin  { background: var(--info-light);   color: #175cd3;      border-color: #b2ddff; }
.role-viewer { background: var(--gray-100);     color: var(--gray-600); }

.empty {
    grid-column: 1 / -1;
    text-align: center;
    padding: 64px 20px;
    color: var(--text-muted);
    background: var(--surface);
    border-radius: var(--r-lg);
    border: 2px dashed var(--border-strong);
}
.empty svg { color: var(--text-subtle); margin-bottom: 12px; }
.empty h3 { color: var(--text-2); margin-bottom: 6px; }
/* Compact empty state — used inside card panels (attachments, comments, tags) */
.empty-mini {
    text-align: center;
    padding: 24px 12px;
    color: var(--text-muted);
    font-size: 13px;
    background: var(--gray-50);
    border-radius: var(--r);
    border: 1px dashed var(--border);
}
.empty-mini .empty-mini-icon { color: var(--text-subtle); margin-bottom: 6px; }
.empty-mini strong { display: block; color: var(--text-2); margin-bottom: 2px; font-size: 13px; }
.empty-mini span { display: block; font-size: 12px; }

/* ===== Cards (stat cards for reports) ===== */
.stat-card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    padding: 20px;
}
.stat-card .label {
    font-size: 12px;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--text-muted);
    font-weight: 600;
}
.stat-card .value { font-size: 28px; font-weight: 700; margin-top: 6px; color: var(--text); letter-spacing: -0.02em; }
.stat-card .sub   { font-size: 12px; color: var(--text-muted); margin-top: 6px; }

.stat-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
    gap: 16px;
    margin-bottom: 24px;
}

/* ===== Modal ===== */
.modal {
    position: fixed;
    inset: 0;
    background: rgba(16, 24, 40, 0.55);
    backdrop-filter: blur(4px);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 100;
    padding: 20px;
    overflow-y: auto;
    animation: fadeIn 200ms var(--ease-out);
}
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
.modal[hidden] { display: none; }
.modal-body {
    background: var(--surface);
    border-radius: var(--r-lg);
    padding: 24px;
    max-width: 480px;
    width: 100%;
    box-shadow: var(--shadow-2xl);
    max-height: 90vh;
    overflow-y: auto;
    animation: pop 240ms var(--ease-out);
}
@keyframes pop {
    from { opacity: 0; transform: translateY(8px) scale(.98); }
    to   { opacity: 1; transform: translateY(0)   scale(1); }
}
.modal-lg .modal-body { max-width: 860px; padding: 24px; }
.modal h2 { margin: 0 0 16px; }
.modal-actions {
    display: flex;
    justify-content: flex-end;
    gap: 8px;
    margin-top: 24px;
}

.color-picker { display: flex; gap: 10px; margin-top: 6px; flex-wrap: wrap; }
.swatch {
    width: 32px;
    height: 32px;
    border-radius: 50%;
    cursor: pointer;
    position: relative;
    border: 2px solid transparent;
    transition: transform var(--dur), box-shadow var(--dur);
}
.swatch:hover { transform: scale(1.1); }
.swatch input { position: absolute; opacity: 0; inset: 0; cursor: pointer; }
.swatch:has(input:checked) { box-shadow: 0 0 0 2px var(--surface) inset, 0 0 0 4px var(--brand); }

.inline-form {
    display: flex;
    gap: 8px;
    margin-top: 12px;
    align-items: stretch;
}
.inline-form input, .inline-form select, .inline-form textarea { flex: 1; }
.inline-form textarea { min-height: 56px; }
.inline-form input[type="color"] { max-width: 44px; padding: 4px; min-height: 38px; }

/* ===== Board page ===== */
.board-page { background: var(--gray-100); min-height: 100vh; }
.board-bar {
    background: var(--surface);
    border-bottom: 1px solid var(--border);
    position: sticky;
    top: 53px;
    z-index: 20;
}
.board-bar-inner {
    max-width: 1600px;
    margin: 0 auto;
    padding: 14px 24px 14px 20px;
    display: flex;
    justify-content: space-between;
    align-items: center;
    border-left: 4px solid var(--board-color, var(--brand));
    gap: 20px;
}
.board-title { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; min-width: 0; }
.board-title h1 { font-size: 18px; margin: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.back-link {
    color: var(--text-muted);
    font-size: 13px;
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 4px 8px;
    border-radius: var(--r-sm);
    transition: all var(--dur);
}
.back-link:hover { background: var(--gray-100); color: var(--text); text-decoration: none; }
.board-actions { display: flex; gap: 6px; }

.board-canvas {
    overflow-x: auto;
    padding: 20px 24px 40px;
    height: calc(100vh - 53px - 63px);
    scrollbar-color: var(--gray-300) transparent;
}
.board-canvas::-webkit-scrollbar { height: 12px; }
.board-canvas::-webkit-scrollbar-thumb { background: var(--gray-300); border-radius: 6px; }
.columns {
    display: flex;
    gap: 16px;
    align-items: flex-start;
    min-height: 100%;
    padding-bottom: 4px;
}

/* ===== Column ===== */
.column {
    background: var(--gray-100);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    width: 308px;
    flex-shrink: 0;
    display: flex;
    flex-direction: column;
    max-height: calc(100vh - 170px);
}
.column.col-dragging { opacity: 0.4; }
.column-head {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 12px 14px 10px;
    cursor: grab;
    user-select: none;
}
.col-name {
    font-weight: 600;
    font-size: 14px;
    flex: 1;
    padding: 2px 4px;
    border-radius: 4px;
    color: var(--text);
}
.col-name:hover { background: rgba(0,0,0,.04); }
.col-count {
    background: var(--gray-200);
    color: var(--gray-600);
    padding: 2px 8px;
    border-radius: var(--r-full);
    font-size: 11px;
    font-weight: 700;
    min-width: 22px;
    text-align: center;
}

.cards {
    padding: 0 8px 8px;
    overflow-y: auto;
    flex: 1;
    min-height: 24px;
    scrollbar-color: var(--gray-300) transparent;
}
.cards::-webkit-scrollbar { width: 6px; }
.cards::-webkit-scrollbar-thumb { background: var(--gray-300); border-radius: 3px; }
.add-card-btn {
    display: flex;
    align-items: center;
    gap: 4px;
    background: transparent;
    border: none;
    color: var(--text-muted);
    padding: 10px 14px;
    text-align: left;
    cursor: pointer;
    border-radius: var(--r-sm);
    margin: 4px 8px 8px;
    font-size: 13px;
    font-weight: 500;
    transition: all var(--dur);
}
.add-card-btn:hover { background: rgba(255,255,255,.7); color: var(--text); }

.column-add {
    background: rgba(255,255,255,.4);
    border: 2px dashed var(--border-strong);
    padding: 8px;
    min-height: 60px;
}
.add-col-btn {
    display: flex;
    align-items: center;
    gap: 6px;
    background: transparent;
    border: none;
    padding: 14px;
    width: 100%;
    text-align: left;
    color: var(--text-muted);
    font-weight: 500;
    cursor: pointer;
    border-radius: var(--r-sm);
    font-size: 14px;
}
.add-col-btn:hover { background: var(--surface); color: var(--text); }

/* ===== Card ===== */
.card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r-md);
    padding: 12px 14px;
    margin-bottom: 8px;
    cursor: pointer;
    transition: all var(--dur) var(--ease-out);
    box-shadow: var(--shadow-xs);
}
.card:hover { box-shadow: var(--shadow); border-color: var(--border-strong); }
.card:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.card.dragging { opacity: 0.5; transform: rotate(2deg); }
/* Cards in the final stage — visually calm, not "struck through".
   These are cabins that are DELIVERED but not yet archived (warranty may still be open). */
.card.done { border-left: 3px solid var(--success); padding-left: 11px; }

/* Small banner shown inside the card modal when a job is already archived */
.archive-banner {
    background: var(--success-light);
    border: 1px solid #a6f4c5;
    border-radius: var(--r);
    padding: 12px 14px;
}
.archive-banner label {
    color: var(--brand-dark);
    font-weight: 700;
    display: block;
    margin-bottom: 4px;
}
.card-placeholder {
    background: rgba(20, 83, 45, .08);
    border: 2px dashed var(--brand-light);
    border-radius: var(--r-md);
    height: 70px;
    margin-bottom: 8px;
}
.card-tags {
    display: flex;
    flex-wrap: wrap;
    gap: 4px;
    margin-bottom: 8px;
}
.tag {
    color: #fff;
    padding: 3px 8px;
    border-radius: var(--r-sm);
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.02em;
    line-height: 1.2;
    display: inline-flex;
    align-items: center;
}
.card-title { margin: 0 0 8px; font-size: 14px; font-weight: 500; line-height: 1.4; color: var(--text); }
.card-foot {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 8px;
}
.card-meta { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; color: var(--text-muted); font-size: 12px; }
.card-meta .meta-item {
    display: inline-flex;
    align-items: center;
    gap: 3px;
    font-weight: 500;
}
.card-meta .meta-item.danger { color: var(--danger); }
.card-meta .meta-item.warning { color: var(--warning); }

.card-quick-add {
    background: var(--surface);
    border: 1px solid var(--brand-light);
    border-radius: var(--r-md);
    padding: 10px;
    margin-bottom: 8px;
    box-shadow: var(--shadow);
}
.card-quick-add textarea {
    width: 100%;
    border: none;
    resize: none;
    padding: 4px;
    font-size: 14px;
    min-height: 60px;
}
.card-quick-add textarea:focus { outline: none; box-shadow: none; }
.card-quick-add .row { display: flex; gap: 8px; margin-top: 8px; }

/* ===== Card modal ===== */
.card-modal-head {
    display: flex;
    padding: 20px 24px 16px;
    border-bottom: 1px solid var(--border);
    gap: 12px;
    align-items: center;
}
.card-title-input {
    font-size: 20px;
    font-weight: 600;
    border: none;
    padding: 4px 8px;
    flex: 1;
    background: transparent;
    letter-spacing: -0.01em;
    min-height: 0;
}
.card-title-input:hover { background: var(--gray-50); }
.card-title-input:focus {
    background: var(--surface);
    border: 1px solid var(--brand-light);
    box-shadow: 0 0 0 4px rgba(82, 183, 136, 0.15);
}

.card-modal-grid {
    display: grid;
    grid-template-columns: 1fr 280px;
    gap: 32px;
    padding: 20px 24px 28px;
}
.card-modal-main section { margin-bottom: 28px; }
.card-modal-main section:last-child { margin-bottom: 0; }
.section-head {
    display: flex;
    align-items: center;
    gap: 8px;
    color: var(--text-muted);
    margin-bottom: 12px;
    font-size: 12px;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    font-weight: 600;
}
.section-head svg { color: var(--text-muted); }
.card-modal-main textarea { min-height: 90px; }

.card-modal-side .side-block {
    margin-bottom: 20px;
    padding-bottom: 20px;
    border-bottom: 1px solid var(--border);
}
.card-modal-side .side-block:last-child { border-bottom: none; }
.card-modal-side label {
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--text-muted);
    margin-bottom: 8px;
    font-weight: 600;
    display: block;
}
.chip-list { display: flex; flex-direction: column; gap: 6px; }
.chip {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 6px 10px;
    border: 1px solid var(--border);
    border-radius: var(--r-sm);
    background: var(--surface);
    cursor: pointer;
    text-align: left;
    font-size: 13px;
    transition: all var(--dur);
    color: var(--text-2);
    min-height: 32px;
}
.chip:hover { background: var(--gray-50); border-color: var(--border-strong); }
.chip.chip-on { background: var(--brand-50); border-color: var(--brand-light); color: var(--brand); }

/* Checklist */
.checklist { display: flex; flex-direction: column; gap: 2px; margin-bottom: 8px; }
.checklist-progress {
    height: 6px;
    background: var(--gray-100);
    border-radius: var(--r-full);
    overflow: hidden;
    margin-bottom: 12px;
}
.checklist-progress-bar {
    height: 100%;
    background: var(--success);
    border-radius: var(--r-full);
    transition: width 300ms var(--ease-out);
}
.checklist-item {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 10px;
    border-radius: var(--r-sm);
    transition: background var(--dur);
}
.checklist-item:hover { background: var(--gray-50); }
.checklist-item.done .cl-text { text-decoration: line-through; color: var(--text-muted); }
.checklist-item input[type="checkbox"] {
    width: 18px; height: 18px; margin: 0; flex-shrink: 0;
    accent-color: var(--brand);
    cursor: pointer;
    min-height: 0;
}
.cl-text { flex: 1; font-size: 14px; color: var(--text-2); }

/* Attachments */
.attach-list { display: flex; flex-direction: column; gap: 6px; margin-bottom: 12px; }
.attach-item {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 8px 12px;
    border: 1px solid var(--border);
    border-radius: var(--r);
    background: var(--surface);
    text-decoration: none;
    color: var(--text-2);
    transition: all var(--dur);
}
.attach-item:hover { border-color: var(--border-strong); background: var(--gray-50); text-decoration: none; color: var(--text); }
.attach-thumb {
    width: 40px;
    height: 40px;
    border-radius: var(--r-sm);
    background: var(--gray-100);
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--text-muted);
    overflow: hidden;
    flex-shrink: 0;
}
.attach-thumb img { width: 100%; height: 100%; object-fit: cover; }
.attach-info { flex: 1; min-width: 0; }
.attach-info .name {
    font-weight: 500;
    font-size: 13px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.attach-info .meta { font-size: 11px; color: var(--text-muted); margin-top: 2px; }
.attach-upload {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 8px 12px;
    border: 1px dashed var(--border-strong);
    border-radius: var(--r);
    color: var(--text-muted);
    cursor: pointer;
    font-size: 13px;
    transition: all var(--dur);
    background: var(--gray-50);
}
.attach-upload:hover { border-color: var(--brand); color: var(--brand); background: var(--brand-50); }
.attach-upload input { display: none; }

/* Comments */
.comment-list { display: flex; flex-direction: column; gap: 14px; margin-bottom: 14px; }
.comment { display: flex; gap: 10px; align-items: flex-start; }
.comment-body {
    background: var(--gray-50);
    border: 1px solid var(--border);
    padding: 10px 14px;
    border-radius: var(--r);
    flex: 1;
    font-size: 13px;
}
.comment-head { display: flex; gap: 10px; align-items: baseline; margin-bottom: 6px; }
.comment-head strong { font-size: 13px; }
.comment-text { line-height: 1.5; color: var(--text-2); }
.comment-text .mention {
    color: var(--brand);
    background: var(--brand-50);
    padding: 1px 4px;
    border-radius: 3px;
    font-weight: 500;
}

/* Members / tags manage lists */
.member-list { display: flex; flex-direction: column; gap: 6px; margin-bottom: 16px; }
.member-row {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 10px 12px;
    border-radius: var(--r);
    background: var(--gray-50);
    border: 1px solid var(--border);
}
.member-info { flex: 1; display: flex; flex-direction: column; min-width: 0; }
.member-info strong { font-size: 13px; }
.member-info .muted { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

.tag-manage-list { display: flex; flex-direction: column; gap: 6px; margin-bottom: 12px; }
.tag-row {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 8px 12px;
    background: var(--gray-50);
    border-radius: var(--r);
    border: 1px solid var(--border);
}

/* Mention dropdown */
.mention-drop {
    position: absolute;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r);
    box-shadow: var(--shadow-lg);
    padding: 4px;
    z-index: 200;
    min-width: 220px;
    max-height: 220px;
    overflow-y: auto;
}
.mention-opt {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 6px 8px;
    border-radius: var(--r-sm);
    cursor: pointer;
    font-size: 13px;
}
.mention-opt:hover, .mention-opt.on { background: var(--brand-50); color: var(--brand); }

/* ===== Table (for reports) ===== */
.data-table {
    width: 100%;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    overflow: hidden;
    border-collapse: separate;
    border-spacing: 0;
}
.data-table th, .data-table td {
    padding: 12px 16px;
    text-align: left;
    font-size: 13px;
    border-bottom: 1px solid var(--border);
}
.data-table th {
    background: var(--gray-50);
    font-weight: 600;
    color: var(--text-2);
    font-size: 12px;
    text-transform: uppercase;
    letter-spacing: 0.04em;
}
.data-table tr:last-child td { border-bottom: none; }
.data-table tr:hover td { background: var(--gray-25); }
.progress {
    height: 8px;
    background: var(--gray-100);
    border-radius: var(--r-full);
    overflow: hidden;
    width: 100%;
    max-width: 200px;
}
.progress-bar {
    height: 100%;
    background: var(--brand);
    border-radius: var(--r-full);
}

/* ===== Reports > Workload by teammate (phase-card layout) ===== */

.phase-title {
    display: flex;
    align-items: baseline;
    gap: 12px;
    font-size: 15px;
    margin: 28px 0 10px;
    padding: 4px 0 4px 14px;
    border-left: 4px solid var(--brand);
    color: var(--text);
}
.phase-title-txt { font-weight: 700; }
.phase-title-count {
    font-size: 12px;
    color: var(--text-muted);
    font-weight: 500;
    font-variant-numeric: tabular-nums;
}

/* Card grid: auto-fill so cards fill available width; each card is at least
   280px wide so 4 stage chips fit on one row on typical desktops, wrapping
   to 2×2 on mobile. Zero horizontal scrolling. */
.wl-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
    gap: 12px;
    margin-bottom: 8px;
}
.wl-card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    padding: 14px;
    display: flex;
    flex-direction: column;
    gap: 10px;
    border-top: 3px solid var(--phase-accent, var(--brand));
    transition: box-shadow .15s, transform .15s;
}
.wl-card:hover { box-shadow: 0 3px 10px rgba(0,0,0,.06); }
.wl-card[hidden] { display: none; }

.wl-card-head {
    display: flex;
    align-items: center;
    gap: 10px;
}
.wl-card-head .avatar-md {
    width: 40px; height: 40px;
    flex-shrink: 0;
    font-size: 15px;
}
.wl-card-txt { flex: 1; min-width: 0; }
.wl-card-name {
    font-weight: 600;
    font-size: 14px;
    color: var(--text);
    line-height: 1.2;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.wl-card-role {
    font-size: 11px;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.04em;
    margin-top: 2px;
}
.wl-card-total {
    font-size: 22px;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    color: var(--phase-accent, var(--brand));
    line-height: 1;
    display: flex;
    flex-direction: column;
    align-items: flex-end;
}
.wl-card-total-lbl {
    font-size: 9px;
    font-weight: 500;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    margin-top: 2px;
}

.wl-card-chips {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
}

/* ── Workload "By cabin": one card per job ──────────────────────────────────
   Same visual language as .wl-card above (surface, accent top border, flex
   column) so the three groupings read as one control rather than a table and
   two card views. 320px min because a job title plus a stage name needs more
   room than four stage chips do; auto-fill still gives 3-4 across on desktop
   and exactly one on a phone, with no horizontal scroll either way. */
.wl-cab-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
    gap: 12px;
    margin-bottom: 24px;
}
.wl-cab-card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    border-top: 3px solid var(--phase-accent, var(--brand));
    padding: 12px 14px;
    display: flex;
    flex-direction: column;
    gap: 6px;
    transition: box-shadow .15s;
}
/* The card IS the link (no "Open" button), so it needs anchor resets and the
   same hover/press affordance the stage-list job rows have. */
.wl-cab-card { text-decoration: none; color: var(--text); }
.wl-cab-card:hover {
    box-shadow: var(--shadow-sm);
    border-color: var(--gray-400);
    transform: translateY(-1px);
    text-decoration: none;
    color: var(--text);
}
.wl-cab-card:active { transform: none; }
.wl-cab-card:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }
[data-theme="dark"] .wl-cab-card:hover { border-color: var(--gray-300); }
/* Explicit, because the search filter hides cards with the [hidden] attribute
   and the UA rule behind it loses to any author `display`. This is the bug that
   made a phone show unfiltered results while desktop showed the real hit. */
.wl-cab-card[hidden] { display: none; }

.wl-cab-top {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
}
.wl-cab-job {
    font-size: 11px;
    color: var(--text-muted);
    letter-spacing: 0.02em;
}
.wl-cab-status {
    font-size: 10px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    padding: 2px 7px;
    border-radius: 999px;
    background: var(--gray-100);
    color: var(--text-muted);
    white-space: nowrap;
    flex-shrink: 0;
}
.wl-cab-status.is-danger  { background: rgba(254,226,226,.7); color: var(--danger); }
.wl-cab-status.is-warn    { background: rgba(254,243,199,.7); color: var(--warning); }
.wl-cab-status.is-success { background: rgba(209,250,229,.7); color: var(--success); }

.wl-cab-title {
    font-weight: 600;
    font-size: 14px;
    color: var(--text);
    line-height: 1.3;
}
.wl-cab-sub {
    font-size: 11px;
    color: var(--text-muted);
    line-height: 1.3;
}
/* Icon + value pairs that wrap. An absent field renders nothing at all, which
   is why this replaced fixed columns full of em-dashes. */
.wl-cab-meta {
    display: flex;
    flex-wrap: wrap;
    gap: 4px 12px;
    margin-top: 2px;
}
.wl-cab-m {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    font-size: 12px;
    color: var(--text-muted);
    min-width: 0;
}
.wl-cab-m svg { flex-shrink: 0; opacity: .65; }
.wl-cab-m.is-danger { color: var(--danger); font-weight: 600; }
.wl-cab-m.is-danger svg { opacity: 1; }

/* margin-top:auto pins this to the card's bottom edge. Grid rows stretch every
   card to the tallest in the row, so without it a short card left its button
   floating mid-air and the buttons across a row didn't line up. */
.wl-cab-foot {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    margin-top: auto;
    padding-top: 6px;
}

/* ── List layout: one job per full-width ROW ────────────────────────────────
   The Workload toggle is List / Grid / Charts, and List is the default: on a PC
   this is a list of jobs, one per line, not a 3-across gallery. Same shape as
   .job-row on a stage list — accent stripe down the left, identity left, meta
   centre, status and due date right — so scanning 23 jobs is one vertical sweep
   instead of a zig-zag. Grid keeps the gallery; the base rules above ARE the
   grid, so only List needs describing.

   Scoped to the viewport's data-layout (set by the toggle's apply()) and to
   min-width, because a phone is one card per row either way and the row layout's
   six columns have nowhere to go at 390px.

   The card's children live inside .wl-cab-top / .wl-cab-foot wrappers, which are
   what the phone layout needs. `display: contents` dissolves those wrappers here
   so their spans become direct grid items, and every item is then placed by
   explicit grid-column/grid-row — so DOM order (job, status, title, sub, meta,
   due, Restore) is free to differ from the visual order without touching the
   markup or breaking the mobile card. */
@media (min-width: 641px) {
    .wl-viewport[data-layout="list"] .wl-cab-grid {
        grid-template-columns: 1fr;
        gap: 8px;
    }
    .wl-viewport[data-layout="list"] .wl-cab-card {
        display: grid;
        /* job | title+sub | meta | due | status | Restore.
           The two autos on the right collapse to 0 when a card has no due date
           or no status, and Restore's track only ever materialises on the
           cancelled group. */
        grid-template-columns: 96px minmax(150px, 1.5fr) minmax(0, 2fr) auto auto auto;
        grid-template-rows: auto auto;
        align-items: center;
        gap: 2px 16px;
        padding: 11px 16px;
        /* Accent moves from the top edge to the left, like .job-row: on a
           full-width row a 3px top border reads as a divider, not an accent. */
        border-top: 1px solid var(--border);
        border-left: 3px solid var(--phase-accent, var(--brand));
    }
    /* The `display: grid` above outranks .wl-cab-card[hidden] (attribute +
       two classes beats one class + attribute), so the search filter stopped
       hiding rows the moment List got its own display. Third time this trap has
       bitten in this file — any rule that sets `display` on a filterable element
       has to re-state [hidden] at its own specificity. */
    .wl-viewport[data-layout="list"] .wl-cab-card[hidden] { display: none; }

    /* Every rule below is scoped to data-layout="list" as well — unscoped they
       would leak into Grid and tear the gallery cards apart (dissolved wrappers,
       six-column placement, single-line truncation). */
    .wl-viewport[data-layout="list"] .wl-cab-top,
    .wl-viewport[data-layout="list"] .wl-cab-foot { display: contents; }

    .wl-viewport[data-layout="list"] .wl-cab-job    { grid-column: 1; grid-row: 1 / -1; }
    .wl-viewport[data-layout="list"] .wl-cab-title  { grid-column: 2; grid-row: 1; }
    .wl-viewport[data-layout="list"] .wl-cab-sub    { grid-column: 2; grid-row: 2; }
    .wl-viewport[data-layout="list"] .wl-cab-meta   { grid-column: 3; grid-row: 1 / -1; margin-top: 0; }
    /* The due-date span is the only .wl-cab-m that lives in the footer. */
    .wl-viewport[data-layout="list"] .wl-cab-foot .wl-cab-m { grid-column: 4; grid-row: 1 / -1; justify-self: end; }
    .wl-viewport[data-layout="list"] .wl-cab-status { grid-column: 5; grid-row: 1 / -1; justify-self: end; }
    .wl-viewport[data-layout="list"] .wl-cab-foot .btn { grid-column: 6; grid-row: 1 / -1; }
    /* Placeholder the footer emits when a cancelled card has no due date. With
       the wrapper dissolved it would otherwise claim a grid cell of its own. */
    .wl-viewport[data-layout="list"] .wl-cab-foot > span:empty { display: none; }

    /* A single line each — a row layout has no room to reflow, and truncating
       keeps the right-hand columns from being shoved off by one long title. */
    .wl-viewport[data-layout="list"] .wl-cab-title,
    .wl-viewport[data-layout="list"] .wl-cab-sub { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .wl-viewport[data-layout="list"] .wl-cab-meta { flex-wrap: nowrap; overflow: hidden; }
    .wl-viewport[data-layout="list"] .wl-cab-m { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    /* Rows are shorter than cards, so lift the hover cue off the shadow alone. */
    .wl-viewport[data-layout="list"] .wl-cab-card:hover { background: var(--gray-25); }
}

/* One card per row on a phone. Restore (cancelled cards only) is the sole
   button left, so let it fill the width beside the due date; the 44px height is
   the app's touch-target minimum and is deliberately kept. */
@media (max-width: 640px) {
    .wl-cab-grid { grid-template-columns: 1fr; gap: 10px; }
    .wl-cab-foot .btn { flex: 1; }
}

[data-theme="dark"] .wl-cab-status            { background: rgba(255,255,255,.07); }
[data-theme="dark"] .wl-cab-status.is-danger  { background: rgba(220,38,38,.18); }
[data-theme="dark"] .wl-cab-status.is-warn    { background: rgba(217,119,6,.20); }
[data-theme="dark"] .wl-cab-status.is-success { background: rgba(16,185,129,.18); }
.wl-chip {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 6px 10px;
    border-radius: var(--r-full);
    border: 1px solid var(--border);
    background: var(--gray-50);
    font-size: 12px;
    color: var(--text-2);
    cursor: pointer;
    font-family: inherit;
    line-height: 1;
    transition: background .12s, border-color .12s, transform .08s;
    min-height: 30px;
}
.wl-chip:not([disabled]):hover { background: var(--surface); border-color: var(--phase-accent, var(--brand-light)); transform: translateY(-1px); }
.wl-chip.wl-chip-active { background: var(--phase-accent, var(--brand)); color: #fff; border-color: transparent; }
.wl-chip.wl-chip-active .wl-chip-n { color: #fff; }
.wl-chip[disabled] {
    opacity: 0.4;
    cursor: default;
}
.wl-chip-n {
    font-weight: 700;
    font-size: 13px;
    color: var(--phase-accent, var(--brand));
    font-variant-numeric: tabular-nums;
    min-width: 14px;
    text-align: center;
}
.wl-chip-empty .wl-chip-n { color: var(--text-subtle); }
.wl-chip-lbl { color: inherit; }

/* Editable stage-due chip in the drawer/stage header. Same look as the
   view-only chip but with a subtle hover state + tiny edit icon so mgmt
   knows it's clickable. */
.stage-due-editable {
    border-style: solid; cursor: pointer;
    font-family: inherit;
    display: inline-flex; align-items: center; gap: 6px;
}
.stage-due-editable:hover { filter: brightness(1.05); box-shadow: 0 1px 3px rgba(0,0,0,0.15); }
.stage-due-editable svg { opacity: 0.75; }
.stage-due-unset {
    background: var(--gray-50); color: var(--text-muted);
    border: 1px dashed var(--border-strong);
}

/* Stage-due modal — lightweight, no dependencies on existing modal CSS */
.stage-due-modal {
    position: fixed; inset: 0; z-index: 500;
    background: rgba(0,0,0,0.55);
    display: flex; align-items: center; justify-content: center;
    padding: 20px;
    animation: fadeIn 180ms var(--ease-out);
}
.stage-due-modal-body {
    background: var(--surface); border: 1px solid var(--border);
    border-radius: var(--r-lg); padding: 20px;
    max-width: 400px; width: 100%;
    box-shadow: var(--shadow-xl);
}
.stage-due-modal-body h3 { margin: 0 0 6px; font-size: 16px; }
.stage-due-modal-body input[type="date"] {
    width: 100%; min-height: 44px; padding: 8px 12px;
    border: 1px solid var(--border-strong); border-radius: var(--r);
    background: var(--surface); color: var(--text);
    font-size: 15px; margin-top: 8px;
}
.stage-due-modal-actions {
    display: flex; gap: 8px; justify-content: flex-end; flex-wrap: wrap;
    margin-top: 16px;
}
@media (max-width: 640px) {
    .stage-due-modal-body { padding: 16px; }
    .stage-due-modal-actions .btn { flex: 1 1 auto; min-width: 100px; }
}

/* QC "Send back for rework" button — amber caution styling so it reads as
   distinct from the primary Approve action. Not destructive (no red), but
   clearly a "reverse course" action. */
.qc-rework-btn {
    color: #92400e;
    border-color: #fcd34d;
    background: var(--warning-light, #fef3c7);
}
.qc-rework-btn:hover {
    color: #78350f;
    background: #fde68a;
    border-color: #f59e0b;
}
.qc-rework-btn svg { stroke-width: 2; }
[data-theme="dark"] .qc-rework-btn {
    color: #fde68a;
    background: rgba(234, 179, 8, 0.14);
    border-color: rgba(234, 179, 8, 0.4);
}
[data-theme="dark"] .qc-rework-btn:hover {
    color: #fff;
    background: rgba(234, 179, 8, 0.28);
    border-color: rgba(234, 179, 8, 0.6);
}

/* Call-log phone-number line under each entry */
.call-phone { display: inline-flex; align-items: center; gap: 4px; margin-top: 2px; }
.call-phone a { color: inherit; }
.call-phone a:hover { color: var(--brand); text-decoration: underline; }

/* Reports date-range filter */
.reports-daterange {
    display: flex; flex-direction: column; gap: 10px;
    background: var(--surface); border: 1px solid var(--border);
    border-radius: var(--r); padding: 12px; margin-bottom: 20px;
}
.reports-daterange-presets {
    display: flex; flex-wrap: wrap; gap: 6px;
}
.reports-daterange-presets .chip {
    background: transparent; border: 1px solid var(--border);
    color: var(--text-2); padding: 6px 12px; border-radius: var(--r-full);
    cursor: pointer; font-size: 13px; font-family: inherit;
    text-decoration: none;
    display: inline-block;
    line-height: 1.4;
}
.reports-daterange-presets .chip:hover { border-color: var(--brand-light); color: var(--text); text-decoration: none; }
.reports-daterange-presets .chip.chip-on {
    background: var(--brand); color: #fff; border-color: var(--brand);
}
.reports-daterange-presets .chip.chip-on:hover { color: #fff; }
.reports-daterange-custom {
    display: flex; flex-wrap: wrap; gap: 12px; align-items: end;
    padding-top: 8px; border-top: 1px dashed var(--border);
}
.reports-daterange-custom label {
    display: flex; flex-direction: column; gap: 4px;
    font-size: 12px; color: var(--text-muted); font-weight: 500;
}
.reports-daterange-custom input[type="date"] {
    min-height: 36px; padding: 4px 10px;
    border: 1px solid var(--border-strong);
    border-radius: var(--r-sm); background: var(--surface); color: var(--text);
}

/* Trade-status strip inside a Production chip. Sits after the label so the
   chip reads:  <n> <label> <✓3 ⏳2 ○1>  — a mini breakdown at a glance. */
.wl-chip-status {
    display: inline-flex; align-items: center; gap: 4px;
    margin-left: 4px; padding-left: 6px;
    border-left: 1px solid var(--border);
    font-size: 11px; font-weight: 600;
    font-variant-numeric: tabular-nums;
}
.wl-chip.wl-chip-active .wl-chip-status { border-left-color: rgba(255,255,255,.35); }
.wl-chip-status-bit { display: inline-flex; align-items: center; gap: 1px; }
.wl-cs-complete    { color: #059669; }
.wl-cs-in_progress { color: #2563eb; }
.wl-cs-not_started { color: var(--text-muted); }
.wl-cs-blocked     { color: #dc2626; }
.wl-chip.wl-chip-active .wl-cs-complete,
.wl-chip.wl-chip-active .wl-cs-in_progress,
.wl-chip.wl-chip-active .wl-cs-not_started,
.wl-chip.wl-chip-active .wl-cs-blocked { color: #fff; }

/* Popover row status dot — sits left of the title. */
.wl-pop-status {
    display: inline-flex; align-items: center; justify-content: center;
    width: 18px; height: 18px;
    font-size: 12px; line-height: 1;
    margin-right: 8px; flex-shrink: 0;
}

/* Attention-needed variants */
.wl-chip-danger  { border-color: rgba(220,38,38,0.35); background: rgba(254,226,226,0.5); }
.wl-chip-danger .wl-chip-n  { color: var(--danger); }
.wl-chip-warn    { border-color: rgba(217,119,6,0.35);  background: rgba(254,243,199,0.5); }
.wl-chip-warn .wl-chip-n    { color: var(--warning); }
.wl-chip-success { border-color: rgba(16,185,129,0.35); background: rgba(209,250,229,0.5); }
.wl-chip-success .wl-chip-n { color: var(--success); }

/* User chip (used in by-stage view): avatar + name + count. Same tap target
   as regular chips, just with a picture at the front. */
.wl-chip-user {
    padding-left: 6px;
    padding-right: 10px;
}
.wl-chip-user .wl-chip-avatar {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
}
.wl-chip-user .wl-chip-avatar .avatar-xs { border: none; }
.wl-chip-user .wl-chip-lbl {
    max-width: 120px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.wl-chip-user[hidden] { display: none; }

/* View toggle: segmented buttons, sits in the section header. */
.workload-view-toggle {
    display: inline-flex;
    background: var(--gray-100);
    border-radius: var(--r-full);
    padding: 3px;
    gap: 2px;
    margin-right: 8px;
}
.workload-view-toggle .chip {
    padding: 6px 14px;
    border-radius: var(--r-full);
    border: none;
    background: transparent;
    color: var(--text-muted);
    font-size: 12px;
    font-weight: 500;
    min-height: 30px;
    cursor: pointer;
}
.workload-view-toggle .chip:hover { color: var(--text); }
.workload-view-toggle .chip.chip-on {
    background: var(--surface);
    color: var(--brand);
    box-shadow: 0 1px 3px rgba(0,0,0,.08);
}
@media (max-width: 640px) {
    .workload-view-toggle { margin: 0 0 6px; }
    .workload-view-toggle .chip { min-height: 36px; padding: 8px 14px; font-size: 13px; }
}

/* Popover shown on chip hover/tap */
.wl-pop {
    position: absolute;
    z-index: 1000;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r);
    box-shadow: 0 10px 32px rgba(0,0,0,.14);
    min-width: 260px;
    max-width: 360px;
    max-height: 320px;
    display: flex;
    flex-direction: column;
    overflow: hidden;
    font-size: 13px;
}
.wl-pop[hidden] { display: none; }
.wl-pop-head {
    padding: 10px 12px;
    border-bottom: 1px solid var(--border);
    font-weight: 600;
    color: var(--text);
    display: flex;
    align-items: center;
    gap: 6px;
}
.wl-pop-count {
    background: var(--gray-100);
    color: var(--text-muted);
    padding: 1px 8px;
    border-radius: var(--r-full);
    font-size: 11px;
    font-weight: 500;
    font-variant-numeric: tabular-nums;
    margin-left: auto;
}
.wl-pop-list { overflow-y: auto; }
.wl-pop-item {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 8px 12px;
    border-bottom: 1px solid var(--gray-50);
    text-decoration: none;
    color: var(--text);
    transition: background .1s;
}
.wl-pop-item:last-child { border-bottom: none; }
.wl-pop-item:hover { background: var(--brand-50); }
.wl-pop-title {
    flex: 1;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    font-size: 13px;
}
.wl-pop-job {
    font-family: 'SF Mono', Menlo, Consolas, monospace;
    font-size: 10px;
    color: var(--text-muted);
    background: var(--gray-100);
    padding: 2px 6px;
    border-radius: 4px;
    letter-spacing: 0.02em;
}
.wl-pop-more {
    padding: 8px 12px;
    color: var(--text-muted);
    font-size: 11px;
    text-align: center;
    border-top: 1px solid var(--gray-50);
}

/* Mobile tune: bigger avatar + hero total on narrow screens. */
@media (max-width: 640px) {
    .wl-grid { grid-template-columns: 1fr; }
    .wl-card { padding: 14px; }
    .wl-card-head .avatar-md { width: 44px; height: 44px; font-size: 16px; }
    .wl-card-name { font-size: 15px; }
    .wl-card-total { font-size: 26px; }
    .wl-chip { min-height: 34px; padding: 7px 12px; }
    .wl-pop { min-width: min(90vw, 320px); max-width: 92vw; }
}

/* Stacked bar: each segment is a flex child; segment width = flex value. */
.wl-bar {
    display: flex;
    height: 10px;
    background: var(--gray-100);
    border-radius: var(--r-full);
    overflow: hidden;
    min-width: 60px;
    max-width: 320px;
    margin-bottom: 6px;
}
.wl-seg {
    display: block;
    height: 100%;
    min-width: 3px;
}
/* One colour per bucket. Sales = navy (KMZ brand), Production = amber (hot),
   Delivery = teal (in-transit), Aftercare = purple (post-sale). Colour-blind
   safe: hue + brightness differ across all four. */
.wl-seg-sales { background: #1e3a8a; }
.wl-seg-prod  { background: #f59e0b; }
.wl-seg-deliv { background: #14b8a6; }
.wl-seg-after { background: #8b5cf6; }

.wl-legend {
    display: flex;
    flex-wrap: wrap;
    gap: 4px 12px;
    font-size: 11px;
    color: var(--text-muted);
}
.wl-legend-item {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    white-space: nowrap;
}
.wl-legend-item strong { color: var(--text); font-variant-numeric: tabular-nums; }
.wl-legend-dot {
    width: 8px; height: 8px;
    border-radius: 50%;
    display: inline-block;
}
/* On narrow screens the workload table drops to card-per-row (via the shared
   .data-table mobile rule), and we want the bar to grow to fill. */
@media (max-width: 640px) {
    .wl-bar { max-width: none; }
    .wl-legend { gap: 4px 10px; }
}

/* Modal close */
.modal-close {
    background: none;
    border: none;
    padding: 6px;
    cursor: pointer;
    color: var(--text-muted);
    border-radius: var(--r-sm);
}
.modal-close:hover { background: var(--gray-100); color: var(--text); }

/* =========================================================
   MOBILE-FIRST OVERRIDES (workers use mobile primarily)
   ========================================================= */

/* ===== Layout image (in card modal) ===== */
.layout-wrap {
    border: 2px dashed var(--border-strong);
    border-radius: var(--r-lg);
    background: var(--gray-50);
    overflow: hidden;
    transition: border-color var(--dur);
}
.layout-wrap.has-image {
    border-style: solid;
    border-color: var(--border);
    padding: 0;
    background: var(--gray-100);
}
.layout-wrap:hover:not(.has-image) { border-color: var(--brand-light); background: var(--brand-50); }
.layout-wrap img {
    display: block;
    width: 100%;
    max-height: 460px;
    object-fit: contain;
    cursor: zoom-in;
    background: #fff;
}
.layout-upload {
    display: flex;
    align-items: center;
    gap: 14px;
    padding: 28px 20px;
    cursor: pointer;
    color: var(--text-muted);
    min-height: 100px;
}
.layout-upload svg { flex-shrink: 0; color: var(--brand); width: 44px !important; height: 44px !important; }
.layout-upload input { display: none; }
.layout-upload strong { color: var(--text); font-size: 15px; }
.layout-replace {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 8px 14px;
    margin-top: 10px;
    border: 1px solid var(--border);
    border-radius: var(--r);
    cursor: pointer;
    font-size: 13px;
    color: var(--text-2);
    background: var(--surface);
    transition: all var(--dur);
}
.layout-replace:hover { border-color: var(--brand-light); color: var(--brand); background: var(--brand-50); }
.layout-replace input { display: none; }

/* Lightbox */
.lightbox {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.92);
    z-index: 200;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 20px;
    animation: fadeIn 200ms var(--ease-out);
    overflow: hidden;
}
.lightbox[hidden] { display: none; }
/* Zoom stage sits between the overlay + the image. The image gets a CSS
   transform on double-tap / pinch / wheel; the stage stays fixed so the
   overlay backdrop still catches taps to close. */
.lightbox-stage {
    max-width: 100%;
    max-height: 90vh;
    display: flex;
    align-items: center;
    justify-content: center;
    /* touch-action:none lets pinch/pan events reach our JS without the
       browser trying to gesture-zoom the whole page underneath. */
    touch-action: none;
    -webkit-user-select: none;
    user-select: none;
}
.lightbox img {
    max-width: 100%;
    max-height: 90vh;
    object-fit: contain;
    border-radius: var(--r);
    box-shadow: 0 20px 60px rgba(0,0,0,.5);
    /* Zoom origin at center so translate math works from the middle. */
    transform-origin: center center;
    transition: transform 140ms ease-out;
    will-change: transform;
    cursor: zoom-in;
    -webkit-user-drag: none;
}
.lightbox img:active { transition: none; cursor: grabbing; }
.lightbox-close {
    position: absolute;
    top: calc(16px + env(safe-area-inset-top));
    right: 16px;
    width: 44px;
    height: 44px;
    border-radius: 50%;
    background: rgba(255,255,255,.15);
    color: #fff;
    border: none;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    backdrop-filter: blur(8px);
    transition: background var(--dur);
    z-index: 2;
}
.lightbox-close:hover { background: rgba(255,255,255,.25); }
.lightbox-caption {
    position: absolute;
    left: 0; right: 0;
    bottom: calc(20px + env(safe-area-inset-bottom));
    text-align: center;
    color: rgba(255,255,255,.85);
    font-size: 14px;
    padding: 0 60px;
    pointer-events: none;
}
/* Tiny grey hint at the very top-left explaining how to zoom. Fades on
   any interaction so it doesn't linger. */
.lightbox-hint {
    position: absolute;
    top: calc(16px + env(safe-area-inset-top));
    left: 16px;
    right: 72px;
    color: rgba(255,255,255,.55);
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    pointer-events: none;
    max-width: 340px;
}
@media (max-width: 640px) { .lightbox-hint { font-size: 10px; } }

/* --- PDF page viewer chrome -------------------------------------------------
   Shown only in PDF mode (JS adds .lightbox-doc). Multi-page documents are
   paged here rather than handed to the browser, because a target="_blank"
   hand-off loses the SameSite=Strict session cookie in an installed PWA and
   lands the user on the sign-in page. See openPdfViewer() in _lightbox.php. */
.lightbox-pager {
    position: absolute;
    left: 50%;
    transform: translateX(-50%);
    bottom: calc(16px + env(safe-area-inset-bottom));
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 6px 8px;
    border-radius: 999px;
    background: rgba(20, 20, 22, .92);
    border: 1px solid rgba(255, 255, 255, .14);
    box-shadow: 0 8px 24px rgba(0, 0, 0, .5);
    max-width: calc(100vw - 32px);
}
.lightbox-pager[hidden] { display: none; }
.lightbox-pagebtn {
    width: 40px;
    height: 40px;          /* stays a comfortable tap target on a phone */
    flex: 0 0 auto;
    border: none;
    border-radius: 50%;
    background: rgba(255, 255, 255, .12);
    color: #fff;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
}
.lightbox-pagebtn:hover:not(:disabled) { background: rgba(255, 255, 255, .24); }
.lightbox-pagebtn:disabled { opacity: .3; cursor: default; }
/* One chevron glyph serves both buttons — flip it for "next". */
#lightbox-next svg { transform: rotate(180deg); }
.lightbox-pagenum {
    color: #fff;
    font-size: 13px;
    font-weight: 600;
    white-space: nowrap;
    padding: 0 4px;
}
.lightbox-orig {
    color: #93c5fd;
    font-size: 12px;
    font-weight: 600;
    text-decoration: none;
    white-space: nowrap;
    padding: 0 10px 0 6px;
    border-left: 1px solid rgba(255, 255, 255, .16);
}
.lightbox-orig:hover { color: #dbeafe; text-decoration: underline; }
/* Lift the caption clear of the pager so they can't overlap. */
.lightbox-doc .lightbox-caption { bottom: calc(74px + env(safe-area-inset-bottom)); }
@media (max-width: 640px) {
    .lightbox-pagenum { font-size: 12px; }
    .lightbox-orig    { font-size: 11px; padding: 0 8px 0 4px; }
}
/* Freeze the page underneath so scrolling on mobile doesn't pan the
   card that was showing behind the overlay. */
body.lightbox-open { overflow: hidden; touch-action: none; }

/* Bottom nav — visible on mobile only */
.bottom-nav { display: none; }

@media (max-width: 900px) {
    body {
        padding-bottom: calc(64px + env(safe-area-inset-bottom));
    }

    /* Top bar: compact, keep only logo + bell + avatar */
    .topbar-inner {
        padding: 8px 12px;
        gap: 8px;
    }
    .topbar-nav { display: none; }
    .brand-name { display: none; }
    .brand-logo-img { height: 44px; max-width: 150px; }
    .user-name { display: none; }
    .install-btn { display: none; }

    /* Notification dropdown becomes a bottom sheet */
    .notif-panel {
        position: fixed;
        top: auto !important;
        bottom: calc(64px + env(safe-area-inset-bottom));
        left: 0;
        right: 0;
        width: 100%;
        max-height: 70vh;
        border-radius: 20px 20px 0 0;
        transform: translateY(100%);
        transition: transform 260ms var(--ease-out);
        display: block;
        box-shadow: 0 -8px 32px rgba(16,24,40,.15);
    }
    .notif-panel[data-open="1"] { transform: translateY(0); }
    .notif-panel::before {
        content: '';
        display: block;
        width: 40px;
        height: 4px;
        background: var(--gray-300);
        border-radius: 4px;
        margin: 8px auto 0;
    }

    /* Bottom tab bar */
    .bottom-nav {
        display: flex;
        position: fixed;
        bottom: 0;
        left: 0;
        right: 0;
        background: var(--surface);
        border-top: 1px solid var(--border);
        z-index: 50;
        padding: 6px 4px calc(6px + env(safe-area-inset-bottom));
        box-shadow: 0 -2px 10px rgba(16,24,40,.04);
    }
    .bottom-nav a {
        flex: 1;
        display: flex;
        flex-direction: column;
        align-items: center;
        gap: 2px;
        padding: 8px 4px;
        min-height: 48px;
        color: var(--text-muted);
        text-decoration: none;
        font-size: 10px;
        font-weight: 600;
        text-transform: uppercase;
        letter-spacing: 0.04em;
        border-radius: var(--r-sm);
        transition: color var(--dur), background var(--dur);
    }
    .bottom-nav a.active { color: var(--brand); }
    .bottom-nav a.active svg { color: var(--brand); }
    .bottom-nav a:active { background: var(--gray-100); }
    .bottom-nav a svg { color: var(--text-muted); }

    /* Container / typography */
    .container { padding: 16px 12px; }
    .page-head { flex-direction: column; align-items: stretch; gap: 8px; }
    .page-head h1 { font-size: 22px; }
    .page-head .btn { align-self: flex-start; }

    /* Auth pages behave like a native mobile app on phones:
         - Fixed to the full dynamic viewport (dvh follows the browser
           chrome + on-screen keyboard so no "URL bar shrink" jump).
         - Body pinned (position: fixed) + overscroll-behavior: none →
           kills pull-to-refresh bounce and elastic edge scroll on iOS.
         - The card scrolls INSIDE the wrap only if it exceeds the
           viewport height (small phones + tall keyboard case), never
           the whole page. `overscroll-behavior: contain` keeps that
           inner scroll from bubbling into a page-level bounce.
         - env(safe-area-inset-*) respects notch / home-bar. */
    html:has(.auth-page), body.auth-page {
        height: 100dvh;
        overflow: hidden;
        overscroll-behavior: none;
        position: fixed;
        inset: 0;
        width: 100%;
        touch-action: manipulation;
    }
    .auth-page { display: block; height: 100dvh; min-height: 0; overflow: hidden; }
    .auth-visual { display: none; }
    .auth-form-wrap {
        height: 100dvh;
        min-height: 0;
        padding: max(24px, env(safe-area-inset-top)) 20px max(24px, env(safe-area-inset-bottom));
        overflow-y: auto;
        overscroll-behavior: contain;
        -webkit-overflow-scrolling: touch;
    }
    .auth-card .brand-logo-lg { max-width: 100%; margin: 0 auto 20px; }

    /* Boards dashboard */
    .board-grid { grid-template-columns: 1fr; gap: 12px; }
    .board-tile { min-height: auto; }
    .board-tile-body { padding: 16px; }
    .board-tile-meta { margin-top: 12px; }

    /* Board bar becomes sticky compact header */
    .board-bar { top: 49px; }
    .board-bar-inner {
        flex-direction: column;
        align-items: stretch;
        padding: 10px 12px;
        gap: 8px;
        border-left-width: 3px;
    }
    .board-title { gap: 8px; }
    .board-title h1 { font-size: 16px; }
    .board-title .muted { display: none; }
    .board-actions { justify-content: flex-start; overflow-x: auto; }
    .board-actions .btn { padding: 6px 10px; min-height: 32px; font-size: 12px; }
    .board-actions .btn span { display: inline; }

    /* MOBILE BOARD: swipeable single-column view with tab strip */
    .board-canvas {
        padding: 0;
        height: calc(100dvh - 49px - 96px - 64px - env(safe-area-inset-bottom));
        scroll-snap-type: x mandatory;
        -webkit-overflow-scrolling: touch;
        overflow-x: auto;
        overflow-y: hidden;
    }
    .board-canvas::-webkit-scrollbar { display: none; }
    .columns {
        gap: 0;
        padding: 0;
        height: 100%;
    }
    .column {
        width: 100vw;
        max-width: 100vw;
        flex-shrink: 0;
        scroll-snap-align: start;
        border-radius: 0;
        border-left: none;
        border-right: 1px solid var(--border);
        background: var(--gray-50);
        max-height: none;
        height: 100%;
        padding-bottom: 8px;
    }
    .column-head { padding: 14px 16px 10px; cursor: default; }
    .col-name { font-size: 15px; padding: 0; }
    .col-name:hover { background: transparent; }
    .cards { padding: 0 12px 12px; }
    .add-card-btn {
        margin: 8px 12px;
        padding: 12px 14px;
        border: 1px dashed var(--border-strong);
        border-radius: var(--r);
        background: rgba(255,255,255,.5);
    }
    .column-add { display: none; }

    /* Mobile-only tweaks for the shared col-tabs strip */
    .col-tabs {
        position: sticky;
        top: calc(49px + env(safe-area-inset-top));
    }

    /* Card touch tweaks */
    .card {
        padding: 14px;
        margin-bottom: 10px;
    }
    .card-title { font-size: 15px; margin-bottom: 10px; }
    .card-meta { font-size: 12px; gap: 10px; }
    .avatar-sm { width: 26px; height: 26px; font-size: 11px; }

    /* Modals: full-screen bottom sheet */
    .modal { padding: 0; align-items: flex-end; }
    .modal-body {
        max-width: 100%;
        max-height: 96dvh;
        width: 100%;
        border-radius: 20px 20px 0 0;
        padding: 20px 16px calc(20px + env(safe-area-inset-bottom));
        animation: slideUp 260ms var(--ease-out);
    }
    .modal-body::before {
        content: '';
        display: block;
        width: 40px;
        height: 4px;
        background: var(--gray-300);
        border-radius: 4px;
        margin: -8px auto 12px;
    }
    @keyframes slideUp {
        from { transform: translateY(100%); opacity: 0.5; }
        to   { transform: translateY(0);    opacity: 1; }
    }
    .modal-actions .btn { flex: 1; }
    .modal-actions .btn:first-child { flex: 0 0 auto; }

    /* Card modal on mobile — full-screen, no grid split */
    .modal-lg .modal-body {
        max-width: 100%;
        max-height: 100dvh;
        height: 100dvh;
        border-radius: 0;
        padding: 0;
        display: flex;
        flex-direction: column;
    }
    .modal-lg .modal-body::before { display: none; }
    .card-modal-head {
        padding: 12px 12px 12px 16px;
        padding-top: calc(12px + env(safe-area-inset-top));
        gap: 8px;
        position: sticky;
        top: 0;
        background: var(--surface);
        z-index: 2;
    }
    .card-title-input { font-size: 17px; }
    .card-modal-grid {
        grid-template-columns: 1fr;
        padding: 16px;
        padding-bottom: calc(24px + env(safe-area-inset-bottom));
        gap: 20px;
        overflow-y: auto;
        flex: 1;
    }
    .card-modal-side .side-block {
        padding-bottom: 16px;
        margin-bottom: 16px;
    }
    .chip-list { flex-direction: row; flex-wrap: wrap; gap: 6px; }
    .chip { min-height: 40px; padding: 8px 12px; }

    /* Forms bigger on mobile */
    input, textarea, select {
        font-size: 16px; /* prevents iOS zoom */
        min-height: 44px;
        padding: 10px 14px;
    }
    label { margin-bottom: 16px; }
    textarea { min-height: 88px; }

    /* Buttons larger touch — 44 px minimum from Apple HIG.
       btn-sm still exists but on mobile it inflates to the 44 px target so
       phones aren't stuck with fiddly little Cancel/Delete pills. */
    .btn { min-height: 44px; padding: 10px 16px; }
    .btn-sm { min-height: 44px; padding: 10px 14px; }
    .btn-block { width: 100%; }
    .btn-lg { min-height: 52px; font-size: 16px; }

    .icon-btn { width: 44px; height: 44px; }
    /* Card quick-menu ("..." kebab) — larger on touch */
    .card-quick-menu { width: 40px; height: 40px; }
    /* Design-thumbnail delete X + edit — always visible + reachable on touch */
    .design-image-del { width: 32px; height: 32px; }
    .design-image-edit { width: 32px; height: 32px; right: 48px; }
    /* Stage breadcrumb pills */
    .crumb { min-width: 36px; height: 36px; font-size: 13px; }

    /* Reports table becomes cards */
    .data-table { border: none; background: transparent; }
    .data-table thead { display: none; }
    .data-table tbody, .data-table tr, .data-table td { display: block; width: 100%; }
    /* MUST come after the display:block above. Filtering across the app hides
       rows with the [hidden] attribute, and the UA rule that implements it
       ([hidden] { display: none }) loses to ANY author `display` on the same
       element — so on a phone every table filter silently stopped hiding
       anything and users saw the unfiltered list. Same trap already patched
       once for .wl-chip-user[hidden]. */
    .data-table tr[hidden], .data-table td[hidden], .data-table tbody[hidden] { display: none; }
    .data-table tr {
        background: var(--surface);
        border: 1px solid var(--border);
        border-radius: var(--r-lg);
        margin-bottom: 10px;
        padding: 12px 14px;
    }
    .data-table td { padding: 6px 0; border-bottom: none; text-align: left !important; }
    .data-table td:first-child { font-size: 15px; margin-bottom: 4px; }
    .data-table td[style*="text-align:right"] { display: inline-block; width: auto; margin-right: 12px; }
    .data-table td:last-child { margin-top: 6px; }

    /* ── Workload Table mode stays a REAL table on mobile ───────────────────
       The stacking above works for a list of records, but the By person and By
       stage grids are matrices: a row is a person, every other column is a
       stage count. Stacking them hides the <thead>, so the counts collapsed to
       a meaningless "1  1  —  —  2" with no way to tell which stage was which.
       Table mode should show rows, so these opt back out: real table display,
       header visible, horizontal scroll (the .wl-table-wrap already has
       overflow-x:auto), and the name column pinned left so you always know
       whose row you're reading while scrolling sideways. */
    .wl-person-table, .wl-stage-table {
        border: 1px solid var(--border);
        background: var(--surface);
        /* .data-table sets overflow:hidden (to clip its own border-radius).
           That makes the TABLE the nearest scroll container, so the sticky
           first column below stuck to the table instead of to .wl-table-wrap
           and scrolled straight off screen. The wrap does the clipping now. */
        overflow: visible;
    }
    .wl-person-table thead, .wl-stage-table thead { display: table-header-group; }
    .wl-person-table tbody, .wl-stage-table tbody { display: table-row-group; }
    .wl-person-table tr, .wl-stage-table tr {
        display: table-row;
        width: auto;
        background: none;
        border: none;
        border-radius: 0;
        margin-bottom: 0;
        padding: 0;
    }
    .wl-person-table th, .wl-stage-table th,
    .wl-person-table td, .wl-stage-table td {
        display: table-cell;
        width: auto;
        padding: 10px 12px;
        margin: 0;
        font-size: 13px;
        border-bottom: 1px solid var(--border);
        text-align: left;
    }
    /* The stacking block above has two selectors more specific than a plain
       `.wl-person-table td`, so they need answering by name or the row silently
       stays broken:
         .data-table td[style*="text-align:right"] -> display:inline-block
             The per-stage count cells all carry that inline style, so they were
             still laying out as wrapping inline-blocks inside an otherwise
             real table row — 7 cells landing on 5 different lines.
         .data-table td { text-align: left !important }
             ...which then also killed their right alignment, so numbers no
             longer lined up under their column heading. Hence !important here:
             it's the only way to outrank an !important, and the numeric columns
             have to stay right-aligned to be readable as a matrix.
         .data-table td:last-child { margin-top: 6px } -> pushes the last cell
             down out of its row. */
    .wl-person-table td[style*="text-align:right"], .wl-person-table th[style*="text-align:right"],
    .wl-stage-table  td[style*="text-align:right"], .wl-stage-table  th[style*="text-align:right"] {
        display: table-cell;
        width: auto;
        margin: 0;
        text-align: right !important;
    }
    .wl-person-table td:last-child, .wl-stage-table td:last-child { margin-top: 0; }

    /* Drop Designation on a phone. Name + Designation alone took 335 of the
       390 available px, so the stage counts — the entire point of this view —
       started off screen and needed a long sideways drag to reach. The
       designation is still there on any wider screen. */
    .wl-person-table th:nth-child(2), .wl-person-table td:nth-child(2) { display: none; }
    /* Tighter than the 12px above: this column is pinned, so every px it takes
       is a px permanently unavailable to the counts. */
    .wl-person-table th:first-child, .wl-person-table td:first-child { padding: 10px 8px; }

    /* Person view only. Its cells are a name, a designation and single-digit
       counts — wrapping them just makes the row tall for no gain, and letting
       the table exceed the viewport is the point (that's what the sticky first
       column and the wrap's overflow-x are for). The stage view is NOT included:
       its Contributors cell holds a chip per person and has to wrap. */
    .wl-person-table th, .wl-person-table td { white-space: nowrap; }
    /* Third column of the stage view = Contributors. Chips wrap inside it. */
    .wl-stage-table td:nth-child(3) { white-space: normal; }
    /* Re-assert after the display overrides above, same reason as the
       .data-table tr[hidden] rule: an author `display` beats [hidden]. */
    .wl-person-table tr[hidden], .wl-stage-table tr[hidden] { display: none; }
    /* Pinned name/stage column. Needs its own background or rows scroll
       underneath it, and a z-index above the sideways-moving cells. */
    .wl-person-table tbody td:first-child,
    .wl-stage-table  tbody td:first-child,
    .wl-person-table thead th:first-child,
    .wl-stage-table  thead th:first-child {
        position: sticky;
        left: 0;
        z-index: 1;
        background: var(--surface);
        box-shadow: 1px 0 0 var(--border);
        font-size: 13px;
        margin-bottom: 0;
    }
    .wl-person-table thead th:first-child, .wl-stage-table thead th:first-child {
        z-index: 2;
        background: var(--gray-25);
    }
    /* Tell the user sideways scrolling exists — otherwise the pinned column
       makes it look like the table simply ends. */
    .wl-view-person .wl-table-wrap, .wl-view-stage .wl-table-wrap {
        -webkit-overflow-scrolling: touch;
        border-radius: var(--r-lg);
    }

    .progress { max-width: 100%; }

    /* Stat cards responsive */
    .stat-grid { grid-template-columns: repeat(2, 1fr); gap: 10px; }
    .stat-card { padding: 14px; }
    .stat-card .value { font-size: 22px; }

    /* Members list mobile */
    .member-row { padding: 10px; gap: 10px; }

    /* Attach uploader — big tap target */
    .attach-upload { padding: 14px; justify-content: center; }
    .attach-item { padding: 10px; }

}

/* =========================================================
   Global card + action-sheet styles
   (must live outside the mobile media query — the "Move card to"
   sheet, quick-menu, and status pills are used on any width)
   ========================================================= */

.card { position: relative; }

/* ===== Admin panel ===== */
/* Wrapper hosts the strip + chevron arrows + edge-fade masks. Mirrors
   the stage-tabs-wrap pattern used on the workflow home page. */
.admin-tabs-wrap {
    position: relative;
    margin: 8px 0 20px;
    border-bottom: 1px solid var(--border);
}
.admin-tabs-wrap::before,
.admin-tabs-wrap::after {
    content: "";
    position: absolute;
    top: 0; bottom: 1px;  /* keep the 1px baseline border visible under fade */
    width: 44px;
    pointer-events: none;
    opacity: 0;
    transition: opacity var(--dur);
    z-index: 1;
}
.admin-tabs-wrap::before {
    left: 0;
    background: linear-gradient(to right, var(--surface) 20%, rgba(255,255,255,0));
}
.admin-tabs-wrap::after {
    right: 0;
    background: linear-gradient(to left,  var(--surface) 20%, rgba(255,255,255,0));
}
.admin-tabs-wrap.has-left::before  { opacity: 1; }
.admin-tabs-wrap.has-right::after  { opacity: 1; }

.admin-tabs-arrow {
    position: absolute;
    top: 50%;
    transform: translateY(calc(-50% - 2px));
    width: 30px; height: 30px;
    display: inline-flex;
    align-items: center; justify-content: center;
    padding: 0;
    background: var(--surface);
    color: var(--text-muted);
    border: 1px solid var(--border);
    border-radius: 50%;
    box-shadow: 0 2px 6px rgba(15, 23, 42, 0.08);
    cursor: pointer;
    opacity: 0;
    visibility: hidden;
    transition: opacity var(--dur), transform var(--dur), background var(--dur), color var(--dur), box-shadow var(--dur);
    z-index: 2;
}
.admin-tabs-arrow:hover {
    color: var(--brand);
    box-shadow: 0 3px 10px rgba(15, 23, 42, 0.14);
    transform: translateY(calc(-50% - 2px)) scale(1.06);
}
.admin-tabs-arrow:active {
    transform: translateY(calc(-50% - 2px)) scale(0.96);
}
.admin-tabs-arrow-left  { left: 4px; }
.admin-tabs-arrow-right { right: 4px; }
.admin-tabs-wrap.has-left  .admin-tabs-arrow-left,
.admin-tabs-wrap.has-right .admin-tabs-arrow-right {
    opacity: 1;
    visibility: visible;
}

.admin-tabs {
    display: flex;
    gap: 4px;
    overflow-x: auto;
    scrollbar-width: none;
}
.admin-tabs::-webkit-scrollbar { display: none; }

/* Touch devices scroll natively — hide chevron buttons; fade edges alone
   are enough of a hint that there's more to the right/left. */
@media (max-width: 720px) {
    .admin-tabs-arrow { display: none; }
    .admin-tabs-wrap::before,
    .admin-tabs-wrap::after { width: 32px; }
}
.admin-tab {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 10px 16px;
    color: var(--text-muted);
    font-size: 14px;
    font-weight: 600;
    text-decoration: none;
    border-bottom: 2px solid transparent;
    margin-bottom: -1px;
    white-space: nowrap;
    transition: color var(--dur);
}
.admin-tab:hover { color: var(--text); text-decoration: none; }
.admin-tab.active {
    color: var(--brand);
    border-bottom-color: var(--brand);
}

.admin-toolbar {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 12px;
    margin-bottom: 16px;
    flex-wrap: wrap;
}

/* Stage SLA config table (Admin > Stage SLAs) */
.sla-board-wrap { margin-bottom: 28px; }
.sla-board-title {
    margin: 0 0 8px;
    font-size: 15px;
    padding: 6px 0 6px 12px;
    border-left: 4px solid var(--brand);
}
.sla-table td, .sla-table th { vertical-align: middle; }
.sla-total td { background: var(--gray-50); font-size: 13px; }
.sla-input {
    padding: 6px 8px;
    border: 1px solid var(--border);
    border-radius: var(--r);
    font-size: 14px;
    background: var(--surface);
    font-variant-numeric: tabular-nums;
}
.sla-input:focus { border-color: var(--brand); outline: none; box-shadow: 0 0 0 3px var(--brand-50); }

/* Per-stage due-date chip on stage summary sidebar */
.stage-due-chip {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 2px 8px;
    border-radius: var(--r-full);
    font-size: 12px;
    font-weight: 600;
    letter-spacing: 0.02em;
    font-variant-numeric: tabular-nums;
}
.stage-due-chip.stage-due-ok    { background: #ecfdf5; color: #065f46; border: 1px solid #a7f3d0; }
.stage-due-chip.stage-due-warn  { background: #fef3c7; color: #92400e; border: 1px solid #fcd34d; }
.stage-due-chip.stage-due-late  { background: #fee2e2; color: #991b1b; border: 1px solid #fecaca; }

/* Compact variant of the stage-due chip, used inline in the job row meta.
   Uses the same colour semantics but sits neatly in the meta strip. */
.stage-due-mini {
    display: inline-flex; align-items: center; gap: 3px;
    padding: 2px 7px;
    border-radius: var(--r-full);
    font-size: 11px;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    line-height: 1.4;
}
.stage-due-mini-ok   { background: #ecfdf5; color: #065f46; border: 1px solid #a7f3d0; }
.stage-due-mini-warn { background: #fef3c7; color: #92400e; border: 1px solid #fcd34d; }
.stage-due-mini-late { background: #fee2e2; color: #991b1b; border: 1px solid #fecaca; }

.admin-search {
    max-width: 340px;
    flex: 1;
}
.admin-select {
    max-width: 260px;
    flex: 0 0 auto;
}
.admin-table-wrap {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    overflow: hidden;
}
.admin-table-wrap .data-table {
    border: none;
    border-radius: 0;
}
.admin-users-table tr.row-archived { opacity: 0.55; }

.mono { font-family: 'SF Mono', Menlo, Consolas, monospace; font-size: 13px; font-variant-numeric: tabular-nums; }

/* Admin category cards */
.admin-cats {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
    gap: 12px;
}
.cat-card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    display: flex;
    flex-direction: column;
    overflow: hidden;
    transition: box-shadow var(--dur);
}
.cat-card:hover { box-shadow: var(--shadow); }
.cat-strip { height: 6px; }
.cat-body { padding: 16px; flex: 1; }
.cat-body h3 { margin: 0 0 4px; font-size: 15px; }
.cat-actions {
    display: flex;
    gap: 6px;
    padding: 12px 16px;
    border-top: 1px solid var(--border);
    background: var(--gray-25);
}
.cat-actions .btn { flex: 1; }

/* PIN slice picker */
.pin-picker {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin-bottom: 10px;
    padding: 10px;
    background: var(--gray-50);
    border: 1px solid var(--border);
    border-radius: var(--r);
}
.pin-slice {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 2px;
    padding: 8px 12px;
    border: 1px solid var(--border);
    border-radius: var(--r-sm);
    background: var(--surface);
    cursor: pointer;
    font-family: inherit;
    font-size: 15px;
    min-height: 44px;
    transition: all var(--dur);
}
.pin-slice:hover { border-color: var(--brand-light); background: var(--brand-50); }
.pin-slice.on {
    background: var(--brand);
    color: #fff;
    border-color: var(--brand);
}
.pin-slice.on .muted { color: rgba(255,255,255,0.75); }

/* PIN reveal modal */
.pin-reveal-code {
    font-family: 'SF Mono', Menlo, Consolas, monospace;
    font-size: 48px;
    font-weight: 700;
    letter-spacing: 0.3em;
    padding: 24px 0 8px;
    color: var(--brand);
}

/* Row-form layout helper */
form .row {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 12px;
}
@media (max-width: 640px) {
    form .row { grid-template-columns: 1fr; gap: 0; }
    .admin-toolbar { flex-direction: column; align-items: stretch; }
    .admin-search, .admin-select { max-width: 100%; }
    .pin-reveal-code { font-size: 36px; letter-spacing: 0.2em; }
}

.btn-danger-outline {
    color: var(--danger);
    border-color: var(--danger-light);
    background: transparent;
}
.btn-danger-outline:hover { background: var(--danger-light); }

/* ===== Column tab strip (used on every screen size) ===== */
.col-tabs {
    display: flex;
    overflow-x: auto;
    background: var(--surface);
    border-bottom: 1px solid var(--border);
    padding: 6px 20px;
    gap: 4px;
    scrollbar-width: none;
    z-index: 10;
    max-width: 1600px;
    margin: 0 auto;
    box-shadow: var(--shadow-xs);
}
.col-tabs::-webkit-scrollbar { display: none; }
.col-tab {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 10px 16px;
    min-height: 40px;
    border-radius: var(--r);
    background: transparent;
    border: none;
    color: var(--text-muted);
    font-family: inherit;
    font-size: 13px;
    font-weight: 600;
    cursor: pointer;
    white-space: nowrap;
    transition: all var(--dur) var(--ease-out);
}
.col-tab:hover  { background: var(--gray-100); color: var(--text-2); }
.col-tab:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.col-tab .count {
    background: var(--gray-200);
    color: var(--gray-600);
    padding: 1px 8px;
    border-radius: var(--r-full);
    font-size: 11px;
    font-weight: 700;
    line-height: 1.6;
    min-width: 22px;
    text-align: center;
}
.col-tab.active {
    background: var(--brand-50);
    color: var(--brand);
}
.col-tab.active .count { background: var(--brand); color: #fff; }

.card-quick-menu {
    position: absolute;
    top: 8px;
    right: 8px;
    width: 32px;
    height: 32px;
    border-radius: 50%;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: rgba(255, 255, 255, 0.9);
    backdrop-filter: blur(4px);
    border: 1px solid var(--border);
    color: var(--text-muted);
    cursor: pointer;
    padding: 0;
    z-index: 2;
    opacity: 0;
    transition: opacity var(--dur) var(--ease-out), background var(--dur);
}
.card:hover .card-quick-menu,
.card:focus-within .card-quick-menu,
.card-quick-menu:focus,
.card-quick-menu:hover { opacity: 1; background: var(--surface); color: var(--text); }
/* Always visible on touch devices */
@media (hover: none) { .card-quick-menu { opacity: 1; } }

/* Bottom-sheet action list (Move card, Delete, etc.) — works at any width */
.action-sheet .modal-body { padding: 8px 8px calc(8px + env(safe-area-inset-bottom)); }
.action-sheet-title {
    text-align: center;
    padding: 8px 12px 12px;
    color: var(--text-muted);
    font-size: 12px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
}
.action-btn {
    display: flex;
    align-items: center;
    gap: 12px;
    width: 100%;
    padding: 14px 16px;
    min-height: 52px;
    background: var(--surface);
    border: none;
    border-bottom: 1px solid var(--border);
    font-size: 15px;
    font-family: inherit;
    color: var(--text);
    text-align: left;
    cursor: pointer;
    transition: background var(--dur);
}
.action-btn:first-of-type {
    border-top-left-radius: var(--r-md);
    border-top-right-radius: var(--r-md);
}
.action-btn:last-of-type {
    border-bottom-left-radius: var(--r-md);
    border-bottom-right-radius: var(--r-md);
    border-bottom: none;
}
.action-btn:hover  { background: var(--gray-50); }
.action-btn:active { background: var(--gray-100); }
.action-btn.danger { color: var(--danger); }
.action-btn.danger:hover { background: var(--danger-light); }
.action-sheet .modal-actions { padding: 8px 0 0; }

/* Extra small phones */
@media (max-width: 380px) {
    .stat-grid { grid-template-columns: 1fr; }
    .board-title h1 { font-size: 15px; }
    .brand-logo-img { height: 40px; max-width: 140px; }
}

/* Landscape phones: reduce vertical padding */
@media (max-width: 900px) and (orientation: landscape) and (max-height: 500px) {
    .board-canvas { height: calc(100dvh - 49px - 84px - 64px); }
    body { padding-bottom: 56px; }
}

/* Sidebar "Timeline" buttons — open the wide modal */
.side-timeline-btns {
    display: flex;
    flex-direction: column;
    gap: 8px;
}
.side-tl-btn {
    justify-content: flex-start;
    gap: 8px;
    font-weight: 500;
}

/* Sidebar "Notes" panel — read-only Description + Special notes.
   Label sits on top like other row-labels, body is body-weight text with
   preserved line breaks. Second block gets a hair-line divider so the two
   sections read as separate without a second card. */
/* Notes blocks — read-only Description + Special notes surfaced on the
   stage main area (see enquiries.php). Multiple blocks stack with a hairline
   divider between them. Same styling used in narrower contexts. */
.notes-block-list {
    display: flex;
    flex-direction: column;
    gap: 12px;
}
.notes-block + .notes-block {
    padding-top: 12px;
    border-top: 1px solid var(--border);
}
.notes-block-label {
    font-size: 11px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--text-muted);
    margin-bottom: 4px;
}
.notes-block-body {
    font-size: 14px;
    line-height: 1.55;
    color: var(--text);
    word-break: break-word;
    white-space: pre-wrap;
}

/* Wide timeline modal — Previous stages + Activity in tabs */
.timeline-modal { padding: 24px; }
.timeline-modal .timeline-modal-body {
    max-width: 1080px;
    width: 100%;
    max-height: 90vh;
    padding: 0;
    display: flex;
    flex-direction: column;
    overflow: hidden;
}
.timeline-modal-header {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 12px 16px;
    border-bottom: 1px solid var(--border);
    background: var(--gray-50);
    flex-shrink: 0;
}
.timeline-modal-tabs {
    display: flex;
    gap: 4px;
    flex: 1;
    overflow-x: auto;
}
.tl-tab {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 8px 14px;
    border-radius: var(--r);
    background: transparent;
    border: 1px solid transparent;
    color: var(--text-muted);
    font-weight: 600;
    font-size: 13px;
    cursor: pointer;
    white-space: nowrap;
    transition: background var(--dur), color var(--dur), border-color var(--dur);
}
.tl-tab:hover { background: var(--gray-100); color: var(--text); }
.tl-tab.is-active {
    background: var(--surface);
    color: var(--brand);
    border-color: var(--border);
}
.timeline-modal-close {
    flex-shrink: 0;
}
.timeline-modal-content {
    overflow-y: auto;
    padding: 20px 24px;
    flex: 1;
}
.tl-panel { display: none; }
.tl-panel.is-active { display: block; }
/* Inside the modal, unwrap the outer wrapper cards so we don't get card-in-card
   nesting and let history/activity fill full width */
.tl-panel .stage-card {
    background: transparent;
    border: none;
    padding: 0;
    box-shadow: none;
}
.tl-panel .stage-card > details > summary { display: none; }
/* Previous stages inside the timeline modal: previously rendered as a 2-3
   column grid ("tile by tile"), which cropped long content and made the
   whole thing feel cramped. Switched to a single vertical stack — each
   stage becomes its own full-width block with room to breathe, so all
   the detail (call log, quote revisions, BOM, remarks, edits) is readable
   without expanding tiles side-by-side. */
.tl-panel .hist-body {
    max-height: none;
    padding-top: 0;
    border-top: none;
    margin-top: 0;
    display: flex;
    flex-direction: column;
    gap: 16px;
}
.tl-panel .hist-stage {
    background: var(--surface);
    border: 1px solid var(--border);
    height: auto;
    /* Stages start OPEN in the modal (see the JS "Force any nested details
       open" hook), so let the inner content flow — no fixed height cap. */
}
.tl-panel .hist-stage > summary {
    background: var(--brand-50, #eff6ff);
    color: var(--brand);
    padding: 12px 18px;
    font-size: 15px;
    font-weight: 700;
    border-bottom: 1px solid var(--border);
    letter-spacing: 0.01em;
}
.tl-panel .hist-stage-body {
    padding: 16px 18px 18px;
    gap: 12px;
    font-size: 13px;
}
/* Give each row a tiny bit more vertical breathing room inside the modal.
   Label + value both get bumped a touch so lists / long remarks don't
   collide with adjacent rows. */
.tl-panel .hist-row { gap: 5px; }
.tl-panel .hist-label {
    font-size: 11px;
    color: var(--text-muted);
}
.tl-panel .hist-val { font-size: 13px; line-height: 1.5; }
/* BOM / dispatch checklist / call log tables inside a stage block —
   let them use the full modal width instead of being cropped by the
   old ~360px grid tile. */
.tl-panel .hist-bom { max-height: none; }
.tl-panel .hist-val ul.hist-list { padding-left: 20px; gap: 6px; }
/* Long text values (remarks, descriptions) — cap width at readable
   measure so a 1600px screen doesn't stretch a paragraph into a single
   ribbon of text. */
.tl-panel .hist-val { max-width: 100ch; }
/* Activity panel */
.tl-panel .activity-list-stage {
    max-height: none;
    padding: 0;
    margin: 0;
}
.tl-panel .activity-row-stage {
    padding: 10px 0;
    border-bottom: 1px dashed var(--border);
}
.tl-panel .activity-row-stage:last-child { border-bottom: none; }

@media (max-width: 640px) {
    .timeline-modal { padding: 0; }
    .timeline-modal .timeline-modal-body {
        max-height: 100vh;
        max-height: 100dvh;
        height: 100vh;
        height: 100dvh;
        border-radius: 0;
    }
    .timeline-modal-content { padding: 12px 14px; }
    .timeline-modal-header { padding: 10px 12px; }
}

/* Per-trade chat thread on production stages (MS Fabrication, Ply, PEP) */
.chat-thread {
    display: flex;
    flex-direction: column;
    gap: 10px;
    max-height: 340px;
    overflow-y: auto;
    padding: 10px 4px;
    border: 1px solid var(--border);
    border-radius: var(--r);
    background: var(--gray-50);
    margin-bottom: 12px;
}
.chat-empty {
    padding: 32px 16px 24px;
    text-align: center;
    color: var(--text-muted);
    font-size: 13px;
    background: var(--gray-50);
    border-radius: var(--r);
    border: 1px dashed var(--border);
    margin: 8px 0;
}
.chat-empty::before {
    content: "";
    display: block;
    width: 32px; height: 32px;
    margin: 0 auto 8px;
    background: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='32' height='32' viewBox='0 0 24 24' fill='none' stroke='%2394a3b8' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'><path d='M21 15a2 2 0 01-2 2H7l-4 4V5a2 2 0 012-2h14a2 2 0 012 2z'/></svg>") center/contain no-repeat;
}
.chat-msg {
    display: flex;
    gap: 8px;
    align-items: flex-start;
    max-width: 92%;
}
.chat-msg.chat-mine {
    flex-direction: row-reverse;
    margin-left: auto;
}
.chat-bubble {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 10px;
    padding: 8px 12px;
    line-height: 1.4;
    min-width: 0;
    flex: 1;
}
.chat-mine .chat-bubble {
    background: #ecfdf5;
    border-color: #a7f3d0;
    /* Pin dark text so dark-mode --text doesn't ghost over the pale-green bg. */
    color: #065f46;
}
.chat-mine .chat-bubble,
.chat-mine .chat-bubble .chat-body,
.chat-mine .chat-bubble .tiny,
.chat-mine .chat-bubble .muted { color: #065f46; }
.chat-meta {
    display: flex;
    flex-wrap: wrap;
    gap: 4px;
    align-items: baseline;
    font-size: 12px;
    margin-bottom: 3px;
}
.chat-body {
    font-size: 14px;
    color: var(--text);
    word-break: break-word;
}
.chat-form {
    display: flex;
    gap: 8px;
    align-items: flex-end;
}
.chat-form textarea {
    flex: 1;
    resize: vertical;
    min-height: 44px;
}
.chat-form button {
    align-self: stretch;
    white-space: nowrap;
    display: inline-flex;
    align-items: center;
    gap: 6px;
    min-height: 44px;
}
/* Mobile: full-width Send button below the textarea, no cramp. */
@media (max-width: 640px) {
    .chat-form { flex-direction: column; align-items: stretch; }
    .chat-form textarea { min-height: 64px; }
    .chat-form button { width: 100%; justify-content: center; min-height: 48px; font-size: 15px; }
}

/* Low/Out-of-stock summary panel on the Stock page */
.stock-alert-panel {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    padding: 16px 18px;
    margin-bottom: 20px;
    box-shadow: var(--shadow-sm);
}
.stock-alert-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    flex-wrap: wrap;
    margin-bottom: 12px;
}
.stock-alert-head h2 {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 16px;
    margin: 0 0 2px;
    color: var(--text);
}
.stock-alert-head p { margin: 0; }
.stock-alert-head .text-danger { color: var(--danger); }
.stock-alert-head .text-warn   { color: #b45309; }
.stock-alert-actions { display: flex; gap: 8px; }
.stock-alert-tablewrap { overflow-x: auto; }
.stock-alert-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 13px;
}
.stock-alert-table th,
.stock-alert-table td {
    text-align: left;
    padding: 8px 10px;
    border-bottom: 1px solid var(--border);
    vertical-align: middle;
}
.stock-alert-table th {
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--text-muted);
    font-weight: 600;
    background: var(--gray-50);
}
.stock-alert-table td.num, .stock-alert-table th.num { text-align: right; font-variant-numeric: tabular-nums; }
.stock-alert-table tr:last-child td { border-bottom: none; }
/* Row highlight for out/low stock — dark text pinned so dark-mode --text
   doesn't ghost over the pale bg. */
.stock-alert-table tr.row-out { background: #fef2f2; color: #7f1d1d; }
.stock-alert-table tr.row-out td { color: #7f1d1d; }
.stock-alert-table tr.row-low { background: #fffbeb; color: #713f12; }
.stock-alert-table tr.row-low td { color: #713f12; }
.stock-alert-table .dot { display: inline-block; width: 8px; height: 8px; border-radius: 50%; margin-right: 4px; vertical-align: middle; }
@media (max-width: 640px) {
    .stock-alert-table th, .stock-alert-table td { padding: 6px 8px; }
    .stock-alert-head h2 { font-size: 15px; }
}

/* Priority chips on card rows + summary */
.prio-chip {
    display: inline-block;
    padding: 2px 8px;
    border-radius: 999px;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.03em;
    line-height: 1.4;
}
.prio-chip-low    { background: #ecfdf5; color: #065f46; border: 1px solid #a7f3d0; }
.prio-chip-medium { background: #fefce8; color: #713f12; border: 1px solid #fde68a; }
.prio-chip-urgent { background: #fee2e2; color: #991b1b; border: 1px solid #fecaca; }
.job-row.prio-urgent { border-left: 3px solid #dc2626; }
.job-row.prio-medium { border-left: 3px solid #eab308; }
.job-row.prio-low    { border-left: 3px solid #10b981; }

/* Quote revisions list on Site Visit stage */
.quote-list {
    display: flex;
    flex-direction: column;
    gap: 8px;
}
.quote-item {
    display: flex;
    flex-direction: column;
    gap: 5px;
    padding: 9px 11px;
    background: var(--gray-50);
    border: 1px solid var(--border);
    border-radius: var(--r);
}
.quote-item.is-final {
    background: #ecfdf5;
    border-color: #10b981;
    border-left-width: 3px;
    /* Pin the text color to dark-green so children (quote-body, quote-amt,
       quote-attach, quote-attach-name, tiny.muted lines) all read against
       the pale-green background — otherwise they inherit var(--text) which
       is near-white on dark mode and disappears on the pale bg. */
    color: #065f46;
}
.quote-item.is-final .quote-body,
.quote-item.is-final .quote-amt,
.quote-item.is-final .quote-attach,
.quote-item.is-final .quote-attach-name,
.quote-item.is-final .muted,
.quote-item.is-final .tiny { color: #065f46; }
.quote-rev {
    font-weight: 700;
    font-size: 12px;
    letter-spacing: 0.03em;
    color: var(--text-2);
    white-space: nowrap;
}
.quote-item.is-final .quote-rev { color: #065f46; }
/* Header: id + rev + amount on the left, actions pinned right. */
.quote-head {
    display: flex; align-items: center; gap: 8px;
    flex-wrap: wrap; min-width: 0;
}
/* One row per attached PDF: the name pill stretches, its own download button
   sits at the end so it's unambiguous which button fetches which file.
   A two-column GRID (not per-row flex) so every row shares one button column
   sized to the widest label — otherwise "Download" and "Cust." size their rows
   independently and neither the buttons nor the pills line up. */
.quote-files {
    display: grid;
    grid-template-columns: minmax(0, 1fr) max-content;
    gap: 5px 8px;
    align-items: center;
}
/* display:contents lifts the pill + button into the parent grid so they land in
   the shared columns rather than nesting their own flex row. */
.quote-file { display: contents; }
.quote-file .quote-attach { min-width: 0; }
.quote-file .btn { white-space: nowrap; justify-content: center; width: 100%; }
/* The "No attachment uploaded" note has no button — let it span both columns. */
.quote-files > .tiny { grid-column: 1 / -1; }
.quote-amt { font-weight: 700; font-size: 15px; margin: 0 auto 0 0; }
.quote-attach {
    display: inline-flex; align-items: center; gap: 6px;
    margin: 2px 0;
    padding: 3px 8px;
    background: var(--gray-50);
    border: 1px solid var(--border);
    border-radius: 999px;
    color: var(--text);
    text-decoration: none;
    font-size: 12px;
    max-width: 100%;
}
.quote-attach:hover { border-color: var(--brand-light); background: var(--brand-50); }
.quote-attach-name {
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    max-width: 240px;
}

/* Mobile: the card is already stacked, so only the action row needs help —
   full width with 44px touch targets. The old `order:` / `flex:` juggling for a
   three-column row is gone along with that layout. */
@media (max-width: 640px) {
    .quote-item { padding: 11px; }
    .quote-amt { font-size: 17px; }
    /* Right-aligned and compact: only "Mark final" earns the full-width
       treatment. Stretching the two icon buttons left them marooned at
       opposite ends of the row. */
    .quote-acts { margin-left: auto; justify-content: flex-end; }
    .quote-acts .icon-btn { flex: 0 0 auto; min-height: 38px; min-width: 38px; }
    .quote-acts .btn { flex: 0 1 auto; min-height: 38px; }
    .quote-acts .btn[onclick*="markQuoteFinal"] {
        flex: 1 1 auto;
        background: var(--brand); color: #fff; border-color: var(--brand);
    }
    /* Keep each file's download button beside its name, just tighter. */
    .quote-file { gap: 6px; }
    .quote-file .btn { min-height: 36px; padding: 6px 9px; }
}

/* Sidebar Payments panel — read-only summary with progress bar */
.pay-summary {
    display: flex;
    flex-direction: column;
    gap: 4px;
}
.pay-summary-row {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 8px;
}
.pay-summary-row strong { font-size: 16px; }
.pay-summary-outstanding {
    padding-top: 6px;
    margin-top: 2px;
    border-top: 1px dashed var(--border);
}
.pay-progress-wrap {
    height: 8px;
    background: var(--gray-100);
    border-radius: 999px;
    overflow: hidden;
    margin: 6px 0 4px;
}
.pay-progress-bar {
    height: 100%;
    background: linear-gradient(90deg, #f59e0b, #eab308);
    border-radius: 999px;
    transition: width 240ms var(--ease-out, ease-out);
}
.pay-progress-bar.is-full {
    background: linear-gradient(90deg, #10b981, #059669);
}
.payments-mini-list {
    list-style: none;
    padding: 0;
    margin: 0;
    display: flex;
    flex-direction: column;
    gap: 8px;
}
.payments-mini-list li { padding: 6px 0; border-bottom: 1px dashed var(--border); }
.payments-mini-list li:last-child { border-bottom: none; }
.pay-line { display: flex; align-items: baseline; justify-content: space-between; }
.pay-recent-label {
    text-transform: uppercase;
    letter-spacing: 0.04em;
    font-weight: 600;
    margin-bottom: 4px;
}
/* Payment log rows in the sidebar — allow edit/delete actions to fit */
.pay-log-item {
    display: grid;
    grid-template-columns: 1fr auto;
    align-items: start;
    column-gap: 6px;
    row-gap: 2px;
}
.pay-log-item .pay-line { grid-column: 1; }
.pay-log-item .tiny.muted { grid-column: 1; }
.pay-log-actions {
    grid-column: 2;
    grid-row: 1 / span 2;
    display: flex;
    align-items: center;
    gap: 2px;
}
.pay-log-actions .icon-btn { padding: 4px; }

/* Design revisions on Design & Approval stage */
.design-list {
    display: flex;
    flex-direction: column;
    gap: 14px;
    margin-top: 12px;
}
.design-card {
    background: var(--gray-50);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    padding: 14px 16px;
}
.design-card.is-approved {
    background: #ecfdf5;
    border-color: #10b981;
    border-left-width: 4px;
    color: #065f46;
}
.design-card.is-approved h4,
.design-card.is-approved .muted,
.design-card.is-approved .tiny,
.design-card.is-approved .design-head { color: #065f46; }
.design-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 10px;
    flex-wrap: wrap;
    margin-bottom: 8px;
}
.design-rev-label {
    font-weight: 700;
    font-size: 13px;
    letter-spacing: 0.03em;
    color: var(--text-2);
    text-transform: uppercase;
}
.is-approved .design-rev-label { color: #065f46; }
.design-title {
    font-size: 15px;
    font-weight: 600;
    color: var(--text);
    margin-top: 2px;
}
.design-actions { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; }
.design-notes {
    font-size: 13px;
    color: var(--text-2);
    line-height: 1.5;
    padding: 6px 10px;
    background: var(--surface);
    border-radius: var(--r);
    margin-bottom: 10px;
    white-space: pre-wrap;
    word-break: break-word;
}
/* Design gallery — first image is the "hero" layout (full width, natural
   aspect, no crop) so the workshop can actually READ the dimensions.
   Additional images below tile as square thumbs. */
.design-image-hero {
    position: relative;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    overflow: hidden;
    display: flex;
    flex-direction: column;
    margin-bottom: 12px;
}
.design-image-hero a {
    display: block;
    max-width: 100%;
    background: var(--gray-50);
    /* No forced aspect ratio — the image stays at its natural proportions,
       which is what "full size" means for a layout drawing. */
}
.design-image-hero img {
    width: 100%;
    height: auto;
    max-height: 70vh;   /* stop a 4K photo from consuming the whole viewport */
    object-fit: contain;
    display: block;
    cursor: zoom-in;
    background: var(--gray-50);
}

.design-images {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
    gap: 8px;
}
/* Design / layout reference on production + QC stages — renders the
   drawing full-width so annotations and dimensions are legible without
   forcing the worker to open a lightbox first. Every image stacks
   vertically at full width; multiple images (e.g. plan + elevation)
   just keep stacking. object-fit: contain preserves the aspect ratio
   so tall floor plans aren't cropped to a square. */
.worker-ref-card { padding-top: 12px; }
.worker-ref-imgs {
    display: flex;
    flex-direction: column;
    gap: 12px;
    margin-top: 8px;
}
.worker-ref-img {
    display: block;
    position: relative;
    background: var(--gray-50);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    overflow: hidden;
}
.worker-ref-img img {
    display: block;
    width: 100%;
    height: auto;
    max-height: 80vh;
    object-fit: contain;
    cursor: zoom-in;
    background: var(--gray-50);
}
.worker-ref-caption {
    display: block;
    padding: 6px 10px;
    background: var(--surface);
    border-top: 1px solid var(--border);
    font-size: 12px;
    color: var(--text-muted);
}
/* --- Document tiles (PDF spec sheets / CAD exports in a design gallery) -----
   A design attachment isn't always an image. thumb.php rasterises page 1 of a
   PDF so the tile still shows a real preview, and these two overlays make it
   unmistakable that (a) it's a document, and (b) you're only seeing page 1.
   Both sit on the anchor, which the markup gives position:relative.
   Flex, not table/absolute-grid, so they behave on a phone. */
.doc-badge {
    position: absolute;
    top: 8px;
    left: 8px;
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 3px 8px;
    border-radius: 999px;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: .03em;
    line-height: 1.4;
    box-shadow: 0 1px 3px rgba(0, 0, 0, .18);
    pointer-events: none;   /* never swallow the tap meant for the link */
}
.doc-badge svg { display: block; }
/* 96px thumb strip: no room for the glyph or the padding. */
.doc-badge-sm {
    top: 4px;
    left: 4px;
    padding: 1px 5px;
    font-size: 9px;
    gap: 0;
}
/* Deliberately a STATIC block, not an absolute overlay: the anchor in
   _worker_ref.php / design.php also contains the caption row, so a
   bottom:0 overlay landed on top of the caption instead of the preview.
   As a normal strip under the <img> it can't collide with anything. */
.doc-open-hint {
    display: block;
    padding: 5px 8px;
    background: #111827;
    color: #fff;
    font-size: 11px;
    font-weight: 600;
    text-align: center;
}
/* A rasterised page is a document, not a photo — cue "opens" not "zooms". */
.design-image-doc img,
.worker-ref-doc img { cursor: pointer; }
/* Page 1 of an A4 doc is tall; cap it so it can't eat the whole viewport. */
.design-image-doc img { max-height: 60vh; object-fit: contain; }
.worker-ref-doc img   { max-height: 70vh; object-fit: contain; }

.design-images-extra { margin-top: 4px; }
/* Mobile: additional images become full-width stacked hero-style tiles
   too. A grid of tiny thumbs on a phone was cramped + unreadable. */
@media (max-width: 640px) {
    .design-images-extra {
        display: flex;
        flex-direction: column;
        gap: 10px;
    }
    .design-images-extra .design-image-item {
        border-radius: var(--r-lg);
    }
    .design-images-extra .design-image-item a {
        aspect-ratio: auto !important;
        background: var(--gray-50);
    }
    .design-images-extra .design-image-item img {
        aspect-ratio: auto;
        object-fit: contain !important;
        width: 100%;
        height: auto;
        max-height: 70vh;
        background: var(--gray-50);
    }
}
.design-image-item {
    position: relative;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r);
    overflow: hidden;
    display: flex;
    flex-direction: column;
}
.design-image-item a {
    display: block;
    aspect-ratio: 1 / 1;
    overflow: hidden;
}
.design-image-item img {
    width: 100%; height: 100%;
    object-fit: cover;
    display: block;
    cursor: zoom-in;
}
.design-image-caption {
    padding: 4px 6px;
    border-top: 1px solid var(--border);
    background: var(--gray-50);
}
.design-caption-input {
    width: 100%;
    border: none;
    background: transparent;
    padding: 2px 0;
    font-size: 12px;
    outline: none;
    color: var(--text-2);
}
.design-caption-input::placeholder { color: var(--text-subtle); font-style: italic; }
.design-caption-input:focus { color: var(--text); }
.design-caption-view {
    display: block;
    font-size: 12px;
    color: var(--text-muted);
    padding: 2px 0;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
/* Design gallery delete affordance.
 * Discreet by default — a glass-morph square that reads clearly on any
 * image bg but doesn't scream. Turns red only on hover / focus, so the
 * destructive intent is present but not the first thing the eye sees.
 * Same 32 px hit target as our other icon-btns; matches on touch (no
 * hover-to-reveal, since mobile has no hover). */
.design-image-del {
    position: absolute;
    top: 8px; right: 8px;
    width: 32px; height: 32px;
    padding: 0;
    border-radius: 8px;
    background: rgba(255, 255, 255, 0.72);
    color: var(--text-2);
    border: 1px solid rgba(255, 255, 255, 0.6);
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.18);
    -webkit-backdrop-filter: blur(6px) saturate(1.2);
    backdrop-filter: blur(6px) saturate(1.2);
    line-height: 1;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    opacity: 1;
    transition: background var(--dur), color var(--dur), border-color var(--dur),
                transform 90ms ease-out, box-shadow var(--dur);
}
.design-image-del svg { display: block; width: 15px; height: 15px; stroke-width: 1.75; }
.design-image-del:hover,
.design-image-del:focus-visible {
    background: rgba(220, 38, 38, 0.92);
    color: #fff;
    border-color: rgba(220, 38, 38, 0.92);
    box-shadow: 0 2px 6px rgba(220, 38, 38, 0.32);
    outline: none;
}
.design-image-del:active { transform: scale(0.94); }

/* Dark theme — dark glass instead of frosted white, same behaviour on hover. */
[data-theme="dark"] .design-image-del {
    background: rgba(15, 23, 42, 0.68);
    color: var(--text);
    border-color: rgba(255, 255, 255, 0.08);
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.5);
}
[data-theme="dark"] .design-image-del:hover,
[data-theme="dark"] .design-image-del:focus-visible {
    background: rgba(220, 38, 38, 0.88);
    color: #fff;
    border-color: rgba(220, 38, 38, 0.9);
}

/* Edit-in-builder button — sits to the LEFT of the trash so mgmt can
   jump straight into the layout editor for scenes generated from the
   builder. Same frosted glass treatment as .design-image-del, blue
   hover state so its intent is distinct from delete. Only rendered
   when the design image is joined to a layout_scenes row (see
   stages/design.php). */
.design-image-edit {
    position: absolute;
    top: 8px; right: 48px;       /* 32px trash + 8px gap */
    width: 32px; height: 32px;
    padding: 0;
    border-radius: 8px;
    background: rgba(255, 255, 255, 0.72);
    color: var(--text-2);
    border: 1px solid rgba(255, 255, 255, 0.6);
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.18);
    -webkit-backdrop-filter: blur(6px) saturate(1.2);
    backdrop-filter: blur(6px) saturate(1.2);
    line-height: 1;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    transition: background var(--dur), color var(--dur), border-color var(--dur),
                transform 90ms ease-out, box-shadow var(--dur);
}
.design-image-edit svg { display: block; width: 15px; height: 15px; stroke-width: 1.75; }
.design-image-edit:hover,
.design-image-edit:focus-visible {
    background: var(--brand, #2563eb);
    color: #fff;
    border-color: var(--brand, #2563eb);
    box-shadow: 0 2px 6px rgba(37, 99, 235, 0.32);
    outline: none;
}
.design-image-edit:active { transform: scale(0.94); }
[data-theme="dark"] .design-image-edit {
    background: rgba(15, 23, 42, 0.68);
    color: var(--text);
    border-color: rgba(255, 255, 255, 0.08);
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.5);
}
[data-theme="dark"] .design-image-edit:hover,
[data-theme="dark"] .design-image-edit:focus-visible {
    background: rgba(37, 99, 235, 0.88);
    color: #fff;
    border-color: rgba(37, 99, 235, 0.9);
}
.design-image-add {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 4px;
    aspect-ratio: 1 / 1;
    background: var(--surface);
    border: 2px dashed var(--border);
    border-radius: var(--r);
    color: var(--text-muted);
    cursor: pointer;
    transition: all var(--dur);
}
.design-image-add:hover {
    border-color: var(--brand);
    color: var(--brand);
    background: var(--brand-50, #f0fdf4);
}
.design-image-add input[type="file"] { display: none; }
.design-add-details summary { list-style: none; cursor: pointer; }
.design-add-details summary::-webkit-details-marker { display: none; }

/* Long "Add Layouts" button placed BELOW the gallery grid (not inside it).
   Uses .btn.btn-primary.btn-block so it looks + behaves like every other
   primary CTA in the app. */
.design-image-add-wide {
    /* .btn already handles flex layout + colors; the label wrapper just
       needs to stop the file input from taking any layout space. */
    text-decoration: none;
}
.design-image-add-wide input[type="file"] {
    /* Belt-and-braces — inline style already hides it, but if that gets
       stripped by any transform this keeps the visual clean. */
    position: absolute;
    left: -9999px;
    width: 1px;
    height: 1px;
    opacity: 0;
}

/* Order Confirmed stage — payment progress block above the log */
.pay-progress-block {
    background: var(--gray-50);
    border: 1px solid var(--border);
    border-radius: var(--r);
    padding: 12px 14px;
    margin-bottom: 12px;
}
.pay-progress-figures {
    display: flex;
    align-items: flex-end;
    gap: 12px;
    flex-wrap: wrap;
    margin-bottom: 10px;
}
.pay-fig-value { font-size: 18px; font-weight: 700; color: var(--text); }
.pay-fig-sep   { font-size: 16px; color: var(--text-muted); padding: 0 4px 4px; font-weight: 300; }
@media (max-width: 640px) {
    .pay-progress-figures { gap: 8px; }
    .pay-fig-value { font-size: 15px; }
    .pay-fig-sep { display: none; }
}

/* Stock page banner linking to /stock_alerts.php */
.stock-alert-banner {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 12px 16px;
    background: #fef2f2;
    border: 1px solid #fecaca;
    border-radius: var(--r-lg);
    color: #7f1d1d;
    text-decoration: none;
    margin-bottom: 16px;
    transition: background var(--dur), border-color var(--dur);
}
.stock-alert-banner:hover { background: #fee2e2; border-color: #fca5a5; }
.stock-alert-banner-icon { display: flex; }
.stock-alert-banner-text { flex: 1; }
.stock-alert-banner-cta {
    display: inline-flex; align-items: center; gap: 4px;
    font-weight: 600; font-size: 13px;
}
@media (max-width: 640px) {
    .stock-alert-banner { flex-wrap: wrap; }
    .stock-alert-banner-cta { margin-left: auto; }
}

/* Dedicated /stock_alerts.php page */
.alerts-filter-bar {
    display: flex;
    align-items: center;
    gap: 12px;
    margin: 16px 0 12px;
    flex-wrap: wrap;
}
.alerts-filter-chips { display: flex; gap: 6px; flex-wrap: wrap; }
.alerts-filter-chips .chip {
    padding: 6px 12px;
    border-radius: 999px;
    border: 1px solid var(--border);
    background: var(--surface);
    font-size: 13px;
    cursor: pointer;
    font-weight: 500;
}
.alerts-filter-chips .chip:hover { background: var(--gray-100); }
.alerts-filter-chips .chip.chip-on {
    background: var(--brand);
    color: #fff;
    border-color: var(--brand);
}
.alerts-search {
    flex: 1;
    min-width: 220px;
    max-width: 400px;
    padding: 8px 12px;
    border: 1px solid var(--border);
    border-radius: var(--r);
    font-size: 14px;
}
.alert-cat {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    padding: 14px 16px;
    margin-bottom: 16px;
    box-shadow: var(--shadow-sm);
}
.alert-cat[hidden] { display: none; }
.alert-cat-head {
    display: flex; align-items: center; gap: 8px;
    padding-bottom: 10px;
    border-bottom: 1px solid var(--border);
    margin-bottom: 10px;
}
.alert-cat-head h2 { margin: 0; font-size: 16px; flex: 1; }
.alert-cat .dot { width: 10px; height: 10px; border-radius: 50%; }
.alert-cat-actions {
    display: inline-flex;
    gap: 6px;
    margin-left: 8px;
    flex-shrink: 0;
}
@media (max-width: 640px) {
    .alert-cat-head { flex-wrap: wrap; row-gap: 8px; }
    .alert-cat-head h2 { flex: 1 1 100%; order: 1; }
    .alert-cat-head .dot { order: 0; }
    .alert-cat-head > .tiny.muted { order: 2; flex: 1; }
    .alert-cat-actions { order: 3; margin-left: 0; }
}

/* BOM head layout (title + Download PDF button) */
.bom-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 12px;
    margin-bottom: 10px;
    flex-wrap: wrap;
}

/* Grid-based BOM / basic / fittings rows live in .check-grid earlier in
   this file. Nothing responsive needed here — CSS Grid + @media inside
   the .check-grid block already handles the mobile reflow. */

/* Reports section headers with export buttons + filter bar */
.reports-section-head {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: 12px;
    flex-wrap: wrap;
}
/* Two segmented toggle groups + PDF + Excel. Their combined min-content width
   is ~500px, so without wrapping they run straight off a phone viewport and
   widen the whole document (the Excel button was unreachable). Wrapping lets
   the buttons drop onto a second line instead; `min-width: 0` stops the bar
   from forcing .reports-section-head wider than the container. */
.reports-export {
    display: flex;
    gap: 6px;
    flex-wrap: wrap;
    justify-content: flex-end;
    min-width: 0;
}
/* Phone: the bar gets the whole line under the "Workload" heading and reads
   left-to-right like the rest of the page instead of right-aligned and ragged.
   Kept here rather than in the .workload-view-toggle media block further up,
   because that block loses the cascade to the base rule above. */
@media (max-width: 640px) {
    .reports-export { width: 100%; justify-content: flex-start; }
}
.reports-filter-bar {
    display: flex;
    align-items: center;
    gap: 12px;
    margin-bottom: 12px;
    flex-wrap: wrap;
}

/* Filter + sort bar above each Home tab's job list */
.job-filter-details { margin: 0 0 12px; }
.job-filter-details > summary {
    list-style: none;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 8px 14px;
    background: var(--gray-50);
    border: 1px solid var(--border);
    border-radius: var(--r);
    font-size: 13px;
    font-weight: 500;
    color: var(--text-2);
    user-select: none;
    min-height: 40px;
}
.job-filter-details > summary::-webkit-details-marker { display: none; }
.job-filter-details > summary::after {
    content: '▾';
    margin-left: auto;
    color: var(--text-muted);
    font-size: 10px;
    transition: transform .15s;
}
.job-filter-details[open] > summary::after { transform: rotate(180deg); }
.job-filter-active {
    background: var(--brand);
    color: #fff;
    font-size: 10px;
    padding: 2px 8px;
    border-radius: 10px;
    font-weight: 700;
    letter-spacing: 0.02em;
}

/* Desktop: always show the filter bar, hide the collapse chip. `<details>`
   normally hides its body when closed regardless of child display rules,
   so we override to force the body visible. */
@media (min-width: 901px) {
    .job-filter-details > summary { display: none; }
    .job-filter-details > .job-filter-bar { display: flex !important; }
}

.job-filter-bar {
    display: flex;
    gap: 10px;
    flex-wrap: wrap;
    margin: 8px 0 12px;
    padding: 10px 12px;
    background: var(--gray-50);
    border: 1px solid var(--border);
    border-radius: var(--r);
    align-items: end;
}
/* Mobile: two columns of stacked filters, wider tap targets. */
@media (max-width: 640px) {
    .job-filter-bar {
        display: grid;
        grid-template-columns: 1fr 1fr;
        gap: 8px 10px;
    }
    .job-filter-lbl { min-width: 0; }
    .job-filter-lbl:last-child { grid-column: 1 / -1; }
    .job-filter-lbl select,
    .job-filter-lbl input {
        width: 100%;
        min-height: 40px;
        font-size: 14px;
    }
}
.job-filter-lbl {
    display: flex;
    flex-direction: column;
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--text-muted);
    font-weight: 600;
    gap: 3px;
}
.job-filter-lbl select {
    font-size: 13px;
    padding: 4px 8px;
    border: 1px solid var(--border);
    border-radius: 4px;
    background: var(--surface);
    text-transform: none;
    letter-spacing: 0;
    font-weight: 400;
    color: var(--text);
}

/* ---- Quote number strip on the .quote-item rev block ---- */
.quote-num-strip {
    display: inline-block;
    padding: 2px 6px;
    margin-bottom: 0;
    background: var(--brand);
    color: #fff;
    border-radius: 4px;
    font-family: 'SF Mono', Menlo, Consolas, monospace;
    font-size: 10.5px;
    font-weight: 700;
    letter-spacing: 0.03em;
}

/* ---- Admin → Quote Template form ---- */
.quote-settings-form {
    display: flex;
    flex-direction: column;
    gap: 16px;
    max-width: 820px;
    margin-top: 12px;
}
.quote-settings-fs {
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    padding: 14px 16px;
    background: var(--surface);
    display: flex;
    flex-direction: column;
    gap: 10px;
}
.quote-settings-fs > legend {
    padding: 0 8px;
    font-size: 12px;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--text-muted);
    font-weight: 700;
}
.quote-settings-fs label { margin: 0; display: flex; flex-direction: column; gap: 4px; }
.quote-settings-fs .row { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
@media (max-width: 560px) {
    .quote-settings-fs .row { grid-template-columns: 1fr; }
}

/* ---- Quote generator modal ---- */
.qgen-modal .modal-body { max-width: 900px; }
.qgen-section {
    padding: 12px 0;
    border-top: 1px dashed var(--border);
}
.qgen-section:first-of-type { border-top: none; padding-top: 0; }
.qgen-section-title {
    font-size: 11.5px;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--text-muted);
    font-weight: 700;
    margin: 0 0 8px;
}
.qgen-meta-row {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 10px;
}
@media (max-width: 640px) {
    .qgen-meta-row { grid-template-columns: 1fr; }
}
.qgen-lines-head,
.qgen-line {
    display: grid;
    /* 7 columns: drag-handle (24px) + particulars + size + qty + rate + amount + remove
       Head row has an empty first column so the labels stay aligned. */
    grid-template-columns: 24px minmax(0, 3fr) 0.8fr 0.5fr 0.8fr 0.8fr 32px;
    gap: 6px;
    /* Align inputs to the top so a multi-line particulars textarea grows
       downward instead of vertically centering (which pushed the other
       cells' text off-baseline once the textarea got tall). */
    align-items: start;
}
.qgen-lines-head { align-items: center; }
.qgen-lines-head {
    padding: 6px 4px;
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--text-muted);
    font-weight: 700;
    border-bottom: 1px solid var(--border);
}
.qgen-line {
    padding: 6px 4px;
    border-bottom: 1px solid var(--gray-50);
}
.qgen-line input,
.qgen-line textarea {
    min-height: 34px;
    padding: 6px 8px;
    font-size: 13px;
}
.qgen-line textarea.qgen-particulars {
    /* Grow with content — height is set inline by _qgenAutoGrow. */
    resize: vertical;
    line-height: 1.35;
    font-family: inherit;
    overflow: hidden;
    min-height: 34px;
}
.qgen-line.is-group textarea.qgen-particulars {
    color: var(--danger);
    font-weight: 700;
    background: var(--danger-50, #fef2f2);
    border-color: #fecaca;
}
.qgen-line.is-group input:not(.qgen-particulars) {
    visibility: hidden;
    pointer-events: none;
}
.qgen-line-remove {
    background: transparent;
    border: none;
    color: var(--text-muted);
    cursor: pointer;
    font-size: 18px;
    line-height: 1;
    padding: 4px;
    border-radius: 4px;
}
.qgen-line-remove:hover { color: var(--danger); background: var(--danger-50, #fef2f2); }
.qgen-line-actions {
    display: flex; gap: 6px; margin-top: 8px; flex-wrap: wrap;
}
.qgen-totals {
    margin-left: auto;
    display: flex;
    flex-direction: column;
    gap: 4px;
    padding: 8px 12px;
    background: var(--gray-50);
    border: 1px solid var(--border);
    border-radius: var(--r);
    font-variant-numeric: tabular-nums;
    max-width: 320px;
}
.qgen-totals .row-item {
    display: flex; justify-content: space-between; gap: 12px;
    font-size: 13px;
}
.qgen-totals .row-item.total {
    font-size: 15px;
    font-weight: 700;
    padding-top: 4px;
    border-top: 1px solid var(--border);
    color: var(--brand);
}

/* --------------------------------------------------------------------
   Searchable multi-select pill picker — used by the batch-split wizard
   so every specialist column (MS, Ply, Painter, Electrician, Plumber)
   shares one consistent input style. Selected users appear as pill
   chips; typing filters a dropdown of candidates.
   -------------------------------------------------------------------- */
.pill-picker {
    position: relative;
    background: var(--surface);
    border: 1px solid var(--border-strong);
    border-radius: 6px;
    padding: 4px 6px;
    min-height: 40px;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 4px;
    font-size: 13px;
    cursor: text;
}
.pill-picker:focus-within {
    border-color: var(--brand-light);
    box-shadow: 0 0 0 2px rgba(30, 58, 138, 0.12);
}
.pill-picker .pp-selected {
    display: contents;   /* let chips flow inside the parent's flex layout */
}
.pill-picker .pp-chip {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    background: var(--brand-50, #eff6ff);
    color: var(--brand, #1e3a8a);
    border: 1px solid var(--brand-light, #bfdbfe);
    border-radius: 999px;
    padding: 2px 4px 2px 8px;
    font-weight: 600;
    font-size: 12px;
    white-space: nowrap;
    max-width: 100%;
}
.pill-picker .pp-chip > span:first-child {
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 120px;
}
.pill-picker .pp-chip-x {
    background: transparent;
    border: none;
    color: var(--brand);
    cursor: pointer;
    padding: 0 4px;
    font-size: 14px;
    line-height: 1;
    border-radius: 999px;
}
.pill-picker .pp-chip-x:hover { background: rgba(30, 58, 138, 0.15); }
.pill-picker .pp-input {
    flex: 1 1 80px;
    min-width: 60px;
    border: none;
    outline: none;
    background: transparent;
    padding: 4px 2px;
    font-size: 13px;
    color: var(--text);
}
.pill-picker .pp-dropdown {
    position: absolute;
    top: calc(100% + 4px);
    left: 0;
    right: 0;
    background: var(--surface);
    border: 1px solid var(--border-strong);
    border-radius: 6px;
    box-shadow: 0 6px 18px rgba(15, 23, 42, 0.12);
    max-height: 220px;
    overflow-y: auto;
    z-index: 30;
}
.pill-picker .pp-option {
    padding: 8px 10px;
    cursor: pointer;
    font-size: 13px;
    line-height: 1.3;
    color: var(--text);
    border-bottom: 1px solid var(--gray-50);
}
.pill-picker .pp-option:last-child { border-bottom: none; }
.pill-picker .pp-option.is-hover,
.pill-picker .pp-option:hover {
    background: var(--brand-50, #eff6ff);
    color: var(--brand);
}
.pill-picker .pp-empty {
    padding: 10px 12px;
    font-size: 12px;
    color: var(--text-muted);
    font-style: italic;
}

/* Vendor stats popup — 2x2 tile grid used inside a simple modal.
   Chunk 3 nice-to-have; matches the muted-tile look of the sidebar stats.  */
.vs-grid { display:grid; grid-template-columns:1fr 1fr; gap:8px; }
.vs-tile { background:var(--gray-50); border:1px solid var(--border); border-radius:8px; padding:10px 12px; }
.vs-lbl  { font-size:10px; text-transform:uppercase; letter-spacing:0.05em; color:var(--text-muted); font-weight:700; }
.vs-val  { font-size:20px; font-weight:700; margin:2px 0; font-family:monospace; }

/* ============================================================================
   Reorder + PO history tables — inherit .data-table look, override for the
   in-cart row tint + tighter input cells. Also fixes the fact that the
   reorder-tab controls sat unstyled before. */

/* Compact input cells on the reorder table so 6 controls fit per row on a
   laptop screen without wrapping. */
.reorder-table th, .reorder-table td { padding: 8px 10px; vertical-align: middle; }
.reorder-table td input[type="number"],
.reorder-table td select {
    padding: 6px 8px;
    min-height: 32px;
    border: 1px solid var(--border);
    border-radius: var(--r-sm);
    background: var(--surface);
    font-size: 13px;
}
.reorder-table td input[type="checkbox"] {
    width: 18px; height: 18px;
    accent-color: var(--brand);
    cursor: pointer;
}
.reorder-table td input:disabled,
.reorder-table td select:disabled {
    background: var(--gray-25);
    color: var(--text-muted);
    cursor: not-allowed;
}
/* Row highlight when the row is in the reorder cart. Inline style writes
   `background:rgba(30,64,175,0.06)` on the <tr> and this rule adds a left
   accent stripe so it reads as "selected" not just "coloured". */
.reorder-table tbody tr[style*="rgba(30,64,175"] > td:first-child {
    box-shadow: inset 3px 0 0 var(--brand);
}
/* Neutralise the default row-hover from .data-table — otherwise ticked
   rows get a grey wash on hover that fights with the blue tint. */
.reorder-table tbody tr:hover td { background: transparent; }
.reorder-table tbody tr[style*="rgba(30,64,175"]:hover td {
    background: rgba(30,64,175,0.09);
}
/* Pull the master "select all visible" checkbox in the thead into a
   pleasant column. */
.reorder-table thead th input[type="checkbox"] {
    width: 16px; height: 16px;
    accent-color: var(--brand);
}

/* PO history table: mostly just the same width + hover-tone as data-table.
   Pills / variance already carry their own inline colors. */
.po-history-table th, .po-history-table td { padding: 10px 14px; vertical-align: middle; }
.po-history-table .pill { display: inline-block; padding: 2px 10px; border-radius: 999px; }
.po-history-table a { text-decoration: none; }
.po-history-table a:hover { text-decoration: underline; }

/* Dark mode: bump the in-cart tint so it still reads on the darker surface. */
[data-theme="dark"] .reorder-table tbody tr[style*="rgba(30,64,175"] > td {
    background: rgba(96,165,250,0.14) !important;
}
[data-theme="dark"] .reorder-table tbody tr[style*="rgba(30,64,175"] > td:first-child {
    box-shadow: inset 3px 0 0 #60a5fa;
}

/* Qty stepper — inline − / value / + control. Mobile-friendly (native
   number spinner arrows don't render on iOS/Android), also convenient on
   desktop for one-tap increments. Used on stock_alerts.php + the new
   purchase-orders.php picker rows. Wire-up (app.js) uses event delegation
   so any wrapper .qty-stepper works automatically. */
.qty-stepper {
    display: inline-flex;
    align-items: stretch;
    max-width: 128px;
}
.qty-stepper .qty-btn {
    width: 30px;
    background: var(--gray-100);
    border: 1px solid var(--border);
    color: var(--text);
    font-size: 18px;
    font-weight: 600;
    line-height: 1;
    cursor: pointer;
    user-select: none;
    -webkit-user-select: none;
    padding: 0;
}
.qty-stepper .qty-btn:hover { background: var(--gray-200); }
.qty-stepper .qty-btn:active { background: var(--gray-300); }
.qty-stepper .qty-minus { border-radius: var(--r-sm) 0 0 var(--r-sm); border-right: none; }
.qty-stepper .qty-plus  { border-radius: 0 var(--r-sm) var(--r-sm) 0; border-left:  none; }
.qty-stepper input[type="number"] {
    width: 60px;
    text-align: center;
    border-radius: 0 !important;
    min-height: 32px;
    padding: 6px 4px;
}
/* Grey out the buttons when the input is disabled (row not yet ticked)
   so users don't try to click a control that would silently do nothing. */
.qty-stepper:has(input:disabled) .qty-btn {
    opacity: 0.4;
    pointer-events: none;
    cursor: not-allowed;
}
/* Hide the native browser spinner inside the stepper since our +/- buttons
   replace it — otherwise desktop shows both, cluttering a narrow cell. */
.qty-stepper input[type="number"]::-webkit-outer-spin-button,
.qty-stepper input[type="number"]::-webkit-inner-spin-button {
    -webkit-appearance: none; margin: 0;
}
.qty-stepper input[type="number"] { -moz-appearance: textfield; }

/* Reorder-tab hint bar — small info banner between the toolbar and the table. */
.reorder-hint {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    padding: 10px 14px;
    margin: 4px 0 12px;
    background: var(--info-light);
    color: #075985;
    border: 1px solid #b2ddff;
    border-radius: var(--r);
    font-size: 12.5px;
    line-height: 1.4;
}
.reorder-hint svg { flex-shrink: 0; margin-top: 1px; }
[data-theme="dark"] .reorder-hint {
    background: rgba(59,130,246,0.14);
    color: #dbeafe;
    border-color: #1e3a8a;
}

/* ============================================================================
   Stock Alerts — inline PO drafting.
   Reuses .reorder-hint from the (now-removed) admin reorder tab; adds a
   sticky-ish cart-bar + row-selected accent for the reorder-capable version
   of the stock-alert table.  */
.reorder-cart-bar {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
    padding: 10px 14px;
    margin: 4px 0 16px;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r);
    box-shadow: var(--shadow-sm);
    position: sticky;
    top: 12px;
    z-index: 5;
}
.reorder-cart-bar #reorder-cart-summary { font-size: 12.5px; }

/* Reorder-capable stock-alert table: normalise input sizing. */
.stock-alert-table-reorder .reorder-tick-cell { text-align: center; width: 32px; }
.stock-alert-table-reorder input[type="checkbox"] {
    width: 18px; height: 18px; accent-color: var(--brand); cursor: pointer;
}
.stock-alert-table-reorder input[type="number"],
.stock-alert-table-reorder select {
    padding: 6px 8px;
    min-height: 32px;
    border: 1px solid var(--border);
    border-radius: var(--r-sm);
    background: var(--surface);
    font-size: 13px;
    width: 100%;
    max-width: 180px;
}
.stock-alert-table-reorder td input[type="number"] { max-width: 110px; text-align: right; }
.stock-alert-table-reorder input:disabled,
.stock-alert-table-reorder select:disabled {
    background: var(--gray-25);
    color: var(--text-muted);
    cursor: not-allowed;
}
/* Selected row — soft blue tint + left-edge accent. Overrides the existing
   row-out / row-low tints so ticked rows read as "in cart" regardless of
   status. */
.stock-alert-table tr.reorder-selected > td {
    background: rgba(30,64,175,0.08) !important;
}
.stock-alert-table tr.reorder-selected > td:first-child {
    box-shadow: inset 3px 0 0 var(--brand);
}
[data-theme="dark"] .stock-alert-table tr.reorder-selected > td {
    background: rgba(96,165,250,0.16) !important;
}
[data-theme="dark"] .stock-alert-table tr.reorder-selected > td:first-child {
    box-shadow: inset 3px 0 0 #60a5fa;
}

/* ============================================================================
   Contractor Bills — mobile-first modal + list.
   Below 720px:
     * Modal fills the viewport edge-to-edge (fullscreen-sheet style).
     * Add-line form stacks each input full-width, big touch targets.
     * Line-item table + bill-list table both flip to stacked "cards" —
       each row becomes its own card, each cell shows its column heading
       inline via the data-label attribute set in bills.js.
     * Footer actions pin to the bottom of the viewport.
   Above 720px behaviour is unchanged.
   ============================================================================ */

/* --- Modal shell --- */
.bill-modal .bill-modal-body {
    max-width: 820px;
    max-height: 90vh;
    overflow: auto;
    padding: 20px;
    display: flex;
    flex-direction: column;
    gap: 10px;
}
.bill-modal-head {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
}
.bill-modal-head h2 { margin: 0; flex: 1; font-size: 18px; min-width: 0; }
.bill-modal-head #bill-modal-status { font-weight: 700; }
.bill-modal-sub { font-size: 12px; margin: 2px 0 0; }
/* Collapse the sub-line when it has no text (draft bills, no submit-date). */
.bill-modal-sub:empty { display: none; }
.bill-reject-note {
    margin: 6px 0;
    padding: 10px 12px;
    background: #fef2f2;
    color: #7f1d1d;
    border: 1px solid #fca5a5;
    border-radius: 6px;
    font-size: 13px;
}
[data-theme="dark"] .bill-reject-note {
    background: rgba(220, 38, 38, 0.14);
    color: #fecaca;
    border-color: #7f1d1d;
}
.bill-section-h { margin: 12px 0 6px; font-size: 13px; }

/* --- Add-line form (desktop is a 5-col grid). --- */
.bill-add-line {
    padding: 12px;
    background: var(--gray-50);
    border: 1px solid var(--border);
    border-radius: 8px;
}
/* Two-row grid: fields on top (Cabin + Particulars wide, Qty + Charge narrow),
   Add button gets its own full-width row below. Reads clearly on every
   viewport width instead of squeezing 5 controls onto one line. */
.bill-add-line-grid {
    display: grid;
    grid-template-columns: minmax(0, 2fr) 110px 130px;
    grid-template-areas:
        "particulars qty charge"
        "add         add add";
    gap: 12px;
    align-items: end;
}
.bill-add-line-grid > label:nth-of-type(1) { grid-area: particulars; }
.bill-add-line-grid > label:nth-of-type(2) { grid-area: qty; }
.bill-add-line-grid > label:nth-of-type(3) { grid-area: charge; }
.bill-add-line-grid .bill-add-btn         { grid-area: add; width: 100%; }

.bill-add-line-grid label {
    display: flex;
    flex-direction: column;
    gap: 4px;
    font-size: 11px;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.03em;
    font-weight: 600;
    min-width: 0;
}
.bill-add-line-grid input,
.bill-add-line-grid select {
    min-height: 44px;
    padding: 10px 12px;
    font-size: 14px;
    text-transform: none;
    letter-spacing: normal;
    font-weight: 400;
    color: var(--text);
    width: 100%;
}
.bill-add-btn {
    min-height: 46px;
    font-size: 15px;
    font-weight: 600;
    letter-spacing: 0.01em;
}
.bill-add-hint { margin: 10px 0 0; }

.bill-notes-label {
    display: block;
    margin-top: 6px;
    font-size: 13px;
}

/* --- Footer --- */
.bill-modal-footer {
    display: flex;
    justify-content: space-between;
    align-items: center;
    flex-wrap: wrap;
    gap: 12px;
    margin-top: 12px;
    padding-top: 14px;
    border-top: 1px solid var(--border);
}
.bill-total { font-size: 22px; font-weight: 700; }
.bill-actions { display: flex; gap: 8px; flex-wrap: wrap; }

/* --- Lines table extras (desktop) --- */
.bill-lines-table td { vertical-align: middle; }
.bill-lines-table input[type="number"],
.bill-lines-table input[type="text"],
.bill-lines-table select {
    min-height: 36px;
    padding: 6px 8px;
    font-size: 14px;
}

/* Sequential-number chip next to the cabin title on the bill list.
   Compact display of "#<seq>" — the per-worker sequence, not the raw
   auto-increment id which was eating a whole row before. */
.bill-list-table .bill-seq-chip {
    display: inline-block;
    padding: 1px 8px;
    margin-right: 4px;
    border-radius: 999px;
    background: var(--gray-100, #f3f4f6);
    color: var(--text-muted);
    font-size: 11px;
    font-weight: 600;
    vertical-align: baseline;
}
[data-theme="dark"] .bill-list-table .bill-seq-chip {
    background: rgba(255,255,255,0.06);
}

/* ================================================================
   Mobile — max-width 720px. Both tables flip to stacked cards; the
   modal becomes a full-height sheet; add-line form stacks; footer
   sticks.
   ================================================================ */
@media (max-width: 720px) {
    .bill-modal { padding: 0; }
    .bill-modal .bill-modal-body {
        max-width: 100%;
        width: 100%;
        max-height: 100vh;
        max-height: 100dvh;
        height: 100vh;
        height: 100dvh;
        border-radius: 0;
        padding: 14px 14px 24px;
    }

    .bill-modal-head h2 { font-size: 16px; }
    .bill-modal-close { min-width: 40px; min-height: 40px; }

    /* Add-line form on mobile — Particulars is full-width on top; Qty and
       Charge sit side-by-side to keep the form compact; Add button spans
       both. Big touch targets. */
    .bill-add-line-grid {
        grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
        grid-template-areas:
            "particulars particulars"
            "qty         charge"
            "add         add";
        gap: 10px;
    }
    .bill-add-btn { width: 100%; min-height: 48px; font-size: 15px; margin-top: 4px; }
    .bill-add-line-grid input,
    .bill-add-line-grid select { min-height: 44px; font-size: 15px; }

    /* Footer sits inline at the natural bottom of the sheet. Earlier
       revision used position:sticky which made the footer visibly float
       whenever the sheet's content was shorter than the viewport — total
       on one full-width row, actions stacked full-width below, floating
       with a shadow. Ugly. Keep it simple: normal in-flow block, total
       and actions on the same row, actions wrap to their own row when
       there are too many. */
    .bill-modal-footer {
        display: flex;
        align-items: center;
        justify-content: space-between;
        flex-wrap: wrap;
        gap: 10px;
        margin: 14px 0 0;
        padding: 12px 0 calc(4px + env(safe-area-inset-bottom));
        border-top: 1px solid var(--border);
    }
    .bill-total-wrap { flex: 0 0 auto; }
    .bill-actions {
        flex: 1 1 100%;
        display: flex;
        flex-direction: column;
        gap: 8px;
    }
    .bill-actions .btn { width: 100%; min-height: 44px; font-size: 15px; }

    /* Bill list + bill line items — no table hackery. bills.js renders
       real <ul>/<ol> of flex cards; see .bc-* and .bl-* rules outside
       this media block. Mobile inherits the fluid flex layout. */
}


/* ============================================================================
   Worker payments — admin section on bills.php.
   Card grid of workers + a per-worker modal with entry form + history.
   ============================================================================ */

/* --- Worker list card + header --- */
.wp-card { min-width: 0; }
.wp-head {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 8px;
    margin-bottom: 6px;
}
.wp-search-wrap { margin: 6px 0 10px; }
.wp-search { max-width: 320px; width: 100%; }

/* --- Worker list (table) --- */
.wp-worker-table td { vertical-align: middle; }
.wp-worker-table tr.wp-row { cursor: pointer; }
.wp-worker-table tr.wp-row:hover { background: var(--gray-50, #f9fafb); }
[data-theme="dark"] .wp-worker-table tr.wp-row:hover { background: rgba(255,255,255,0.04); }
.wp-avatar {
    flex: 0 0 36px;
    width: 36px; height: 36px;
    border-radius: 50%;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    color: #fff;
    font-weight: 700;
    font-size: 13px;
    letter-spacing: 0.02em;
    object-fit: cover;
}

/* --- Modal --- */
#wp-modal .modal-body { max-height: 90vh; overflow: auto; }
/* Worker payments modal header — desktop places name / total / close on one
   row; mobile promotes the total block to a full-width row underneath the
   name so it has real breathing space and never fights the close button. */
.wp-modal-head {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto auto;
    grid-template-areas: "id total close";
    align-items: center;
    gap: 12px;
    margin-bottom: 10px;
}
.wp-modal-id     { grid-area: id;    min-width: 0; }
.wp-modal-total  { grid-area: total; }
.wp-modal-close  { grid-area: close; }

.wp-modal-total {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 4px;
    text-align: right;
    line-height: 1.3;
    color: var(--text);
    min-width: 0;
}
.wp-modal-total .wp-total-row {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
    justify-content: flex-end;
}
.wp-modal-total .wp-total-paid {
    font-weight: 700;
    font-size: 18px;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
    color: var(--text);
}
.wp-modal-total .wp-total-context {
    font-size: 12px;
    color: var(--text-muted);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}
.wp-modal-total .pend-badge {
    font-size: 12px;
    padding: 3px 10px;
}

/* Mobile: name+close stay on top row, total drops to its own full-width
   row underneath — full width, left-aligned, breathing room around it. */
@media (max-width: 640px) {
    .wp-modal-head {
        grid-template-columns: minmax(0, 1fr) auto;
        grid-template-areas:
            "id    close"
            "total total";
        gap: 8px 12px;
    }
    .wp-modal-total {
        align-items: stretch;
        text-align: left;
        margin-top: 4px;
        padding: 10px 12px;
        background: var(--gray-50, #f9fafb);
        border: 1px solid var(--border);
        border-radius: 10px;
    }
    .wp-modal-total .wp-total-row {
        justify-content: space-between;
        gap: 8px;
    }
    .wp-modal-total .wp-total-paid { font-size: 17px; }
    .wp-modal-total .wp-total-context { font-size: 12px; }
}
[data-theme="dark"] .wp-modal-total {
    /* mobile bg */
}
[data-theme="dark"] @media (max-width: 640px) {}
[data-theme="dark"] .wp-modal-total { }
@media (max-width: 640px) {
    [data-theme="dark"] .wp-modal-total {
        background: rgba(255,255,255,0.03);
    }
}
.wp-make-card { background: var(--gray-50, #f9fafb); }

/* Action strip below the modal header — holds the "Make payment" toggle.
   Kept out of the form container so a closed form is truly invisible. */
.wp-modal-actions {
    display: flex;
    gap: 8px;
    margin: 4px 0 10px;
}
.wp-modal-actions .wp-make-toggle {
    min-height: 40px;
    font-weight: 600;
}

/* --- Entry-form grid inside the modal --- */
.wp-form-grid {
    display: grid;
    grid-template-columns: 1fr 1fr 140px 150px;
    grid-template-areas:
        "kind   mode    amount paidon"
        "note   note    note   note"
        "add    add     add    add";
    gap: 10px;
    align-items: end;
    margin-top: 4px;
}
.wp-form-grid > label:nth-of-type(1) { grid-area: kind; }
.wp-form-grid > label:nth-of-type(2) { grid-area: mode; }
.wp-form-grid > label:nth-of-type(3) { grid-area: amount; }
.wp-form-grid > label:nth-of-type(4) { grid-area: paidon; }
.wp-form-grid > label:nth-of-type(5) { grid-area: note; }
.wp-form-grid .wp-add-btn            { grid-area: add; width: 100%; min-height: 46px; font-size: 15px; font-weight: 600; }
.wp-form-grid label {
    display: flex;
    flex-direction: column;
    gap: 4px;
    font-size: 11px;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.03em;
    font-weight: 600;
    min-width: 0;
}
.wp-form-grid input,
.wp-form-grid select,
.wp-form-grid textarea {
    min-height: 42px;
    padding: 8px 10px;
    font-size: 14px;
    text-transform: none;
    letter-spacing: normal;
    font-weight: 400;
    color: var(--text);
    width: 100%;
    font-family: inherit;
}
.wp-form-grid textarea {
    resize: vertical;
    min-height: 60px;
    line-height: 1.4;
}

/* --- Kind pills — colour-coded so admins can scan the ledger --- */
.wp-kind-pill {
    display: inline-block;
    padding: 3px 10px;
    border-radius: 999px;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.02em;
}
.wp-kind-salary          { background: #eff6ff; color: #1d4ed8; }
.wp-kind-incentive       { background: #ecfdf5; color: #065f46; }
.wp-kind-bonus           { background: #fef3c7; color: #92400e; }
.wp-kind-overtime        { background: #fef2f2; color: #7f1d1d; }
.wp-kind-contract_charge { background: #f3e8ff; color: #6b21a8; }
.wp-kind-advance         { background: #f3f4f6; color: #374151; }
[data-theme="dark"] .wp-kind-salary          { background: rgba(29,78,216,0.18); color: #bfdbfe; }
[data-theme="dark"] .wp-kind-incentive       { background: rgba(6,95,70,0.24);  color: #a7f3d0; }
[data-theme="dark"] .wp-kind-bonus           { background: rgba(146,64,14,0.24); color: #fde68a; }
[data-theme="dark"] .wp-kind-overtime        { background: rgba(127,29,29,0.24); color: #fecaca; }
[data-theme="dark"] .wp-kind-contract_charge { background: rgba(107,33,168,0.24); color: #e9d5ff; }
[data-theme="dark"] .wp-kind-advance         { background: rgba(55,65,81,0.30);  color: #e5e7eb; }
[data-theme="dark"] .wp-make-card            { background: rgba(255,255,255,0.03); }

.wp-note-cell { color: var(--text-muted); font-size: 13px; }

/* ============================================================================
   Payment history — flex-card list inside the Worker payments modal.
   Same visual language as .wpc / .bc / .bl-line. Each card has three modes:
     .wph-card             view (default)
     .wph-card.wph-editing inline edit form
     .wph-card.wph-confirming inline delete confirm
   ============================================================================ */
.wp-history-cards {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 8px;
}
.wph-card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 12px;
    padding: 12px 14px;
    box-shadow: 0 1px 2px rgba(0,0,0,0.03);
    display: flex;
    flex-direction: column;
    gap: 6px;
}
/* Top strip: kind pill on the left, amount in the middle, action icons
   on the right. On narrow screens (< ~400px) the "Contractor Bill
   Amount" + "Advance" pills + amount + two 32px icons overflowed the
   viewport, hiding the edit/× buttons off-screen. flex-wrap lets the
   amount + actions drop onto a second line when there isn't enough
   room; row-gap gives that line breathing space. */
.wph-top {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 6px 10px;
    min-width: 0;
    /* Kind pill can shrink if the label is long ("Contractor Bill Amount");
       forces it to break to two lines within its pill rather than push
       everything else past the right edge. */
    overflow: hidden;
}
.wph-top .wp-kind-pill {
    max-width: 100%;
    white-space: normal;
    word-break: break-word;
    line-height: 1.2;
}
.wph-amt {
    font-size: 15px;
    font-weight: 700;
    color: var(--text);
    font-variant-numeric: tabular-nums;
    margin-left: auto;
    white-space: nowrap;
}
.wph-actions {
    flex: 0 0 auto;
    display: flex;
    gap: 2px;
    margin-left: 4px;
}
.wph-actions .icon-btn {
    min-width: 32px;
    min-height: 32px;
    font-size: 15px;
    line-height: 1;
    padding: 0;
}
/* Very narrow phones — force the amount + actions onto their own row
   under the pills so the two 32px icons always sit fully in-viewport. */
@media (max-width: 480px) {
    .wph-top { row-gap: 4px; }
    .wph-amt { margin-left: 0; }
    .wph-actions { margin-left: auto; }
}
/* Meta line: date · mode · recorded by. Wraps on narrow screens. */
.wph-meta {
    display: flex;
    flex-wrap: wrap;
    gap: 4px 8px;
    font-size: 12px;
    color: var(--text-muted);
    line-height: 1.4;
}
/* Note — indented + subtle background so it's visually a quote */
.wph-note {
    margin: 2px 0 0;
    padding: 6px 10px;
    background: var(--gray-50, #f9fafb);
    border-left: 3px solid var(--border);
    border-radius: 4px;
    font-size: 13px;
    line-height: 1.4;
    color: var(--text);
}
[data-theme="dark"] .wph-note {
    background: rgba(255,255,255,0.03);
}

/* Inline edit mode — a 2-column grid of labelled inputs + a footer with
   Cancel / Save buttons. */
.wph-editing {
    background: var(--gray-50, #f9fafb);
    border-color: var(--primary, #2563eb);
    box-shadow: 0 0 0 3px rgba(37,99,235,0.10);
}
[data-theme="dark"] .wph-editing {
    background: rgba(255,255,255,0.03);
}
.wph-edit-grid {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 8px 10px;
}
.wph-note-field { grid-column: 1 / -1; }
.wph-field {
    display: flex;
    flex-direction: column;
    gap: 4px;
    min-width: 0;
}
.wph-field > span {
    text-transform: uppercase;
    letter-spacing: 0.03em;
    font-size: 11px;
    font-weight: 600;
    color: var(--text-muted);
}
.wph-field input,
.wph-field select,
.wph-field textarea {
    height: 40px;
    padding: 8px 10px;
    font-size: 14px;
    width: 100%;
    box-sizing: border-box;
    border: 1px solid var(--border);
    border-radius: 8px;
    background: var(--surface);
    color: var(--text);
    font-family: inherit;
}
.wph-field textarea {
    height: auto;
    min-height: 60px;
    line-height: 1.4;
    resize: vertical;
}
.wph-field input:focus,
.wph-field select:focus,
.wph-field textarea:focus {
    outline: none;
    border-color: var(--primary, #2563eb);
    box-shadow: 0 0 0 3px rgba(37,99,235,0.15);
}
.wph-edit-actions {
    display: flex;
    justify-content: flex-end;
    gap: 8px;
    margin-top: 4px;
}
.wph-edit-actions .btn { min-height: 36px; }
@media (max-width: 480px) {
    .wph-edit-grid { grid-template-columns: 1fr; }
    .wph-edit-actions { flex-direction: column-reverse; }
    .wph-edit-actions .btn { width: 100%; min-height: 42px; }
}

/* Inline delete-confirm mode — red-tinted card with prompt + actions */
.wph-confirming {
    background: #fef2f2;
    border-color: #fecaca;
    border-left: 3px solid #b42318;
}
[data-theme="dark"] .wph-confirming {
    background: rgba(220,38,38,0.12);
    border-color: rgba(220,38,38,0.35);
    border-left-color: #f87171;
}
.wph-delete-strip {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    flex-wrap: wrap;
    font-size: 13px;
    color: #7f1d1d;
}
[data-theme="dark"] .wph-delete-strip { color: #fecaca; }
.wph-delete-actions { display: flex; gap: 8px; flex-wrap: wrap; }
@media (max-width: 480px) {
    .wph-delete-actions { width: 100%; }
    .wph-delete-actions .btn { flex: 1 1 auto; min-height: 40px; }
}

@media (max-width: 720px) {
    /* The section's outer stage-card eats a lot of horizontal space by
       default. Tighten padding on phones so the row cards get more room. */
    .wp-card { padding: 12px 12px; }
    .wp-head-hint { display: none; }
    .wp-search { max-width: 100%; }

    /* Make payment toggle full-width on phones so it's an obvious tap target. */
    .wp-modal-actions { margin: 6px 0 12px; }
    .wp-modal-actions .wp-make-toggle {
        width: 100%;
        min-height: 46px;
        font-size: 15px;
    }

    /* Worker list flips into stacked cards below 720px so each row stays
       scannable on a phone. The .data-table parent has its own border +
       overflow:hidden — clear both so our per-row cards render cleanly. */
    .wp-worker-table {
        border: none !important;
        background: transparent !important;
        border-radius: 0 !important;
        overflow: visible !important;
    }
    .wp-worker-table thead { display: none; }
    .wp-worker-table,
    .wp-worker-table tbody,
    .wp-worker-table tr,
    .wp-worker-table td { display: block; width: 100%; box-sizing: border-box; }
    .wp-worker-table tr.wp-row {
        background: var(--surface);
        border: 1px solid var(--border);
        border-radius: 12px;
        padding: 12px 14px;
        margin: 0 0 10px;
    }
    .wp-worker-table tr.wp-row:hover { background: var(--surface); }
    .wp-worker-table td {
        border: none !important;
        padding: 4px 0 !important;
        display: flex;
        align-items: center;
        gap: 10px;
        text-align: left !important;
        min-width: 0;
    }
    /* Column label inline on the left. Kept narrow so long values get room. */
    .wp-worker-table td::before {
        content: attr(data-label);
        flex: 0 0 88px;
        font-size: 11px;
        text-transform: uppercase;
        letter-spacing: 0.04em;
        font-weight: 600;
        color: var(--text-muted);
    }
    /* Worker cell is the "header" of the card — no inline label, name gets
       full width, title wraps below. */
    .wp-worker-table td[data-label="Worker"] {
        padding-bottom: 6px !important;
        border-bottom: 1px dashed var(--border) !important;
        margin-bottom: 4px;
    }
    .wp-worker-table td[data-label="Worker"]::before { display: none; }
    .wp-worker-table td[data-label="Worker"] > div {
        flex: 1;
        min-width: 0;
        display: flex;
        align-items: center;
        gap: 10px;
    }
    .wp-worker-table td[data-label="Worker"] > div > div {
        flex: 1;
        min-width: 0;
    }
    .wp-worker-table td[data-label="Worker"] strong {
        display: block;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
        font-size: 15px;
    }
    .wp-worker-table td[data-label="Worker"] .tiny {
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }
    /* Value cells: push the value all the way right so it doesn't butt
       against the label. justify-content works because the cell is flex
       with an ::before pseudo (the label) + the cell content. */
    .wp-worker-table td[data-label="Total"],
    .wp-worker-table td[data-label="Last paid"],
    .wp-worker-table td[data-label="Records"] {
        justify-content: space-between;
        padding: 3px 0 !important;
        font-size: 13px;
        gap: 12px;
    }
    .wp-worker-table td[data-label="Total"] { font-size: 15px; font-weight: 700; }
    .wp-worker-table td[data-label="Total"]::before,
    .wp-worker-table td[data-label="Last paid"]::before,
    .wp-worker-table td[data-label="Records"]::before {
        flex: 0 0 auto;
        font-size: 11px;
    }
    /* Open button sits below Records with clear separation */
    .wp-worker-table td.wp-row-open {
        margin-top: 8px !important;
        padding-top: 10px !important;
    }
    /* Open button gets its own full-width row at the bottom of the card */
    .wp-worker-table td.wp-row-open {
        padding-top: 8px !important;
        border-top: 1px dashed var(--border) !important;
        margin-top: 4px;
    }
    .wp-worker-table td.wp-row-open::before { display: none; }
    .wp-worker-table td.wp-row-open .btn {
        width: 100%;
        min-height: 44px;
        font-size: 15px;
    }
    .wp-worker-table .wp-avatar { flex: 0 0 40px; width: 40px; height: 40px; }

    /* Modal becomes a full-height sheet on phones */
    #wp-modal { padding: 0; }
    #wp-modal .modal-body {
        max-width: 100%;
        width: 100%;
        max-height: 100vh;
        max-height: 100dvh;
        height: 100vh;
        height: 100dvh;
        border-radius: 0;
        padding: 14px 14px 24px;
    }

    .wp-form-grid {
        grid-template-columns: 1fr;
        grid-template-areas: none;
    }
    /* Bump specificity to beat the desktop :nth-of-type(N) rules that pin
       each label into a named area. Without these, on mobile the labels
       still request grid-area: kind / mode / … while `grid-template-areas`
       is `none`, and the browser collapses them all into row/col 1. */
    .wp-form-grid > label:nth-of-type(1),
    .wp-form-grid > label:nth-of-type(2),
    .wp-form-grid > label:nth-of-type(3),
    .wp-form-grid > label:nth-of-type(4),
    .wp-form-grid > label:nth-of-type(5),
    .wp-form-grid .wp-add-btn { grid-area: auto; }
    .wp-form-grid input,
    .wp-form-grid select { min-height: 44px; font-size: 15px; }
    .wp-form-grid .wp-add-btn { min-height: 48px; font-size: 15px; }

    /* Payment history is now a flex-card list (.wph-*), not a table.
       No mobile-specific overrides needed — the layout is fluid. */
}

/* ============================================================================
   Bill cards — semantic <ul>/<li> flex layout for the "Your bills" list.
   Replaces the old table-with-media-query-overrides pattern which never
   collapsed cleanly on mobile. Works fluidly at every width.
   ============================================================================ */
/* Purchase-order admin tab — top-of-tab money summary tiles + card list.
   Same visual pattern as .bill-cards below (bills.php inspired) so mgmt
   sees one consistent layout across billing surfaces. Fully responsive:
   tiles collapse to single column, cards reflow their rows. */
.po-summary-tiles {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 12px;
}
@media (max-width: 700px) {
    .po-summary-tiles { grid-template-columns: 1fr; }
}
.po-cards {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    /* Roomier gap so each card visibly stands alone rather than blending
       into the next one on a long list. */
    gap: 14px;
}
.po-cards .po-card {
    background: var(--surface);
    /* Solid 2px slate border at rest so every card is clearly its own
       block. Hover keeps the border but flips it to brand blue + deepens
       the shadow. 4px left accent stays reserved for the amber "short
       delivery" flag. */
    border: 2px solid #94a3b8;
    border-left: 4px solid #94a3b8;
    border-radius: 12px;
    padding: 14px 16px;
    box-shadow: 0 1px 3px rgba(15, 23, 42, 0.06);
    display: flex;
    flex-direction: column;
    gap: 6px;
    cursor: pointer;
    transition: border-color .1s ease, box-shadow .1s ease, background .1s ease, transform .1s ease;
}
.po-cards .po-card:hover {
    border-color: var(--primary, #2563eb);
    box-shadow: 0 6px 16px rgba(15, 23, 42, 0.12);
    transform: translateY(-1px);
}
/* Amber accent + tint for POs that were received short — same signal as
   the row highlight in the old table view. */
.po-cards .po-card--short {
    border-left-color: #f59e0b;
    background: #fffbeb;
}
.po-cards .po-card__row {
    display: flex;
    gap: 10px;
    align-items: baseline;
    flex-wrap: wrap;
    min-width: 0;
}
.po-cards .po-card__num {
    color: var(--brand);
    font-weight: 700;
    font-size: 13px;
    text-decoration: none;
    flex: 0 0 auto;
}
.po-cards .po-card__vendor {
    flex: 1 1 auto;
    min-width: 0;
}
.po-cards .po-card__vendor strong { font-weight: 600; }
.po-cards .po-card__total {
    margin-left: auto;
    font-weight: 700;
    font-size: 15px;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
    flex: 0 0 auto;
}
/* Admin-only trash icon on each PO card. Kept muted at rest, turns red on
   hover so it doesn't visually compete with the vendor / total info. */
.po-cards .po-card__del {
    flex: 0 0 auto;
    width: 26px; height: 26px;
    border: 1px solid var(--border);
    background: #fff;
    color: #94a3b8;
    border-radius: 6px;
    font-size: 16px;
    line-height: 1;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    transition: color .15s, border-color .15s, background .15s;
}
.po-cards .po-card__del:hover {
    color: #dc2626;
    border-color: #fecaca;
    background: #fef2f2;
}
.po-cards .po-card__meta {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px 10px;
    font-size: 12px;
    color: var(--text-muted);
    line-height: 1.4;
}
.po-cards .po-card__meta .pill { font-size: 10px; padding: 2px 8px; font-weight: 700; }
/* Payment status row — single line on desktop, wraps on narrow cards.
   Three flavours: --full (green chip only), --none (red chip), partial
   (numbers + short bar + amber pending chip). Bar is compact rather than
   full-width because the same info now lives in the pending chip. */
.po-cards .po-card__pay {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
    margin-top: 4px;
    font-size: 12px;
    font-variant-numeric: tabular-nums;
}
.po-cards .po-card__pay-numbers {
    display: inline-flex;
    align-items: baseline;
    gap: 6px;
}
.po-cards .po-card__pay-bar {
    flex: 0 0 auto;
    width: 140px;
    height: 6px;
    background: #e5e7eb;
    border-radius: 999px;
    overflow: hidden;
    display: inline-block;
}
.po-cards .po-card__pay-bar > i {
    display: block;
    height: 100%;
    background: linear-gradient(90deg, #fbbf24, #f59e0b);
    transition: width .2s;
}
.po-cards .po-card__pay-chip {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    font-size: 11px;
    font-weight: 700;
    padding: 3px 10px;
    border-radius: 999px;
    line-height: 1.4;
    white-space: nowrap;
}
.po-cards .po-card__pay-chip--full {
    background: #ecfdf5;
    color: #065f46;
    border: 1px solid #a7f3d0;
}
.po-cards .po-card__pay-chip--partial {
    background: #fffbeb;
    color: #92400e;
    border: 1px solid #fde68a;
    margin-left: auto;
}
.po-cards .po-card__pay-chip--none {
    background: #fef2f2;
    color: #b42318;
    border: 1px solid #fecaca;
    /* Push subsequent siblings (the bill chip) to the far right of the
       pay row — matches the partial chip's alignment behaviour. */
    margin-right: auto;
}
.po-cards .po-card__pay--none .po-card__bill-chip,
.po-cards .po-card__pay--full .po-card__bill-chip {
    /* Full/none pay chips don't grow, so the bill chip needs its own
       auto-margin to reach the right edge on those rows. */
    margin-left: auto;
}
/* Vendor-bill-number chip — sits at the right end of the pay row on
   every state (unpaid, partial, fully paid). Muted slate palette so it
   reads as a reference tag, not another payment signal. */
.po-cards .po-card__bill-chip {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    font-size: 11px;
    font-weight: 600;
    padding: 3px 10px;
    border-radius: 999px;
    background: #f1f5f9;
    color: #334155;
    border: 1px solid #e2e8f0;
    line-height: 1.4;
    white-space: nowrap;
    max-width: 45%;
    overflow: hidden;
    text-overflow: ellipsis;
}
/* Below ~500px viewport, let the partial pending chip wrap under the bar
   instead of getting shoved off — the "margin-left:auto" trick that
   right-aligns it on desktop doesn't play nice on tiny screens. */
@media (max-width: 500px) {
    .po-cards .po-card__pay-chip--partial { margin-left: 0; }
    .po-cards .po-card__pay-bar { flex: 1 1 100px; width: auto; min-width: 100px; }
}
/* Created-at + author stamp lives in the meta row as another muted chip so
   the card doesn't need a dedicated footer row (saves vertical space).
   Pushed to the right end via margin-left: auto. */
.po-cards .po-card__stamp {
    margin-left: auto;
    font-size: 11px;
}

.bill-cards {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 8px;
}
.bill-cards .bc {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 10px;
    padding: 10px 12px;
    box-shadow: 0 1px 2px rgba(0,0,0,0.04);
    cursor: pointer;
    transition: border-color .1s ease, box-shadow .1s ease;
    display: flex;
    flex-direction: column;
    gap: 4px;
}
.bill-cards .bc:hover {
    border-color: var(--primary, #2563eb);
    box-shadow: 0 2px 6px rgba(0,0,0,0.08);
}

/* Line 1 (mgmt only): worker name + job title, muted */
.bill-cards .bc-worker {
    font-size: 12px;
    color: var(--text-muted);
    line-height: 1.4;
}
.bill-cards .bc-worker strong {
    font-weight: 700;
    color: var(--text);
}

/* Line 2 (title row):
     [#seq]   cabin title  ·  N cabins                          ₹amount
   Amount hugs the right edge so a vertical scan of the list makes totals
   directly comparable. Uses tabular-nums so digits align by column. */
.bill-cards .bc-title {
    display: flex;
    align-items: baseline;
    gap: 8px;
    font-size: 14px;
    line-height: 1.35;
    flex-wrap: nowrap;
    min-width: 0;
}
.bill-cards .bc-seq {
    display: inline-block;
    padding: 1px 8px;
    border-radius: 999px;
    background: var(--gray-100, #f3f4f6);
    color: var(--text-muted);
    font-size: 11px;
    font-weight: 600;
    flex: 0 0 auto;
}
[data-theme="dark"] .bill-cards .bc-seq { background: rgba(255,255,255,0.06); }
.bill-cards .bc-cabin {
    min-width: 0;
    flex: 1 1 auto;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.bill-cards .bc-cabin strong { font-weight: 600; color: var(--text); }

/* Amount right-aligned in its own column so amounts line up across rows. */
.bill-cards .bc-title .bc-amt {
    color: var(--text);
    font-weight: 700;
    font-size: 14px;
    text-align: right;
    flex: 0 0 auto;
    font-variant-numeric: tabular-nums;
    margin-left: auto;
    white-space: nowrap;
}

/* Line 3 (meta row): [status pill]  · N lines  · date  ·  reject-note? */
.bill-cards .bc-meta {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px 8px;
    font-size: 12px;
    color: var(--text-muted);
    line-height: 1.4;
}
.bill-cards .bc-meta .pill {
    font-size: 10px;
    padding: 2px 8px;
    font-weight: 700;
}

/* Utility — .muted works everywhere */
.bill-cards .muted { color: var(--text-muted); }

/* "Create bill" inline picker — cabin dropdown + Create button on one row.
   Both controls share the same 42px height and sit on the same baseline;
   the label is above the SELECT only, so we align to flex-end (bottom)
   and pin the button to that same 42px height. Wraps to stacked on mobile. */
.bill-create-form {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;   /* button bottom == select bottom */
    gap: 10px;
    margin-top: 8px;
}
.bill-create-form label {
    display: flex;
    flex-direction: column;
    gap: 4px;
    flex: 1 1 200px;
    min-width: 0;
    margin: 0;               /* remove default form-label margin */
}
.bill-create-form label > span {
    text-transform: uppercase;
    letter-spacing: 0.03em;
    font-weight: 600;
    font-size: 11px;
    line-height: 1.4;
    color: var(--text-muted);
}
.bill-create-form select {
    height: 42px;
    padding: 0 12px;
    font-size: 14px;
    line-height: 42px;
    width: 100%;
    box-sizing: border-box;
}
.bill-create-form .btn {
    height: 42px;
    padding: 0 18px;
    line-height: 1;
    display: inline-flex;
    align-items: center;
    gap: 6px;
    flex: 0 0 auto;
    box-sizing: border-box;
}
@media (max-width: 640px) {
    .bill-create-form { flex-direction: column; align-items: stretch; }
    .bill-create-form label { flex: 1 1 auto; }
    .bill-create-form .btn { width: 100%; height: 46px; }
}

/* Cabin banner inside the bill modal — the most important context for the
   entire bill. Rendered as a bold heading (not a chip) so it's the first
   thing the eye lands on after the modal opens. */
.bill-modal-cabin {
    display: block;
    /* Tighter to the "Bill #X" heading above — this is the primary context
       for the whole modal, so keep it visually anchored to the title. */
    margin: 2px 0 6px;
    padding: 0;
    background: transparent;
    border: none;
    font-size: 24px;
    font-weight: 800;
    letter-spacing: -0.01em;
    line-height: 1.2;
    color: var(--text);
}
[data-theme="dark"] .bill-modal-cabin { color: var(--text); }
.bill-modal-cabin[hidden] { display: none; }

/* Worker payments modal — tab strip (Payments | Bills) */
.wp-tabs {
    display: flex;
    gap: 4px;
    border-bottom: 1px solid var(--border);
    margin: 4px 0 12px;
}
.wp-tab {
    background: transparent;
    border: none;
    border-bottom: 2px solid transparent;
    padding: 8px 14px;
    font: inherit;
    font-size: 13px;
    font-weight: 600;
    color: var(--text-muted);
    cursor: pointer;
    margin-bottom: -1px;
}
.wp-tab:hover { color: var(--text); }
.wp-tab.active {
    color: var(--primary, #2563eb);
    border-bottom-color: var(--primary, #2563eb);
}
.wp-pane[hidden] { display: none; }

/* Shared pending badge — used on worker rows, worker modal header, bill card,
   bill modal header. Coloured by sign: green + / red − / neutral 0. Class is
   toggled by renderPendingBadge() in bills.js. */
.pend-badge {
    display: inline-block;
    padding: 2px 10px;
    border-radius: 999px;
    font-size: 12px;
    font-weight: 700;
    line-height: 1.45;
    white-space: nowrap;
}
.pend-badge.pend-pos {
    background: #dcfce7;   /* green-100 */
    color: #14532d;         /* green-900 */
}
[data-theme="dark"] .pend-badge.pend-pos {
    background: rgba(34,197,94,0.18);
    color: #86efac;
}
.pend-badge.pend-neg {
    background: #fee2e2;   /* red-100 */
    color: #7f1d1d;         /* red-900 */
}
[data-theme="dark"] .pend-badge.pend-neg {
    background: rgba(220,38,38,0.16);
    color: #fca5a5;
}
.pend-badge.pend-zero {
    background: var(--gray-100, #f3f4f6);
    color: var(--text-muted);
}
[data-theme="dark"] .pend-badge.pend-zero {
    background: rgba(255,255,255,0.06);
}
.bill-pending-wrap {
    display: flex;
    align-items: center;
    gap: 6px;
    margin-top: 4px;
    flex-wrap: wrap;
}

/* Contractor's Your bills / Payments received tab strip header — tab
   buttons on the left, persistent pending pill on the right. */
.me-tabs-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    flex-wrap: wrap;
    margin-bottom: 4px;
}
.me-tabs-head .wp-tabs { margin: 0; flex: 1 1 auto; min-width: 0; }
.me-tabs-head .pend-badge { flex: 0 0 auto; }

/* wp-tabs originally used inside the mgmt modal — no left padding to fit
   the icon in the button label there. Same rules apply here. */
.me-tabs-head .wp-tab { display: inline-flex; align-items: center; gap: 6px; }

/* Panes reuse the shared wp-pane hide rule. Nothing else needed. */
.wp-pane[data-me-pane][hidden] { display: none; }

/* Base .pill styling — round-edged; used everywhere a plain <span class="pill">
   is rendered (bill status, kind labels, generic tags). Inline style="…" on
   individual pills can still override background / colour per row. */
.pill {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 2px 10px;
    border-radius: 999px;
    font-size: 11px;
    font-weight: 700;
    line-height: 1.45;
    white-space: nowrap;
}

/* Dark-mode: bill status pills use hard-coded light pastel backgrounds via
   inline `style` on the element. Those pastels stay lighter than the dark
   surface, so they still read — we just soften them so they don't scream. */
[data-theme="dark"] .pill {
    filter: brightness(0.88) saturate(0.9);
    box-shadow: inset 0 0 0 1px rgba(255,255,255,0.05);
}

/* ============================================================================
   Bill line items — flex-card layout for the bill modal.
   Two shapes:
     .bl-line              writable line (worker editing a draft/rejected bill)
     .bl-line.bl-readonly  read-only display (submitted/approved lines)
   Works identically on desktop + mobile. No table→card CSS gymnastics.
   ============================================================================ */
.bill-lines {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 8px;
}
.bl-line {
    background: var(--surface);
    /* Darker, higher-contrast border so each work card reads as a clearly
       fenced block. Falls back through --gray-400/300 depending on theme
       vars. Dark mode gets a brighter overlay for the same visual weight. */
    border: 2px solid var(--gray-400, #94a3b8);
    border-radius: 10px;
    padding: 8px 10px;
    box-shadow: 0 1px 2px rgba(0,0,0,0.04);
    display: flex;
    flex-direction: column;
    gap: 6px;
}
[data-theme="dark"] .bl-line {
    border-color: rgba(255,255,255,0.28);
}

/* Head strip — "Line N" on the left, delete button (or read-only total)
   on the right. Tighter padding + no dashed rule to save vertical space. */
.bl-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    padding-bottom: 0;
    border-bottom: none;
    margin: 0;
}
.bl-num {
    text-transform: uppercase;
    letter-spacing: 0.05em;
    font-size: 11px;
    font-weight: 700;
    color: var(--text-muted);
}
.bl-remove.icon-btn {
    min-width: 28px;
    min-height: 28px;
    font-size: 16px;
    line-height: 1;
    padding: 0;
}

/* Field group — label above input, stacked tightly. */
.bl-field {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
}
.bl-field > span {
    text-transform: uppercase;
    letter-spacing: 0.03em;
    font-size: 10px;
    font-weight: 600;
    color: var(--text-muted);
    line-height: 1.2;
}
.bl-field input {
    height: 36px;
    padding: 0 10px;
    font-size: 14px;
    width: 100%;
    box-sizing: border-box;
    border: 1px solid var(--border);
    border-radius: 6px;
    background: var(--surface);
    color: var(--text);
}
.bl-field input:focus {
    outline: none;
    border-color: var(--primary, #2563eb);
    box-shadow: 0 0 0 3px rgba(37,99,235,0.15);
}

/* Particulars gets its own full-width row. */
.bl-particulars { width: 100%; }

/* Qty × Charge — two-column row with a "×" separator so the math is visible.
   Stays side-by-side at ALL viewport widths — stacking each into its own row
   made the line-item card too tall on phones. Inputs shrink instead. */
.bl-row {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
    align-items: end;
    gap: 6px;
}
.bl-mult {
    align-self: center;
    padding-top: 14px;   /* offset so × sits between inputs, not labels */
    font-size: 14px;
    font-weight: 700;
    color: var(--text-muted);
}
.bl-row .bl-field { min-width: 0; }
.bl-row .bl-field input {
    text-align: right;
    font-variant-numeric: tabular-nums;
    min-width: 0;
}
/* Tight on narrow phones — smaller padding + font so both inputs breathe. */
@media (max-width: 420px) {
    .bl-row { gap: 4px; }
    .bl-row .bl-field input {
        padding: 0 8px;
        font-size: 13px;
    }
    .bl-mult { padding-top: 12px; }
}

/* Work total — compact separator + label right-aligned. */
.bl-total {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    padding-top: 6px;
    margin-top: 2px;
    border-top: 1px solid var(--border);
    font-size: 14px;
}
.bl-total-lbl {
    text-transform: uppercase;
    letter-spacing: 0.04em;
    font-size: 11px;
    font-weight: 700;
    color: var(--text-muted);
}
.bl-total-val {
    font-weight: 700;
    color: var(--text);
    font-variant-numeric: tabular-nums;
}

/* Read-only shape — no inputs; just plain text lines.
     Line N          ₹total
     Particulars text
     3 × ₹500
*/
.bl-readonly {
    gap: 6px;
    padding: 12px 14px;
}
.bl-readonly .bl-head { border-bottom: none; padding-bottom: 0; }
.bl-particulars-ro {
    font-size: 15px;
    line-height: 1.4;
    color: var(--text);
}
.bl-calc {
    font-size: 13px;
    color: var(--text-muted);
    font-variant-numeric: tabular-nums;
}


/* ============================================================================
   Worker payments — flex-card grid (mgmt "Worker payments" section).
   Replaces the old .wp-worker-table pattern. Cards feel like stage cards:
   pale surface, subtle border, hover lift, click anywhere to open.
   ============================================================================ */
/* Worker list is a vertical stack now, not a grid. One row per worker;
   pending badge gets its own column so it never fights the name for space.
   Row layout via CSS grid:
     [avatar]  [name+title]  [pending badge]
     .........  [paid + last-paid meta row spans the ID column]
*/
.wp-cards {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 8px;
}
.wpc {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 12px;
    padding: 12px 14px;
    box-shadow: 0 1px 2px rgba(0,0,0,0.04);
    cursor: pointer;
    transition: border-color .12s ease, box-shadow .12s ease;
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) auto;
    grid-template-areas:
        "avatar id   pend"
        ".      meta meta";
    align-items: center;
    column-gap: 12px;
    row-gap: 6px;
}
.wpc:hover, .wpc:focus-visible {
    border-color: var(--primary, #2563eb);
    box-shadow: 0 2px 6px rgba(0,0,0,0.06);
    outline: none;
}
[data-theme="dark"] .wpc:hover,
[data-theme="dark"] .wpc:focus-visible {
    box-shadow: 0 2px 6px rgba(0,0,0,0.35);
}

.wpc-head {
    display: contents;   /* let avatar + id + pend flow directly into the grid */
}
.wpc-head .wp-avatar {
    grid-area: avatar;
    flex: 0 0 auto;
    width: 40px;
    height: 40px;
    border-radius: 50%;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    color: white;
    font-weight: 700;
    font-size: 14px;
    object-fit: cover;
}
.wpc-id {
    grid-area: id;
    min-width: 0;
}
.wpc-id strong {
    display: block;
    font-size: 15px;
    line-height: 1.3;
    color: var(--text);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.wpc-id .tiny {
    font-size: 12px;
    line-height: 1.35;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.wpc-pend {
    grid-area: pend;
    justify-self: end;
}

/* Meta row — paid + last paid, right under the name column. Small dashed
   rule above so the row visually separates from the id line. */
.wpc-meta {
    grid-area: meta;
    display: flex;
    flex-wrap: wrap;
    gap: 4px 14px;
    padding-top: 6px;
    border-top: 1px dashed var(--border);
    font-size: 12.5px;
    line-height: 1.4;
    color: var(--text-muted);
}
.wpc-meta .mono {
    font-variant-numeric: tabular-nums;
    color: var(--text);
}

/* Inactive workers have no meta row — collapse the second grid track so
   the card stays compact. */
.wpc-inactive {
    grid-template-areas: "avatar id pend";
    row-gap: 0;
}

/* Mobile: allow the pending pill to drop under the name if the row gets
   too narrow, but keep the visual hierarchy. */
@media (max-width: 480px) {
    .wpc {
        grid-template-columns: auto minmax(0, 1fr);
        grid-template-areas:
            "avatar id"
            "avatar pend"
            ".      meta";
        column-gap: 10px;
        row-gap: 4px;
    }
    .wpc-inactive {
        grid-template-areas:
            "avatar id"
            "avatar pend";
    }
    .wpc-pend { justify-self: start; }
    .wpc-head .wp-avatar { width: 40px; height: 40px; grid-row: span 2; }
}

/* Inactive worker card — no bills and no payments yet. Muted body + a
   distinct "No activity" pill (styled like pend-zero but with a subtle
   dashed border so it doesn't get confused with a real settled balance). */
.wpc-inactive { opacity: 0.85; }
.wpc-inactive .wpc-id strong { color: var(--text); }
.pend-inactive {
    background: transparent;
    color: var(--text-muted);
    border: 1px dashed var(--border);
    font-weight: 600;
}
[data-theme="dark"] .pend-inactive {
    color: var(--text-muted);
    border-color: rgba(255,255,255,0.15);
}

/* ============================================================================
   Company Expenses — mgmt outer summary card + modal.
   ============================================================================ */
.exp-card {
    cursor: pointer;
    transition: border-color .12s ease, box-shadow .12s ease, transform .12s ease;
}
.exp-card:hover, .exp-card:focus-visible {
    border-color: var(--primary, #2563eb);
    box-shadow: 0 4px 12px rgba(0,0,0,0.08);
    transform: translateY(-1px);
    outline: none;
}
.exp-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    flex-wrap: wrap;
}
.exp-head-right {
    display: flex;
    align-items: center;
    gap: 16px;
    flex-wrap: wrap;
}
.exp-my-btn {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    font-weight: 600;
    background: var(--gray-100, #f3f4f6);
    color: var(--text);
    border: 1px solid var(--border);
}
[data-theme="dark"] .exp-my-btn {
    background: rgba(255,255,255,0.06);
}
.exp-my-btn:hover {
    border-color: var(--primary, #2563eb);
    background: rgba(37,99,235,0.08);
}
.exp-my-btn .mono {
    font-variant-numeric: tabular-nums;
    color: var(--text);
    font-weight: 700;
}
.exp-total-block {
    text-align: right;
    min-width: 0;
}
.exp-total {
    font-size: 22px;
    font-weight: 700;
    color: var(--text);
    font-variant-numeric: tabular-nums;
}

/* Add-expense form grid — description wide, then amount+date, then note wide */
.exp-add-card { background: var(--gray-50, #f9fafb); }
[data-theme="dark"] .exp-add-card { background: rgba(255,255,255,0.03); }
.exp-form-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 10px 12px;
}
.exp-form-grid label {
    display: flex;
    flex-direction: column;
    gap: 4px;
    font-size: 11px;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.03em;
    font-weight: 600;
    min-width: 0;
}
.exp-form-grid input,
.exp-form-grid select,
.exp-form-grid textarea {
    min-height: 42px;
    padding: 8px 10px;
    font-size: 14px;
    text-transform: none;
    letter-spacing: normal;
    font-weight: 400;
    color: var(--text);
    width: 100%;
    box-sizing: border-box;
    font-family: inherit;
    border: 1px solid var(--border);
    border-radius: 8px;
    background: var(--surface);
}
.exp-form-grid textarea {
    resize: vertical;
    min-height: 60px;
    line-height: 1.4;
}
.exp-form-grid input:focus,
.exp-form-grid select:focus,
.exp-form-grid textarea:focus {
    outline: none;
    border-color: var(--primary, #2563eb);
    box-shadow: 0 0 0 3px rgba(37,99,235,0.15);
}
.exp-desc-cell, .exp-note-cell { grid-column: 1 / -1; }

/* Receipt picker — a full-width row inside the expense form grid. Not a
   <label> like its siblings because it wraps a button + filename + Remove,
   and a label wrapping a button swallows the button's clicks. */
.exp-receipt-cell {
    display: flex;
    flex-direction: column;
    gap: 4px;
    min-width: 0;
}
.exp-receipt-label {
    font-size: 11px;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.03em;
    font-weight: 600;
}
.exp-receipt-row {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
    min-width: 0;
}
.exp-receipt-pick {
    /* It's a <label> acting as the file-input trigger. */
    cursor: pointer;
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    gap: 6px;
}
.exp-receipt-row #exp-receipt-name {
    flex: 1 1 auto;
    min-width: 0;
    overflow-wrap: anywhere;
}
/* Receipt link on a saved expense row. */
.exp-receipt-chip {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    margin-left: 8px;
    max-width: 100%;
    padding: 1px 7px;
    border: 1px solid var(--border);
    border-radius: 999px;
    background: var(--surface-2, #f1f5f9);
    color: var(--primary, #2563eb);
    font-size: 11px;
    text-decoration: none;
    overflow-wrap: anywhere;
}
.exp-receipt-chip:hover { border-color: var(--primary, #2563eb); }

/* Participant picker — grid of checkbox chips */
.exp-parts-head {
    display: flex;
    align-items: center;
    gap: 8px 12px;
    flex-wrap: wrap;
    margin-bottom: 6px;
}
.exp-parts-head > :first-child { flex: 0 0 auto; }
.exp-parts-head #exp-parts-hint { flex: 1 1 auto; min-width: 0; }
.exp-parts-head .exp-selectall-btn {
    flex: 0 0 auto;
    min-height: 28px;
    padding: 4px 12px;
    font-size: 12px;
    font-weight: 600;
}
.exp-participants {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
    gap: 8px;
    margin-top: 6px;
}
.exp-part-chip {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 10px;
    border: 1px solid var(--border);
    border-radius: 10px;
    background: var(--surface);
    cursor: pointer;
    transition: border-color .1s ease, background .1s ease;
    min-width: 0;
}
.exp-part-chip:hover { border-color: var(--primary, #2563eb); }
.exp-part-chip.is-selected {
    border-color: var(--primary, #2563eb);
    background: rgba(37,99,235,0.06);
}
[data-theme="dark"] .exp-part-chip.is-selected {
    background: rgba(37,99,235,0.14);
}
.exp-part-chip input[type="checkbox"] {
    flex: 0 0 auto;
    width: 18px;
    height: 18px;
    cursor: pointer;
}
.exp-part-chip .wp-avatar {
    flex: 0 0 32px;
    width: 32px;
    height: 32px;
    border-radius: 50%;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    color: white;
    font-weight: 700;
    font-size: 12px;
    object-fit: cover;
}
.exp-part-id { min-width: 0; flex: 1 1 auto; }
.exp-part-id strong {
    display: block;
    font-size: 13px;
    line-height: 1.3;
    color: var(--text);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    text-transform: none;
    letter-spacing: normal;
}

/* Split preview */
.exp-split-preview {
    margin: 10px 0 4px;
    padding: 8px 12px;
    background: var(--surface);
    border: 1px dashed var(--border);
    border-radius: 8px;
    color: var(--text);
    font-size: 13px;
    font-variant-numeric: tabular-nums;
}
.exp-split-preview:empty { display: none; }

.exp-add-actions {
    display: flex;
    justify-content: flex-end;
    gap: 8px;
    margin-top: 10px;
}
.exp-add-actions .btn { min-height: 38px; }
@media (max-width: 480px) {
    .exp-form-grid { grid-template-columns: 1fr; }
    .exp-add-actions { flex-direction: column-reverse; }
    .exp-add-actions .btn { width: 100%; min-height: 44px; }
}

/* Expense history cards inside the modal */
.exp-cards {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 8px;
}
.exp-card, .exp-card:hover, .exp-card:focus-visible { transform: none; }
.exp-cards .exp-card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 12px;
    padding: 12px 14px;
    box-shadow: 0 1px 2px rgba(0,0,0,0.03);
    cursor: default;
    transition: none;
    display: flex;
    flex-direction: column;
    gap: 6px;
}
.exp-cards .exp-card:hover { transform: none; box-shadow: 0 1px 2px rgba(0,0,0,0.03); border-color: var(--border); }
.exp-row-top {
    display: flex;
    align-items: center;
    gap: 10px;
    min-width: 0;
}
.exp-row-id { flex: 1 1 auto; min-width: 0; }
.exp-row-id strong {
    display: block;
    font-size: 14px;
    line-height: 1.35;
    color: var(--text);
}
.exp-row-amt {
    font-size: 15px;
    font-weight: 700;
    color: var(--text);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}
.exp-row-note {
    padding: 6px 10px;
    background: var(--gray-50, #f9fafb);
    border-left: 3px solid var(--border);
    border-radius: 4px;
    font-size: 13px;
    line-height: 1.4;
    color: var(--text);
}
[data-theme="dark"] .exp-row-note { background: rgba(255,255,255,0.03); }

/* Stock — search bar + last-updated line on each item card.
   Search sticks to the top of the item column so it's always reachable
   after scrolling long inventory lists. */
.stock-search-wrap {
    position: sticky;
    top: 0;
    z-index: 3;
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 6px 0 10px;
    background: var(--bg);
}
.stock-search-wrap .admin-search {
    flex: 1 1 auto;
    min-width: 0;
    height: 40px;
    padding: 0 12px;
    font-size: 14px;
    border: 1px solid var(--border);
    border-radius: 8px;
    background: var(--surface);
    color: var(--text);
}
.stock-search-wrap .admin-search:focus {
    outline: none;
    border-color: var(--primary, #2563eb);
    box-shadow: 0 0 0 3px rgba(37,99,235,0.15);
}
.stock-search-count { flex: 0 0 auto; white-space: nowrap; }

/* "Updated 2h ago by Kumar" line under the item name */
.stock-updated {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    margin-top: 4px;
    font-size: 11px;
    line-height: 1.35;
    opacity: 0.85;
}
.stock-updated svg { flex: 0 0 auto; opacity: 0.7; }

/* Payment-timing segmented toggle (Regular / Advance) used inside the "Make
   payment" form and inline-edit row. Two-button segment reads more clearly
   than a checkbox because both states are visible at once. */
.wp-timing {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 10px;
    margin-bottom: 10px;
    background: rgba(37,99,235,0.05);
    border: 1px solid var(--border);
    border-radius: 8px;
}
[data-theme="dark"] .wp-timing { background: rgba(37,99,235,0.10); }
.wp-timing-label {
    font-size: 12px;
    color: var(--muted, #64748b);
    flex: 0 0 auto;
}
.seg {
    display: inline-flex;
    border: 1px solid var(--border);
    border-radius: 6px;
    overflow: hidden;
    background: var(--surface);
}
.seg-btn {
    appearance: none;
    background: transparent;
    color: var(--text);
    border: none;
    padding: 6px 14px;
    font-size: 12px;
    font-weight: 500;
    cursor: pointer;
    line-height: 1.3;
    transition: background 0.12s, color 0.12s;
}
.seg-btn:hover { background: rgba(0,0,0,0.04); }
[data-theme="dark"] .seg-btn:hover { background: rgba(255,255,255,0.04); }
.seg-btn.active {
    background: var(--primary, #2563eb);
    color: #fff;
}
.seg-btn + .seg-btn { border-left: 1px solid var(--border); }
.seg-btn.active + .seg-btn { border-left-color: var(--primary, #2563eb); }
.seg-btn.active + .seg-btn { /* keep separator visible after the active btn */ }

/* Compact segment inside the inline-edit grid — fits the wph-field cadence. */
.wph-edit-timing .seg-btn { padding: 4px 10px; font-size: 11px; }

/* "Advance" pill shown next to the kind pill on payment history rows.
   Warm amber to signal "money moving ahead of billing" without competing
   with the kind-pill colour scheme. */
.wp-advance-pill {
    background: #fef3c7;
    color: #92400e;
    border: 1px solid #fde68a;
    font-weight: 600;
    letter-spacing: 0.02em;
    text-transform: uppercase;
    font-size: 10px;
    padding: 2px 8px;
}
[data-theme="dark"] .wp-advance-pill {
    background: rgba(146,64,14,0.24);
    color: #fde68a;
    border-color: rgba(253,230,138,0.35);
}

/* ---------- Quote generator: drag handle + drop hints + preset picker ---------- */
.qgen-line-drag {
    background: transparent;
    border: none;
    color: var(--text-muted);
    cursor: grab;
    font-size: 15px;
    line-height: 1;
    padding: 6px 2px;
    border-radius: 4px;
    align-self: start;
    margin-top: 4px;
}
.qgen-line-drag:hover { background: var(--gray-50); color: var(--text); }
.qgen-line-drag:active { cursor: grabbing; }
.qgen-line-dragging   { opacity: 0.55; }
.qgen-line.qgen-drop-above { box-shadow: inset 0 3px 0 0 var(--brand, #2563eb); }
.qgen-line.qgen-drop-below { box-shadow: inset 0 -3px 0 0 var(--brand, #2563eb); }

/* Preset picker overlay */
.qgen-preset-picker {
    position: fixed;
    inset: 0;
    background: rgba(15, 23, 42, 0.45);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 10050;
    padding: 20px;
}
.qgen-preset-picker-body {
    background: var(--surface, #fff);
    border-radius: 12px;
    box-shadow: 0 20px 40px rgba(0,0,0,0.25);
    width: 100%;
    max-width: 560px;
    max-height: 80vh;
    display: flex;
    flex-direction: column;
    overflow: hidden;
}
.qgen-preset-picker-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 14px 16px;
    border-bottom: 1px solid var(--border);
}
.qgen-preset-picker-list {
    padding: 6px 8px 10px;
    overflow-y: auto;
    display: flex;
    flex-direction: column;
    gap: 4px;
}
.qgen-preset-picker-item {
    text-align: left;
    background: transparent;
    border: 1px solid transparent;
    padding: 10px 12px;
    border-radius: 8px;
    cursor: pointer;
    display: grid;
    grid-template-columns: 1fr auto;
    grid-template-rows: auto auto;
    gap: 2px 8px;
}
.qgen-preset-picker-item:hover {
    background: var(--gray-50);
    border-color: var(--border);
}
.qgen-preset-picker-title { font-weight: 600; color: var(--text); font-size: 14px; grid-column: 1; grid-row: 1; }
.qgen-preset-picker-size  { grid-column: 2; grid-row: 1; text-align: right; }
.qgen-preset-picker-body-text {
    grid-column: 1 / -1;
    grid-row: 2;
    line-height: 1.4;
    white-space: pre-wrap;
    word-break: break-word;
}

/* ===== Finance export panel (bills.php, mgmt only) =====
   Period picker + Excel / PDF buttons. Flex row that wraps to a stack on a
   phone; each control keeps a sensible minimum so the month/date inputs never
   get squeezed into unreadable ellipses. */
.fx-head {
    display:flex; align-items:flex-start; justify-content:space-between;
    gap:12px; flex-wrap:wrap; margin-bottom:12px;
}
.fx-hint {
    background:#eff6ff; color:#1e40af; border:1px solid #bfdbfe;
    border-radius:999px; padding:3px 10px; white-space:nowrap;
}
.fx-controls { display:flex; align-items:flex-end; gap:10px; flex-wrap:wrap; }
/* Wrapper around a mode's inputs — itself a flex row so quarter (2 selects)
   and custom range (2 dates) sit side by side. */
.fx-fields { display:flex; align-items:flex-end; gap:10px; flex-wrap:wrap; }
.fx-fields[hidden] { display:none; }
.fx-field { display:flex; flex-direction:column; gap:3px; min-width:132px; }
.fx-field select, .fx-field input { width:100%; }
/* Buttons push to the far end on a wide row, but sit inline once wrapped. */
.fx-actions { display:flex; gap:8px; margin-left:auto; flex-wrap:wrap; }
.fx-foot { margin:12px 0 0; }
@media (max-width: 560px) {
    .fx-controls, .fx-fields { flex-direction:column; align-items:stretch; }
    .fx-field { min-width:0; }
    /* margin-left:auto is meaningless in a column, and full-width buttons are
       a much better tap target on a phone. */
    .fx-actions { margin-left:0; }
    .fx-actions .btn { flex:1; justify-content:center; }
}
[data-theme="dark"] .fx-hint { background:#1e3a5f; color:#bfdbfe; border-color:#1e40af; }

/* ===== Cabin Payments (bills.php summary card + cabins_payment.php) =====
   Receivables per cabin order. Grid/flex cards throughout — no tables, so the
   money columns stack cleanly on a phone instead of scrolling sideways. */
.cp-card { display:block; text-decoration:none; color:inherit; }
.cp-tiles {
    display:grid; grid-template-columns:repeat(auto-fit, minmax(150px, 1fr));
    gap:10px; margin-top:12px;
}
.cp-tiles-lg { grid-template-columns:repeat(auto-fit, minmax(170px, 1fr)); }
.cp-tile {
    background:var(--bg-soft, #f8fafc); border:1px solid var(--border);
    border-radius:8px; padding:10px 12px;
}
.cp-tile-in  { background:#f0fdf4; border-color:#bbf7d0; }
.cp-tile-due { background:#fef2f2; border-color:#fecaca; }
.cp-amt { font-size:19px; font-weight:700; line-height:1.25; margin-top:2px; }
.cp-tile-in  .cp-amt { color:#166534; }
.cp-tile-due .cp-amt { color:#991b1b; }
.cp-statusline { display:flex; gap:6px; flex-wrap:wrap; margin-top:10px; }

/* --- order rows --- */
.cp-order { border:1px solid var(--border); border-radius:8px; margin-bottom:8px; background:#fff; }
.cp-order[open] { border-color:#c7d2fe; box-shadow:0 1px 3px rgba(0,0,0,.06); }
.cp-order-head {
    display:flex; justify-content:space-between; align-items:center; gap:12px;
    padding:10px 12px; cursor:pointer; flex-wrap:wrap;
}
.cp-order-head::-webkit-details-marker { display:none; }
.cp-order-head:hover { background:#f9fafb; }
.cp-order-main { min-width:0; flex:1 1 260px; }
.cp-order-title { display:flex; align-items:center; gap:6px; flex-wrap:wrap; font-weight:600; }
.cp-order-name { overflow-wrap:anywhere; }
.cp-order-sub { margin-top:3px; }
.cp-batch-pill {
    background:#fef3c7; color:#92400e; border:1px solid #fde68a;
    font-size:11px; font-weight:600; padding:1px 8px; border-radius:999px;
}
.cp-order-money { display:flex; align-items:center; gap:16px; flex-wrap:wrap; }
.cp-money-col { text-align:right; min-width:88px; }
.cp-money-col .mono { font-weight:600; }

.cp-order-body { padding:0 12px 14px; border-top:1px dashed var(--border); }
.cp-bar { height:6px; border-radius:999px; background:#e5e7eb; overflow:hidden; margin:12px 0 14px; }
.cp-bar > span { display:block; height:100%; background:#16a34a; }
.cp-detail-cols { display:grid; grid-template-columns:repeat(auto-fit, minmax(260px, 1fr)); gap:18px; }
.cp-h4 { margin:0 0 8px; font-size:13px; display:flex; align-items:center; gap:6px; }

.cp-cabins { display:flex; flex-direction:column; gap:6px; }
.cp-cabin { display:flex; align-items:center; gap:10px; background:#f8fafc;
            border:1px solid var(--border); border-radius:6px; padding:6px 10px; }
.cp-cabin-n {
    flex:0 0 22px; height:22px; border-radius:50%; background:#e0e7ff; color:#3730a3;
    font-size:11px; font-weight:700; display:flex; align-items:center; justify-content:center;
}
.cp-cabin-info { min-width:0; }
.cp-cabin-label { font-size:13px; font-weight:600; }

.cp-pays { display:flex; flex-direction:column; gap:6px; }
.cp-pay { background:#f0fdf4; border:1px solid #bbf7d0; border-radius:6px; padding:6px 10px; }
.cp-pay-top { display:flex; align-items:center; gap:8px; justify-content:space-between; }
.cp-pay-amt { font-weight:700; color:#166534; }
.cp-mode { background:#dcfce7; color:#166534; border:1px solid #bbf7d0; padding:1px 6px; }

@media (max-width: 640px) {
    /* Money columns go left-aligned and wrap under the title rather than
       squeezing into a right-hand rail. */
    .cp-order-money { width:100%; gap:12px; justify-content:space-between; }
    .cp-money-col { text-align:left; min-width:76px; }
    .cp-amt { font-size:17px; }
}

/* Unconfirmed pipeline — quoted but pre-Order-Confirmed. Deliberately muted and
   visually separated so it never reads as money the client owes us. */
.cp-pipeline { margin-top:10px; padding-top:8px; border-top:1px dashed var(--border); }
.cp-pipeline-box {
    display:flex; justify-content:space-between; align-items:center; gap:12px; flex-wrap:wrap;
    margin-top:12px; padding:10px 12px; border-radius:8px;
    background:#f8fafc; border:1px dashed #cbd5e1;
}
.cp-pipe-amt { font-size:17px; font-weight:700; color:#64748b; }
.cp-unconf-pill {
    background:#f1f5f9; color:#475569; border:1px dashed #cbd5e1;
    font-size:11px; font-weight:600; padding:1px 8px; border-radius:999px;
}
.cp-scope-label { margin-bottom:10px; padding-bottom:8px; border-bottom:1px solid var(--border); }

/* ===== Topbar overflow fix (desktop 901–1200px band) =====
   The ≤900px breakpoint hides the nav / user-name / install button, and wide
   desktops have room to spare — but in between, brand (170px logo) + 7 nav
   links + install button + 3 icons + user name exceeded the viewport and
   pushed the avatar and logout icon off-screen.

   Three causes, all fixed here:
     1. .topbar-actions was shrinkable, so the icons were the first thing
        squeezed out. They're the least optional part of the bar — pin them.
     2. .topbar-nav had `flex: 1` with no `min-width: 0`, so it could not
        shrink below its content width and forced the row wider.
     3. .user-name had no truncation, so a two-word name wrapped to two lines
        and shoved the logout icon sideways. */
.brand-link    { flex: 0 0 auto; }
.topbar-nav    { flex: 1 1 auto; min-width: 0; }
.topbar-actions,
.user-menu     { flex: 0 0 auto; }
.user-menu     { min-width: 0; }
.user-name {
    max-width: 140px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Nav labels are never truncated — a bar of "W… Batc… St…" is worse than the
   overlap it was meant to prevent. The row is kept inside the viewport by
   dropping whole optional elements at the breakpoints below instead.
   .nav-link keeps its default min-width: auto so it cannot be squeezed. */

/* Compaction band ceiling is 1279, not 1200: at 1201-1262 the uncompacted bar
   still overflowed by up to ~60px (8 nav links + the Install-app label + the
   user's name), which is exactly where the overlap was reported. 1280 has
   ~40px of slack, so it keeps the full labels. */
@media (min-width: 901px) and (max-width: 1279px) {
    /* Reclaim horizontal space in the tight band without losing any control:
       smaller logo, tighter gaps, and the two purely-decorative bits (the
       user's name and the Install-app label) step aside first. The icon-only
       install button keeps the same click target. */
    .topbar-inner       { padding: 10px 14px; gap: 12px; }
    .topbar-nav         { margin-left: 10px; gap: 2px; }
    .nav-link           { padding: 8px 9px; }
    .brand-logo-img     { height: 44px; max-width: 132px; }
    .brand-name         { display: none; }
    .user-name          { display: none; }
    .install-btn span   { display: none; }
    .user-menu          { gap: 6px; }
}

/* 1280-1365px: the band the reported overlap actually lives in. With the real
   logo the brand block is 243px wide, so at 1280 the nav needs 647px and only
   gets 594 — "Admin" lands on top of the Install-app button. 53px is all we
   need back, and it's cheaper to drop the "TASKS" wordmark and tighten spacing
   than to strip the labels people read: the user's name and "Install app" both
   survive here, unlike in the tighter band below. */
@media (min-width: 1280px) and (max-width: 1365px) {
    .topbar-inner { padding: 10px 16px; gap: 12px; }
    .topbar-nav   { margin-left: 12px; gap: 2px; }
    .nav-link     { padding: 8px 9px; }
    .brand-name   { display: none; }
}

@media (min-width: 901px) and (max-width: 1010px) {
    /* Tightest desktop band — drop the install button entirely (it's still
       reachable from the browser's own address-bar install affordance). */
    .install-btn { display: none; }
    .nav-link    { padding: 8px 7px; }
}

/* ===== Inline option toggle (checkbox + text on one row) =====
   The base `input` rule (width:100%, padding:9px 12px, min-height:38px) is
   written for text fields; applied to a bare checkbox it renders a giant
   full-width square that shoves the label onto the next line. Reset it here,
   same approach as .fin-item-check / .cg-tick. */
.opt-box {
    border: 1px solid #fde68a; border-radius: 8px;
    padding: 9px 11px; background: #fffbeb; margin: 0 0 10px;
}
.opt-check {
    display: flex; align-items: flex-start; gap: 8px;
    margin: 0; font-weight: 500; cursor: pointer;
}
.opt-check + .opt-check { margin-top: 7px; }
.opt-check input[type="checkbox"] {
    width: 15px; height: 15px; min-height: 0; padding: 0;
    margin: 2px 0 0; flex: 0 0 15px; cursor: pointer;
}
.opt-check input[type="checkbox"]:disabled { cursor: not-allowed; }
.opt-check-text { font-size: 13px; line-height: 1.35; }
.opt-check-hint {
    display: block; font-weight: 400; font-size: 12px;
    color: var(--text-muted); margin-top: 1px;
}
.opt-note {
    margin-top: 7px; padding-top: 7px;
    border-top: 1px dashed #fde68a; font-size: 12px;
}

/* Quote revision row — INTRINSIC wrapping, no breakpoint guessing.
   Two bugs were stacked here:
     1. The download / Cust. / Mark-final / edit / delete controls were bare
        children of .quote-item, so each competed as its own flex sibling of
        .quote-body. Buttons barely shrink, so the body absorbed every pixel of
        loss and collapsed to one word per line. They're one group now.
     2. .job-detail-canvas keeps a fixed 320px sidebar until 900px, so between
        901px and ~1100px the main column is only ~490-650px wide — too narrow
        for rev + body + actions on one line, but above the 640px mobile stack.
        Rather than add another breakpoint, give .quote-body a flex-basis: when
        it can't get 240px the row wraps and it takes the full line instead. */
.quote-acts {
    display: flex; align-items: center; gap: 5px;
    flex: 0 0 auto; margin-left: auto;
}
.quote-attach { margin: 0; }
/* Inside a file row the pill owns the width, so let the name use it all and
   ellipsis at whatever the row allows rather than a fixed cap. */
.quote-file .quote-attach-name { max-width: none; flex: 1 1 auto; }

/* Batch provenance line on an independent unit card — the unit's own name is
   the title, this shows which batch order it belongs to. */
/* ID badge + batch chip share one line so an independent unit card is as
   compact as a single-cabin one. */
.job-row-idline {
    display: flex; align-items: center; gap: 8px;
    flex-wrap: wrap; min-width: 0;
}
.job-row-batch {
    display: inline-flex; align-items: center; gap: 4px; min-width: 0;
}
.job-row-batch .mono { opacity: 0.7; font-size: 10.5px; }
/* Long batch names truncate rather than wrapping the row onto another line. */
.job-row-batch-name {
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    max-width: 260px;
}

/* ===== Additional Fittings: Ordered + Delivered flags =====
   Two independent checkboxes per row, so the row is a <div> and each flag owns
   its own <label> (one label wrapping two inputs would toggle only the first).
   Delivery is disabled until Ordered is ticked — an item can be ordered late,
   but never delivered before it was ordered. */
.fit-row { cursor: default; }
.fit-flag {
    display: inline-flex; align-items: center; gap: 6px;
    flex: 0 0 auto; cursor: pointer; margin: 0;
    font-size: 12px; font-weight: 600; color: var(--text-muted);
    white-space: nowrap;
}
.fit-flag input[type="checkbox"] {
    width: 18px; height: 18px; min-height: 0; padding: 0; margin: 0;
    flex: 0 0 18px; cursor: pointer;
}
.fit-flag input[type="checkbox"]:disabled { cursor: not-allowed; opacity: 0.45; }
.fit-flag input:disabled ~ .fit-flag-txt { opacity: 0.5; }
.fit-flag-del { margin-left: auto; }
/* Ordered ticked → its label reads as confirmed. */
.fit-row:not(.not-ordered) .fit-flag:first-child { color: #1e40af; }
.fit-row.done .fit-flag-del { color: #166534; }
/* Un-ordered rows stay neutral-amber so they stand out as the blocker. */
.fit-row.not-ordered { background: #fffbeb; border-color: #fde68a; }

@media (max-width: 560px) {
    /* Stack: flags on their own line under the item name. */
    .fit-row { flex-wrap: wrap; row-gap: 8px; }
    .fit-row .disp-check-label { flex: 1 1 100%; order: 1; }
    .fit-row .fit-flag { order: 2; }
    .fit-flag-del { margin-left: 0; }
    .fit-flag input[type="checkbox"] { width: 22px; height: 22px; flex-basis: 22px; }
}
