/* ============================================================================
   Syncra Portal — theme tokens (light default) + dark overrides
   ============================================================================ */
:root {
    --bg:          #F5F7FA;
    --bg-2:        #ECEFF4;
    --surface:     #FFFFFF;
    --surface-2:   #F8FAFC;
    --border:      #E2E8F0;
    --text:        #0A0E1A;
    --muted:       #64748B;
    --teal:        #00D4C8;
    --teal-ink:    #0A0E1A;   /* text on a teal button */
    --teal-tint:   rgba(0, 212, 200, 0.12);
    --success:     #10B981;
    --warning:     #F59E0B;
    --danger:      #EF4444;
    --info:        #3B82F6;
    --purple:      #A855F7;
    /* Softer and shorter than before: a card should look like it's resting on the page, not
       hovering above it. Depth comes from the hairline border, the shadow only grounds it. */
    --shadow:      0 1px 2px rgba(16, 24, 40, 0.03), 0 6px 20px rgba(16, 24, 40, 0.05);
    --shadow-lg:   0 8px 32px rgba(16, 24, 40, 0.12);
    --shadow-hover:0 2px 4px rgba(16, 24, 40, 0.04), 0 12px 28px rgba(16, 24, 40, 0.09);
    --radius:      16px;
    --radius-sm:   12px;
    --sidebar-w:   268px;
    /* The two chrome panels. Translucent in light mode so the backdrop runs edge to edge under
       the whole window rather than being boxed into the content area. */
    --panel:       rgba(255, 255, 255, 0.72);
    --panel-top:   rgba(255, 255, 255, 0.62);
}

/* The dark surfaces used to be navy (#0A0E1A / #141B2D). The dark backdrop artwork is deep teal,
   and navy cards sitting on it read as two different products stitched together — so the whole
   ramp is rotated onto the artwork's hue. Every lightness step is the same as it was; only the
   hue moved, which is why nothing else in the dark theme needed touching. */
html[data-theme="dark"] {
    --bg:          #00141C;
    --bg-2:        #04202B;
    --surface:     #072A36;
    --surface-2:   #04212C;
    --border:      #12414F;
    --text:        #F2F9FB;
    --muted:       #8AA6B0;
    --teal-tint:   rgba(0, 212, 200, 0.16);
    --shadow:      0 1px 2px rgba(0, 0, 0, 0.3), 0 4px 16px rgba(0, 0, 0, 0.35);
    --shadow-lg:   0 8px 32px rgba(0, 0, 0, 0.5);
    --shadow-hover:0 2px 6px rgba(0, 0, 0, 0.35), 0 14px 34px rgba(0, 0, 0, 0.45);
    /* Same trick as light mode: translucent chrome so the backdrop runs edge to edge instead of
       being boxed into the content area. */
    --panel:       rgba(0, 26, 36, 0.72);
    --panel-top:   rgba(0, 20, 28, 0.68);
}

* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
body {
    font-family: 'Inter', system-ui, -apple-system, sans-serif;
    background: var(--bg);
    color: var(--text);
    -webkit-font-smoothing: antialiased;
    /* 14px read as a site rendered at 80% zoom. One point up lifts every page without any
       layout having to change. */
    font-size: 15px;
    line-height: 1.5;
}
/* The branded backdrop, behind the whole portal. Fixed rather than scrolling: the artwork is one
   image sized to the window, so a long invoices table scrolls over it instead of dragging it along
   and stretching it down the document. Light theme only — the artwork is a pale blue wash and
   there is no version of it that belongs behind a dark UI. */
html[data-theme="light"] body,
html:not([data-theme="dark"]) body {
    background: #E8F6FA url("../img/app-bg.webp") center / cover no-repeat fixed;
}
html[data-theme="dark"] body {
    background: #001C27 url("../img/app-bg-dark.webp") center / cover no-repeat fixed;
}
a { color: inherit; text-decoration: none; }
h1, h2, h3 { margin: 0; font-weight: 800; letter-spacing: -0.02em; }

/* ── Icons ────────────────────────────────────────────────────────────────
   Every icon in the portal is a stroked path on currentColor at one of two sizes. They inherit
   colour from whatever they sit in, which is what stops the navigation turning back into a
   fifteen-colour rainbow. */
.icon { display: inline-flex; flex-shrink: 0; }
.icon svg { width: var(--icon-size, 20px); height: var(--icon-size, 20px); display: block; }
.muted-icon { color: var(--muted); }

/* ── Boot / loading ──────────────────────────────────────────────────────── */
.app-loading {
    position: fixed; inset: 0; display: flex; flex-direction: column;
    align-items: center; justify-content: center; gap: 22px;
    /* Transparent so the boot screen sits on the backdrop too — otherwise the app opens on a
       flat grey rectangle and then changes colour once Blazor takes over. */
    background: transparent;
}
.app-loading-brand { font-weight: 900; letter-spacing: 5px; color: var(--teal); font-size: 22px; }
.spinner {
    width: 34px; height: 34px; border-radius: 50%;
    border: 3px solid var(--border); border-top-color: var(--teal);
    animation: spin 0.7s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

#blazor-error-ui {
    display: none; position: fixed; bottom: 0; left: 0; right: 0; z-index: 1000;
    padding: 12px 20px; background: var(--danger); color: #fff; font-size: 13px;
}
#blazor-error-ui .reload { color: #fff; text-decoration: underline; margin-left: 8px; }
#blazor-error-ui .dismiss { cursor: pointer; float: right; }

/* ============================================================================
   App shell
   ============================================================================ */
.shell { display: flex; min-height: 100vh; }

.sidebar {
    width: var(--sidebar-w); flex-shrink: 0; background: var(--panel);
    -webkit-backdrop-filter: blur(20px); backdrop-filter: blur(20px);
    border-right: 1px solid var(--border); position: fixed; top: 0; bottom: 0; left: 0;
    display: flex; flex-direction: column; padding: 22px 16px; z-index: 20;
}
.brand { display: flex; align-items: center; gap: 12px; padding: 4px 10px 24px; }
.brand-mark { width: 30px; height: 38px; object-fit: contain; }
.brand-name { font-weight: 900; letter-spacing: 5px; font-size: 21px; color: var(--text); line-height: 1.1; }
.brand-tag { color: var(--teal); font-size: 9px; letter-spacing: 1.6px; margin-top: 3px; }

/* Section headings. Sixteen links with no headings is a wall; the same links under three
   headings is a menu you can skim. */
.nav-group {
    font-size: 10.5px; font-weight: 800; letter-spacing: 1.2px; text-transform: uppercase;
    color: var(--muted); opacity: .75; padding: 0 12px; margin: 18px 0 6px;
}
.nav-group:first-child { margin-top: 0; }

.nav {
    display: flex; flex-direction: column; gap: 4px;
    /* The sidebar is position:fixed, so any nav item past the bottom of the viewport cannot be
       reached — scrolling the page doesn't move it. With 16 items that put Analytics, Team and
       Settings out of reach entirely on a 1366x768 laptop, which is ~650px of viewport once
       browser chrome is taken off. min-height:0 is required: without it a flex child refuses to
       shrink below its content height and never scrolls. */
    flex: 1 1 auto; min-height: 0; overflow-y: auto;
}
.nav-item {
    position: relative; display: flex; align-items: center; gap: 12px;
    padding: 10px 12px; border-radius: var(--radius-sm);
    color: var(--muted); font-weight: 600; font-size: 14.5px; cursor: pointer;
    transition: background .16s ease, color .16s ease;
}
.nav-item:hover { background: var(--surface-2); color: var(--text); }
.nav-item.active { background: var(--teal-tint); color: var(--teal); }
/* The indicator is always in the DOM and scales from nothing, so moving between items reads as
   the bar growing rather than a block appearing. */
.nav-item::before {
    content: ""; position: absolute; left: 0; top: 50%; width: 3px; height: 20px;
    border-radius: 0 3px 3px 0; background: var(--teal);
    transform: translateY(-50%) scaleY(0); transform-origin: center;
    transition: transform .18s ease;
}
.nav-item.active::before { transform: translateY(-50%) scaleY(1); }

.sidebar-foot { margin-top: auto; padding-top: 10px; }
.sidebar-user {
    display: flex; align-items: center; gap: 11px; margin-top: 8px; padding: 10px 12px;
    border-top: 1px solid var(--border); border-radius: 0 0 var(--radius-sm) var(--radius-sm);
    transition: background .16s ease;
}
.sidebar-user:hover { background: var(--surface-2); }
.sidebar-user .avatar { width: 36px; height: 36px; font-size: 13px; }
.sidebar-user .who { min-width: 0; flex: 1; }
.sidebar-user .name { display: block; font-weight: 700; font-size: 13.5px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sidebar-user .role { display: block; color: var(--muted); font-size: 12px; text-transform: capitalize; }

.main { flex: 1; margin-left: var(--sidebar-w); display: flex; flex-direction: column; min-width: 0; }

.topbar {
    height: 68px; display: flex; align-items: center; justify-content: space-between;
    padding: 0 32px; border-bottom: 1px solid var(--border);
    background: var(--panel-top);
    position: sticky; top: 0; z-index: 10;
    -webkit-backdrop-filter: blur(14px); backdrop-filter: blur(14px);
}
.topbar h1 { font-size: 20px; }
.topbar-actions { display: flex; align-items: center; gap: 10px; }

.icon-btn {
    position: relative;
    width: 40px; height: 40px; border-radius: var(--radius-sm); border: 1px solid var(--border);
    background: var(--surface); color: var(--muted); cursor: pointer;
    display: inline-flex; align-items: center; justify-content: center;
    transition: border-color .14s ease, color .14s ease, background .14s ease;
}
.icon-btn:hover { border-color: var(--teal); color: var(--teal); background: var(--teal-tint); }
.icon-btn .badge {
    position: absolute; top: -3px; right: -3px; background: var(--danger); color: #fff;
    border-radius: 999px; font-size: 10px; font-weight: 800; line-height: 16px;
    min-width: 16px; height: 16px; text-align: center; padding: 0 4px;
    border: 2px solid var(--bg);
}

/* Wider than the old 1280: five KPI cards and a two-column body need the room, and anything
   past ~1500 stops being scannable in one look. */
.content { padding: 32px; max-width: 1500px; width: 100%; }

/* ============================================================================
   Cards, stats, tables
   ============================================================================ */
.card {
    background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
    box-shadow: var(--shadow);
}
.card-pad { padding: 24px 26px; }
@media (max-width: 560px) { .card-pad { padding: 18px; } }

.stat-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); gap: 16px; }
.stat {
    background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
    padding: 18px 20px; box-shadow: var(--shadow);
}
.stat .label { color: var(--muted); font-size: 11px; font-weight: 700; letter-spacing: .5px; text-transform: uppercase; }
.stat .value { font-size: 26px; font-weight: 800; margin-top: 6px; }
.stat .trend { font-size: 12px; font-weight: 700; margin-top: 6px; }
.stat.accent .value { color: var(--teal); }
.stat.danger { border-color: color-mix(in srgb, var(--danger) 35%, var(--border)); }
.stat.danger .value, .stat.danger .label { color: var(--danger); }
.up { color: var(--success); } .down { color: var(--danger); }

.section-title { font-size: 16px; font-weight: 800; margin: 32px 0 14px; }
.grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; }
@media (max-width: 900px) { .grid-2 { grid-template-columns: 1fr; } }

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

/* ── Page header ──────────────────────────────────────────────────────────
   The greeting sits on a barely-there Syncra mark. At 3% it isn't decoration you notice, it's
   the reason the page doesn't look like it could belong to any other product. */
.page-head {
    position: relative; overflow: hidden;
    display: flex; justify-content: space-between; align-items: flex-start; gap: 24px;
    flex-wrap: wrap; margin-bottom: 28px;
}
/* No watermark behind the greeting in either theme now: both backdrops carry the mark already, and
   a second one a few hundred pixels away is just the same shape twice. syncra-mark.png is still
   the sidebar brand. */
.page-head-text h1 { font-size: 34px; line-height: 1.15; }
.page-head-text p { margin: 8px 0 0; color: var(--muted); font-size: 15px; }
.page-head-side { position: relative; z-index: 1; display: flex; flex-direction: column; align-items: flex-end; gap: 14px; }
.page-head-actions { display: flex; gap: 10px; flex-wrap: wrap; }
.today { display: flex; align-items: center; gap: 8px; color: var(--muted); font-size: 13.5px; font-weight: 600; }
@media (max-width: 720px) {
    .page-head-text h1 { font-size: 27px; }
    .page-head-side { align-items: flex-start; }
    .page-head::after { display: none; }
}

/* ── KPI cards ────────────────────────────────────────────────────────────
   Value first, then what it is, then the one extra fact that makes it actionable. No trend
   chips: the dashboard endpoint returns a hardcoded zero for month-on-month movement, and a
   card that says "no change" when nothing was measured is worse than a card that says nothing. */
.kpi-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(212px, 1fr)); gap: 20px; }
.kpi {
    background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
    padding: 22px; box-shadow: var(--shadow);
    transition: transform .16s ease, box-shadow .16s ease, border-color .16s ease;
}
.kpi:hover { transform: translateY(-2px); box-shadow: var(--shadow-hover); border-color: color-mix(in srgb, var(--teal) 30%, var(--border)); }
@media (prefers-reduced-motion: reduce) { .kpi:hover { transform: none; } }
.kpi-ico {
    display: inline-flex; align-items: center; justify-content: center;
    width: 42px; height: 42px; border-radius: var(--radius-sm); margin-bottom: 16px;
}
.kpi-ico.teal    { background: var(--teal-tint);                                  color: var(--teal); }
.kpi-ico.info    { background: color-mix(in srgb, var(--info) 12%, transparent);    color: var(--info); }
.kpi-ico.success { background: color-mix(in srgb, var(--success) 12%, transparent); color: var(--success); }
.kpi-ico.warning { background: color-mix(in srgb, var(--warning) 14%, transparent); color: var(--warning); }
.kpi-ico.danger  { background: color-mix(in srgb, var(--danger) 12%, transparent);  color: var(--danger); }
.kpi-ico.muted   { background: var(--surface-2);                                    color: var(--muted); }
.kpi-value { font-size: 28px; font-weight: 800; letter-spacing: -0.02em; font-variant-numeric: tabular-nums; }
.kpi-label { color: var(--muted); font-size: 13.5px; font-weight: 600; margin-top: 2px; }
.kpi-sub { font-size: 12.5px; color: var(--muted); margin-top: 10px; font-weight: 600; }
.kpi-sub.is-danger { color: var(--danger); }

/* ── Card headers + interactive cards ─────────────────────────────────────── */
.dash-grid { display: grid; grid-template-columns: 1.6fr 1fr; gap: 20px; margin-top: 20px; }
.dash-grid + .grid-2 { margin-top: 20px; }
@media (max-width: 1080px) { .dash-grid { grid-template-columns: 1fr; } }

.card-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 18px; }
.card-head h2 { font-size: 17px; }
.card-link { display: inline-flex; align-items: center; gap: 4px; color: var(--teal); font-size: 13px; font-weight: 700; }
.card-link:hover { text-decoration: underline; }

/* ── Quote pipeline ───────────────────────────────────────────────────────
   Four stages left to right with the count under each, so the stage where work stops moving is
   the one you spot first. A stage with nothing in it goes grey rather than coloured. */
.pipeline { display: flex; align-items: flex-start; list-style: none; margin: 0; padding: 0; }
.stage { flex: 1; display: flex; flex-direction: column; align-items: center; gap: 6px; position: relative; text-align: center; }
.stage + .stage::before {
    content: ""; position: absolute; top: 23px; right: 50%; left: -50%; height: 1px;
    background: var(--border); z-index: 0;
}
.stage-ico {
    position: relative; z-index: 1;
    display: inline-flex; align-items: center; justify-content: center;
    width: 46px; height: 46px; border-radius: 50%;
    background: var(--surface-2); color: var(--muted); border: 1px solid var(--border);
}
.stage.info .stage-ico    { background: color-mix(in srgb, var(--info) 12%, transparent);    color: var(--info);    border-color: color-mix(in srgb, var(--info) 25%, transparent); }
.stage.success .stage-ico { background: color-mix(in srgb, var(--success) 12%, transparent); color: var(--success); border-color: color-mix(in srgb, var(--success) 25%, transparent); }
.stage.warning .stage-ico { background: color-mix(in srgb, var(--warning) 14%, transparent); color: var(--warning); border-color: color-mix(in srgb, var(--warning) 28%, transparent); }
.stage.teal .stage-ico    { background: var(--teal-tint);                                    color: var(--teal);    border-color: color-mix(in srgb, var(--teal) 28%, transparent); }
.stage-label { font-size: 12.5px; color: var(--muted); font-weight: 600; }
.stage-count { font-size: 20px; font-weight: 800; font-variant-numeric: tabular-nums; }
.stage.is-empty .stage-count { color: var(--muted); }

.pipeline-foot { margin-top: 22px; padding-top: 20px; border-top: 1px solid var(--border); }
.rate-value { font-size: 26px; font-weight: 800; letter-spacing: -0.02em; }
.rate-label { color: var(--muted); font-size: 13px; margin-top: 2px; }
.meter { height: 6px; border-radius: 999px; background: var(--surface-2); overflow: hidden; margin-top: 12px; max-width: 340px; }
.meter > span { display: block; height: 100%; border-radius: 999px; background: var(--teal); transition: width .4s ease; }

/* ── Needs attention ──────────────────────────────────────────────────────── */
.attention { display: flex; flex-direction: column; }
.attn-row {
    display: flex; align-items: center; gap: 13px; padding: 13px 4px;
    border-bottom: 1px solid var(--border); transition: background .14s ease;
}
.attn-row:last-child { border-bottom: 0; }
a.attn-row { cursor: pointer; margin: 0 -8px; padding-left: 12px; padding-right: 12px; border-radius: 10px; }
a.attn-row:hover { background: var(--surface-2); }
.attn-ico {
    display: inline-flex; align-items: center; justify-content: center;
    width: 34px; height: 34px; border-radius: 50%; flex-shrink: 0;
}
.attn-ico.success { background: color-mix(in srgb, var(--success) 12%, transparent); color: var(--success); }
.attn-ico.info    { background: color-mix(in srgb, var(--info) 12%, transparent);    color: var(--info); }
.attn-ico.warning { background: color-mix(in srgb, var(--warning) 14%, transparent); color: var(--warning); }
.attn-ico.danger  { background: color-mix(in srgb, var(--danger) 12%, transparent);  color: var(--danger); }
.attn-text { flex: 1; min-width: 0; }
.attn-text .t { display: block; font-weight: 700; font-size: 14px; }
.attn-text .s { display: block; color: var(--muted); font-size: 12.5px; margin-top: 1px; }
.attn-clear { text-align: center; padding: 26px 0 8px; }
.attn-clear .attn-ico { width: 44px; height: 44px; margin-bottom: 12px; }
.attn-clear .t { font-weight: 800; font-size: 15px; }
.attn-clear .s { color: var(--muted); font-size: 13px; margin-top: 3px; }

/* ── Compact list rows (jobs, quotes) ─────────────────────────────────────── */
.list { list-style: none; margin: 0; padding: 0; }
.list-row {
    display: flex; align-items: center; gap: 13px; cursor: pointer;
    padding: 13px 12px; margin: 0 -12px; border-radius: 10px;
    border-bottom: 1px solid var(--border); transition: background .14s ease;
}
.list-row:last-child { border-bottom: 0; }
.list-row:hover { background: var(--surface-2); }
.row-ico {
    display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0;
    width: 34px; height: 34px; border-radius: 10px; background: var(--surface-2); color: var(--muted);
}
.row-text { flex: 1; min-width: 0; }
.row-text .t, .row-end .t { display: block; font-weight: 700; font-size: 14px; }
.row-text .s { display: block; color: var(--muted); font-size: 12.5px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.row-end { text-align: right; display: flex; flex-direction: column; align-items: flex-end; gap: 4px; }
.row-end .s { color: var(--muted); font-size: 12px; }

/* ── Empty states ─────────────────────────────────────────────────────────
   An empty panel should say what will fill it and offer the way to do that, rather than
   leaving "No active jobs." floating on its own. */
.empty { text-align: center; padding: 30px 12px 12px; }
.empty-ico {
    display: inline-flex; align-items: center; justify-content: center;
    width: 52px; height: 52px; border-radius: 50%; background: var(--surface-2);
    color: var(--muted); margin-bottom: 14px;
}
.empty .t { font-weight: 800; font-size: 15px; }
.empty .s { color: var(--muted); font-size: 13px; margin: 4px 0 18px; }

/* ============================================================================
   List screens — toolbar, table trimmings, row menu, pager
   ============================================================================ */
.toolbar {
    display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
    padding: 18px 22px; border-bottom: 1px solid var(--border);
}
.toolbar .search { position: relative; flex: 1 1 240px; min-width: 200px; display: flex; align-items: center; }
.toolbar .search .icon { position: absolute; left: 13px; pointer-events: none; }
.toolbar .search .input { padding-left: 38px; }
/* .input sets width:100% and is declared later in this file, so an unqualified .select-sm loses on
   source order and every filter takes a full row. Qualifying it wins on specificity instead of
   depending on where the rule happens to sit. */
.toolbar .input.select-sm { width: auto; min-width: 140px; padding: 10px 12px; font-size: 13.5px; }
.toolbar .input.select-sm[type="date"] { min-width: 148px; }
.pager .input.select-sm { width: auto; min-width: 136px; padding: 8px 12px; font-size: 13.5px; }

/* Two bare date boxes side by side don't say which end of the range they are. */
.date-filter { display: flex; align-items: center; gap: 7px; color: var(--muted); font-size: 13px; font-weight: 600; }

/* A KPI card that is also a filter. Same card, plus a pressed state so it's obvious which one is
   currently narrowing the list. */
.kpi-btn {
    font-family: inherit; text-align: left; width: 100%; cursor: pointer; color: inherit;
    display: block;
}
.kpi-btn.is-on { border-color: var(--teal); box-shadow: 0 0 0 1px var(--teal) inset, var(--shadow); }

.cell-user { display: inline-flex; align-items: center; gap: 10px; }
.avatar-sm { width: 30px; height: 30px; font-size: 11.5px; }
.tag {
    display: inline-block; padding: 3px 10px; border-radius: 8px; font-size: 12px; font-weight: 600;
    background: var(--surface-2); color: var(--muted); border: 1px solid var(--border);
}
/* Category tints, so a long price list can be read by shape as well as by word. Kept pale — these
   are labels, not statuses. */
.tag-labour    { background: color-mix(in srgb, var(--info) 10%, transparent);    color: var(--info);    border-color: color-mix(in srgb, var(--info) 22%, transparent); }
.tag-material  { background: color-mix(in srgb, var(--success) 10%, transparent); color: var(--success); border-color: color-mix(in srgb, var(--success) 22%, transparent); }
.tag-equipment { background: color-mix(in srgb, var(--purple) 12%, transparent);  color: var(--purple);  border-color: color-mix(in srgb, var(--purple) 24%, transparent); }

.is-success { color: var(--success); }
.is-warning { color: var(--warning); }

/* A table cell that acts like a link but isn't navigation — it filters the list in place. */
.link-cell {
    border: 0; background: none; padding: 0; font: inherit; font-weight: 700;
    color: var(--teal); cursor: pointer; text-align: left;
}
.link-cell:hover { text-decoration: underline; }

/* A reversed purchase stays on the list because the ledger keeps it — it just isn't money out
   any more. */
tr.is-reversed { opacity: .5; }
/* Deactivated, not deleted — still readable, just clearly not current. */
tr.is-dim { opacity: .6; }
tr.is-reversed td { text-decoration: line-through; }

/* Excl / VAT / claimable, read together while the total is being typed. */
.split-note {
    display: flex; gap: 26px; flex-wrap: wrap; margin-top: 4px; padding: 12px 14px;
    background: var(--surface-2); border-radius: var(--radius-sm);
}
.split-note .k { display: block; color: var(--muted); font-size: 11.5px; font-weight: 700; }
.split-note .v { display: block; font-weight: 700; font-size: 15px; margin-top: 2px; }

/* ── Team cards ───────────────────────────────────────────────────────────
   People are the one thing on the portal where cards beat a table: a name, a face's worth of
   initials and two numbers read better than a row. */
.people { display: grid; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); gap: 20px; margin-top: 20px; }
.person { display: flex; flex-direction: column; }
.person-head { display: flex; align-items: flex-start; gap: 14px; }
.person-id { flex: 1; min-width: 0; }
.person-id .t { font-weight: 800; font-size: 16px; }
.person-id .s { color: var(--muted); font-size: 12.5px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.person-tags { display: flex; gap: 6px; margin-top: 8px; flex-wrap: wrap; }
.person-stats { display: flex; gap: 14px; margin-top: 18px; }
.person-stats > div { flex: 1; background: var(--surface-2); border-radius: var(--radius-sm); padding: 12px 14px; }
.person-stats .k { display: flex; align-items: center; gap: 6px; color: var(--muted); font-size: 12px; font-weight: 600; }
.person-stats .v { display: block; font-size: 22px; font-weight: 800; margin-top: 4px; }
.person-actions { display: flex; gap: 10px; margin-top: 16px; }

/* Workload cells: the number, and a bar so a full week is visible without reading every figure. */
.load-n { font-weight: 700; font-variant-numeric: tabular-nums; }
.load-bar { height: 5px; border-radius: 999px; background: var(--surface-2); overflow: hidden; margin-top: 5px; max-width: 70px; }
.load-bar > span { display: block; height: 100%; border-radius: 999px; background: var(--teal); }
th.is-today-col { color: var(--teal); }

/* The open-jobs figure at the end of a workload row is a number you read across the table, not a
   badge — the shared count-chip was sized for a heading and came out too small here. */
.load-chip {
    display: inline-flex; align-items: center; justify-content: center;
    min-width: 34px; height: 30px; padding: 0 10px; border-radius: 10px;
    background: var(--teal-tint); color: var(--teal);
    font-size: 16px; font-weight: 800; font-variant-numeric: tabular-nums;
}

/* ── Assemblies ───────────────────────────────────────────────────────────
   A kit is a short list of lines, so it gets a grid rather than a table inside a table. */
.kit { display: flex; flex-direction: column; gap: 8px; }
.kit-row { display: grid; grid-template-columns: 1fr 90px 100px 120px 34px; gap: 8px; align-items: center; }
.kit-row .input { padding: 10px 12px; font-size: 13.5px; }
.kit-add { display: flex; gap: 10px; align-items: center; margin-top: 12px; flex-wrap: wrap; }
.kit-add .input.select-sm { width: auto; min-width: 260px; flex: 1; padding: 10px 12px; font-size: 13.5px; }

tr.sub-row td { background: var(--surface-2); padding: 0; }
.kit-view { padding: 14px 18px; }
.kit-line { display: grid; grid-template-columns: 1fr 120px 110px 110px; gap: 12px; padding: 7px 0; font-size: 13.5px; }
.kit-line + .kit-line { border-top: 1px solid var(--border); }
.kit-line .p, .kit-line .t { text-align: right; }
.kit-line .t { font-weight: 700; }
.kit-line.total { border-top: 1px solid var(--border); font-weight: 800; }
@media (max-width: 720px) {
    .kit-row { grid-template-columns: 1fr 70px 34px; }
    .kit-line { grid-template-columns: 1fr auto; }
    .kit-line .q, .kit-line .p { display: none; }
}

/* Long contents summaries stay on one line rather than pushing the row height around. */
.clamp { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ============================================================================
   Analytics
   ============================================================================ */
.legend-inline { display: flex; gap: 14px; color: var(--muted); font-size: 12.5px; font-weight: 600; }
.legend-inline > span { display: inline-flex; align-items: center; gap: 6px; }

/* A funnel that reads as a funnel: each stage's bar is its share of the top of the pipe, with the
   drop-off between stages called out rather than left to be worked out. */
.funnel { display: flex; flex-direction: column; }
.funnel-step { position: relative; }
.funnel-bar {
    position: absolute; inset: 0; border-radius: 10px; opacity: .16; transition: width .4s ease;
}
.funnel-bar.info { background: var(--info); }
.funnel-bar.success { background: var(--success); }
.funnel-bar.warning { background: var(--warning); }
.funnel-bar.teal { background: var(--teal); }
.funnel-text {
    position: relative; display: flex; align-items: baseline; gap: 10px;
    padding: 12px 14px;
}
.funnel-text .n { font-size: 20px; font-weight: 800; font-variant-numeric: tabular-nums; min-width: 42px; }
.funnel-text .l { font-weight: 600; font-size: 13.5px; flex: 1; }
.funnel-text .v { color: var(--muted); font-size: 13px; font-variant-numeric: tabular-nums; }
.funnel-drop {
    display: flex; align-items: center; gap: 4px; padding: 2px 0 2px 20px;
    color: var(--muted); font-size: 11.5px; font-weight: 700;
}

/* Profit and loss, three lines. */
.pl { display: flex; flex-direction: column; }
.pl-row { display: flex; justify-content: space-between; align-items: baseline; padding: 10px 0; font-size: 14px; }
.pl-row .k { color: var(--muted); font-weight: 600; }
.pl-row .v { font-weight: 700; }
.pl-row.total { border-top: 1px solid var(--border); margin-top: 4px; padding-top: 14px; font-size: 17px; }
.pl-row.total .k { color: var(--text); font-weight: 800; }
.pl-row.total .v { font-weight: 800; }
.pl-margin { display: flex; justify-content: space-between; align-items: flex-end; gap: 14px; margin-top: 18px; }
.pl-delta { font-size: 13px; font-weight: 700; }

/* Ageing rows are buttons — being told money is late is only half of it. */
.ageing { display: flex; flex-direction: column; }
.age-row {
    display: grid; grid-template-columns: 96px 1fr auto; align-items: center; gap: 14px;
    padding: 11px 8px; margin: 0 -8px; border: 0; background: none; border-radius: 10px;
    font-family: inherit; font-size: 13.5px; cursor: pointer; text-align: left; color: inherit;
    transition: background .14s ease;
}
.age-row:hover { background: var(--surface-2); }
.age-row .k { color: var(--muted); font-weight: 600; }
.age-row .v { font-weight: 700; font-variant-numeric: tabular-nums; }
.age-row .bar { height: 8px; border-radius: 999px; background: var(--surface-2); overflow: hidden; }
.age-row .bar > span { display: block; height: 100%; border-radius: 999px; background: var(--info); }
.age-row .bar > span.warn { background: var(--warning); }
.age-row .bar > span.late { background: var(--danger); }

/* Four small numbers in a row — job counts, not KPI cards. */
.mini-stats { display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px; }
.mini-stats > div { background: var(--surface-2); border-radius: var(--radius-sm); padding: 14px 12px; text-align: center; }
.mini-stats .v { display: block; font-size: 22px; font-weight: 800; }
.mini-stats .k { display: block; color: var(--muted); font-size: 11.5px; font-weight: 600; margin-top: 2px; }
@media (max-width: 620px) { .mini-stats { grid-template-columns: repeat(2, 1fr); } }

.access-list { display: flex; flex-direction: column; gap: 9px; margin-top: 16px; }
.access-row { display: flex; align-items: flex-start; gap: 9px; font-size: 13px; }
.access-row > .icon { margin-top: 1px; color: var(--success); }

.btn-danger { background: var(--danger); color: #fff; }
.btn-danger:hover { filter: brightness(0.94); }

/* Ranked spend, without reaching for a chart library. */
.breakdown { display: flex; flex-direction: column; gap: 14px; }
.bd-head { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; margin-bottom: 6px; }
.bd-head .k { font-weight: 700; font-size: 13.5px; }
.bd-head .v { font-weight: 700; font-size: 13.5px; font-variant-numeric: tabular-nums; }
.bd-row .meter { max-width: none; margin-top: 0; }

/* The count field on the stock take. Bigger than a normal input because it's the one thing anyone
   touches on that page, often on a tablet while standing in a storeroom. */
.count-input {
    max-width: 130px; padding: 12px 14px; font-size: 16px; font-weight: 700;
    text-align: right; font-variant-numeric: tabular-nums;
}
.count-input:placeholder-shown { font-weight: 500; }

/* Who's counting and since when, beside the heading. */
.session { display: flex; gap: 28px; padding: 14px 20px; }
.session .k { display: block; color: var(--muted); font-size: 11.5px; font-weight: 700; }
.session .v { display: block; font-weight: 700; font-size: 13.5px; margin-top: 2px; }
.num.is-danger, .num.is-success { font-weight: 700; }
.num .icon { vertical-align: -2px; margin-right: 2px; }

/* Three across for cost / markup / sell, which belong together. */
.grid-3 { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 16px; }
@media (max-width: 700px) { .grid-3 { grid-template-columns: 1fr; } }
th.sortable { cursor: pointer; user-select: none; }
th.sortable .icon { vertical-align: -2px; margin-left: 2px; }
.icon-btn-sm { width: 32px; height: 32px; border-radius: 9px; }
.icon-btn:disabled { opacity: .45; cursor: default; }
.icon-btn:disabled:hover { border-color: var(--border); color: var(--muted); background: var(--surface); }

.menu-wrap { position: relative; display: inline-block; }
.menu-backdrop { position: fixed; inset: 0; z-index: 40; }
.menu {
    position: absolute; right: 0; top: calc(100% + 6px); z-index: 41; min-width: 200px;
    background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-sm);
    box-shadow: var(--shadow-lg); padding: 6px; text-align: left;
}
.menu-up { top: auto; bottom: calc(100% + 6px); }
.menu-item {
    display: flex; align-items: center; gap: 10px; width: 100%; padding: 9px 10px;
    border: 0; border-radius: 9px; background: none; color: var(--text); cursor: pointer;
    font-family: inherit; font-size: 13.5px; font-weight: 600; text-align: left;
}
.menu-item:hover { background: var(--surface-2); color: var(--teal); }
.menu-item:disabled { opacity: .55; cursor: default; }
.menu-item:disabled:hover { background: none; color: var(--text); }

.pager {
    display: flex; align-items: center; justify-content: space-between; gap: 14px; flex-wrap: wrap;
    padding: 16px 22px; border-top: 1px solid var(--border); font-size: 13.5px;
}
.pager-controls { display: flex; align-items: center; gap: 8px; }
.page-num {
    min-width: 32px; height: 32px; border-radius: 9px; display: inline-flex; align-items: center;
    justify-content: center; background: var(--teal-tint); color: var(--teal); font-weight: 700;
}

/* The one empty state that sells rather than apologises — shown only when nothing has ever been
   created, never when a filter merely came back empty. */
.promo { text-align: center; padding: 54px 24px; }
.promo-ico {
    display: inline-flex; align-items: center; justify-content: center;
    width: 76px; height: 76px; border-radius: 22px; margin-bottom: 20px;
    background: var(--teal-tint); color: var(--teal);
}
.promo .t { font-size: 19px; font-weight: 800; }
.promo .s { color: var(--muted); font-size: 14px; margin: 8px auto 22px; max-width: 380px; }

/* ============================================================================
   Two-column form pages (new quote) — sections on the left, sticky summary right
   ============================================================================ */
.form-layout { display: grid; grid-template-columns: minmax(0, 1fr) 340px; gap: 20px; align-items: start; }
.form-main { display: flex; flex-direction: column; gap: 20px; min-width: 0; }
.form-side { position: sticky; top: 92px; }
@media (max-width: 1080px) {
    .form-layout { grid-template-columns: 1fr; }
    .form-side { position: static; }
}
.card-head h2 { display: flex; align-items: center; gap: 10px; }
.head-ico {
    display: inline-flex; align-items: center; justify-content: center;
    width: 32px; height: 32px; border-radius: 9px;
}
.head-ico.teal  { background: var(--teal-tint); color: var(--teal); }
.head-ico.info  { background: color-mix(in srgb, var(--info) 12%, transparent); color: var(--info); }
.head-ico.muted { background: var(--surface-2); color: var(--muted); }

.summary .totals { max-width: none; margin: 0; border-top: 0; padding-top: 0; }
.summary-actions { display: flex; flex-direction: column; gap: 10px; margin-top: 20px; }
.summary-note { color: var(--muted); font-size: 12px; margin: 14px 0 0; line-height: 1.45; }

/* Profile header — the avatar carries the identity so the name doesn't have to be enormous. */
.profile-head { display: flex; align-items: center; gap: 16px; }
.avatar-lg { width: 60px; height: 60px; font-size: 20px; }
.profile-head h1 { font-size: 28px; }
.profile-head p { margin: 5px 0 0; color: var(--muted); font-size: 14px; }
.is-danger { color: var(--danger); }
td.is-danger { font-weight: 700; }

.page-head-text .back { display: inline-flex; align-items: center; gap: 4px; margin-bottom: 8px; }
.page-head-text .back:hover { color: var(--teal); }

/* ── Live summary panel ───────────────────────────────────────────────────
   Fills in as the form is completed, so what you're about to create is readable before you
   commit to it. Anything not yet entered says so in muted text rather than sitting blank. */
.sum-list { display: flex; flex-direction: column; gap: 15px; }
.sum-row { display: flex; align-items: flex-start; gap: 11px; }
.sum-row > .icon { margin-top: 2px; }
.sum-row .k { display: block; color: var(--muted); font-size: 12px; font-weight: 600; }
.sum-row .v { display: block; font-size: 14px; font-weight: 700; margin-top: 1px; }
.sum-row .v.is-empty { color: var(--muted); font-weight: 600; }
.sum-row .v + .k { margin-top: 2px; font-weight: 600; }

.recur-banner {
    background: var(--teal-tint); border: 1px solid color-mix(in srgb, var(--teal) 26%, transparent);
    border-radius: var(--radius-sm); padding: 14px 16px; margin-bottom: 20px;
}
.recur-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.recur-head .t { font-weight: 800; font-size: 14.5px; }
.recur-line { color: var(--muted); font-size: 13px; margin-top: 5px; font-weight: 600; }

.note-box {
    display: flex; align-items: flex-start; gap: 10px; margin-top: 18px;
    background: color-mix(in srgb, var(--info) 9%, transparent);
    border: 1px solid color-mix(in srgb, var(--info) 24%, transparent);
    color: var(--muted); border-radius: var(--radius-sm); padding: 12px 14px;
    font-size: 12.5px; line-height: 1.45;
}
.note-box > .icon { color: var(--info); margin-top: 1px; }
.note-box.warn {
    background: color-mix(in srgb, var(--warning) 11%, transparent);
    border-color: color-mix(in srgb, var(--warning) 32%, transparent);
}
.note-box.warn > .icon { color: var(--warning); }

/* Two lines in one table cell (date over time, name over address). */
.stack-line { display: flex; align-items: center; gap: 6px; white-space: nowrap; }
.stack-line + .stack-line { margin-top: 3px; font-size: 12.5px; }
.avatar-empty { background: var(--surface-2); color: var(--muted); }

/* ============================================================================
   Availability / slot picker
   ============================================================================ */
.slot-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; }
.slot-head p { margin: 4px 0 0; font-size: 12.5px; }
.slot-days {
    display: flex; align-items: center; justify-content: space-between; gap: 10px;
    margin: 16px 0 6px; padding: 8px; background: var(--surface-2); border-radius: var(--radius-sm);
}
.slot-date { font-weight: 800; font-size: 14px; }
.slot-body { max-height: 52vh; overflow-y: auto; padding-right: 2px; }
.slot-section {
    font-size: 10.5px; font-weight: 800; letter-spacing: 1.2px; text-transform: uppercase;
    color: var(--muted); margin: 16px 0 8px;
}
.slot-note { font-size: 13px; padding: 2px 0 4px; }
.slot-foot { font-size: 12px; margin-top: 16px; padding-top: 12px; border-top: 1px solid var(--border); }
.slot-closed { text-align: center; padding: 26px 10px; }
.slot-closed .t { font-weight: 800; margin-top: 12px; }
.slot-closed .s { color: var(--muted); font-size: 13px; margin-top: 4px; }

/* An existing booking. The left edge is teal for a one-time job and navy-ish for a recurring
   service, matching how the app's picker distinguishes them. */
.booking {
    border: 1px solid var(--border); border-left: 4px solid var(--teal);
    border-radius: 10px; padding: 9px 12px; margin-bottom: 8px; background: var(--surface-2);
}
.booking.is-recurring { border-left-color: var(--purple); }
.booking-time { color: var(--teal); font-size: 12px; font-weight: 800; }
.booking.is-recurring .booking-time { color: var(--purple); }
.booking-name { font-weight: 700; font-size: 14px; margin-top: 1px; }
.booking-tag { color: var(--muted); font-size: 11.5px; margin-top: 1px; }

.chips { display: flex; flex-wrap: wrap; gap: 8px; }
.chip {
    background: var(--teal-tint); border: 1px solid color-mix(in srgb, var(--teal) 45%, transparent);
    color: var(--teal); border-radius: 10px; padding: 9px 14px; cursor: pointer;
    font-family: inherit; font-size: 14px; font-weight: 700;
    transition: background .14s ease, transform .14s ease;
}
.chip:hover { background: color-mix(in srgb, var(--teal) 24%, transparent); transform: translateY(-1px); }
@media (prefers-reduced-motion: reduce) { .chip:hover { transform: none; } }

/* ── Skeletons ────────────────────────────────────────────────────────────
   The page keeps its shape while it loads, so numbers land in place instead of the whole
   layout jumping when a spinner is replaced. */
.skeleton-card { pointer-events: none; }
.sk { display: block; border-radius: 8px; background: var(--surface-2); position: relative; overflow: hidden; }
.sk-ico { width: 42px; height: 42px; border-radius: var(--radius-sm); margin-bottom: 16px; }
.sk-line { height: 20px; width: 70%; margin-bottom: 10px; }
.sk-line.short { height: 12px; width: 45%; margin-bottom: 0; }
.sk::after, .skeleton-card.card::after {
    content: ""; position: absolute; inset: 0; transform: translateX(-100%);
    background: linear-gradient(90deg, transparent, color-mix(in srgb, var(--text) 5%, transparent), transparent);
    animation: shimmer 1.4s infinite;
}
.skeleton-card.card { position: relative; overflow: hidden; }
@keyframes shimmer { to { transform: translateX(100%); } }
@media (prefers-reduced-motion: reduce) {
    .sk::after, .skeleton-card.card::after { animation: none; }
}

/* Tables */
.table-wrap { overflow-x: auto; }
/* overflow-x:auto silently turns overflow-y into auto as well, which makes this a clipping box —
   so a row menu on the last row was cut off at the bottom of the table. While a menu is open the
   wrap stops clipping; horizontal scrolling comes back the moment it closes. */
.table-wrap.menu-open { overflow: visible; }
table.data { width: 100%; border-collapse: collapse; font-size: 14px; }
table.data th {
    text-align: left; color: var(--muted); font-weight: 700; font-size: 11px; letter-spacing: .5px;
    text-transform: uppercase; padding: 12px 16px; border-bottom: 1px solid var(--border); white-space: nowrap;
}
table.data td { padding: 14px 16px; border-bottom: 1px solid var(--border); }
/* A right-aligned money column needs its heading over the digits, not out at the left edge.
   `table.data th` outranks a bare `.right`, so it has to be said here. */
table.data th.right { text-align: right; }
table.data tr:last-child td { border-bottom: 0; }
table.data tbody tr { transition: background .1s; }
table.data tbody tr:hover { background: var(--surface-2); }
.num { font-variant-numeric: tabular-nums; }
.right { text-align: right; }
.muted { color: var(--muted); }

/* Status pills */
.pill {
    display: inline-block; padding: 3px 10px; border-radius: 999px; font-size: 12px; font-weight: 700;
    text-transform: capitalize;
}
.pill-draft, .pill-archived { background: color-mix(in srgb, var(--muted) 15%, transparent);   color: var(--muted); }
/* "scheduled" (jobs) had no rule at all, so it rendered as bare text next to properly styled
   siblings. Same for "active"/"paused" on recurring services. */
.pill-sent, .pill-scheduled { background: color-mix(in srgb, var(--info) 15%, transparent);    color: var(--info); }
.pill-accepted, .pill-paid, .pill-completed, .pill-active { background: color-mix(in srgb, var(--success) 15%, transparent); color: var(--success); }
.pill-partial, .pill-in_progress, .pill-quality_check, .pill-paused { background: color-mix(in srgb, var(--warning) 18%, transparent); color: var(--warning); }
.pill-rejected, .pill-overdue, .pill-cancelled { background: color-mix(in srgb, var(--danger) 15%, transparent); color: var(--danger); }

/* States */
.state { padding: 48px; text-align: center; color: var(--muted); }
.state .spinner { margin: 0 auto 14px; }

/* ============================================================================
   Buttons + forms
   ============================================================================ */
.btn {
    display: inline-flex; align-items: center; justify-content: center; gap: 8px;
    padding: 12px 20px; border-radius: 11px; font-weight: 700; font-size: 14px; cursor: pointer;
    border: 1px solid transparent; transition: all .12s;
}
.btn-primary { background: var(--teal); color: var(--teal-ink); }
.btn-primary:hover { filter: brightness(0.95); }
.btn-primary:disabled { opacity: .6; cursor: default; }
.btn-ghost { background: transparent; border-color: var(--border); color: var(--text); }
.btn-ghost:hover { border-color: var(--teal); color: var(--teal); }
.btn-block { width: 100%; }

.field { margin-bottom: 16px; }
.field label { display: block; font-size: 13px; font-weight: 600; color: var(--muted); margin-bottom: 6px; }
.input {
    width: 100%; padding: 13px 14px; border-radius: 11px; border: 1px solid var(--border);
    background: var(--surface-2); color: var(--text); font-size: 14px; font-family: inherit;
}
.input:focus { outline: none; border-color: var(--teal); box-shadow: 0 0 0 3px var(--teal-tint); }
.form-error {
    background: color-mix(in srgb, var(--danger) 12%, transparent);
    border: 1px solid color-mix(in srgb, var(--danger) 40%, transparent);
    color: var(--danger); border-radius: 11px; padding: 12px 14px; font-size: 13.5px; margin-bottom: 16px;
}

/* ============================================================================
   Login / register — always light, on the branded gradient backdrop
   ============================================================================ */
.auth {
    /* Force the light palette locally so sign-in is always light, whatever the app theme is. */
    --bg: #FFFFFF; --surface: #FFFFFF; --surface-2: #F5F7FA; --border: #E2E8F0;
    --text: #0A0E1A; --muted: #64748B;
    color: var(--text);
    min-height: 100vh; display: flex; align-items: center; justify-content: center; padding: 24px;
    position: relative; overflow: hidden;
    /* The brand backdrop: soft blue gradient with the Syncra mark set into the right side.
       The flat colour underneath matches the artwork so there's no white flash before it loads. */
    background: #DCE9F4 url("../img/auth-bg.webp") center / cover no-repeat;
}
/* The artwork is 2:1, so on tall/narrow viewports "cover" crops the mark away. Slide the
   focus right on those screens to keep it in frame behind the card. */
@media (max-aspect-ratio: 1/1) {
    .auth { background-position: 72% center; }
}
.auth-card {
    position: relative; z-index: 1; width: 100%; max-width: 420px;
    /* Slightly translucent so the backdrop reads through the card instead of sitting behind a
       flat white block. */
    background: rgba(255, 255, 255, 0.88);
    -webkit-backdrop-filter: blur(18px) saturate(120%);
            backdrop-filter: blur(18px) saturate(120%);
    border: 1px solid rgba(255, 255, 255, 0.75); border-radius: 22px; padding: 40px 36px 34px;
    box-shadow: 0 24px 60px rgba(10, 14, 26, 0.14);
}
.auth-card::after {
    content: ""; position: absolute; inset: 0; border-radius: 22px; pointer-events: none;
    padding: 1px; background: linear-gradient(150deg, rgba(0,212,200,0.65), transparent 42%);
    -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
    -webkit-mask-composite: xor; mask-composite: exclude;
}
.auth-logo {
    /* The light-background logo asset, so no blend trick is needed: the dark logo only worked
       via mix-blend-mode: screen, which turns invisible the moment the card is white. */
    display: block; width: 210px; margin: 0 auto 14px;
}
.auth-sub { text-align: center; color: var(--muted); font-size: 13.5px; margin-bottom: 26px; }
.auth-foot { text-align: center; color: var(--muted); font-size: 11.5px; margin-top: 20px; }
.auth-foot .teal { color: var(--teal); }

/* ============================================================================
   Detail pages + modals
   ============================================================================ */
.detail-head { display: flex; justify-content: space-between; align-items: flex-start; gap: 16px; flex-wrap: wrap; }
.detail-head .back { color: var(--muted); font-size: 13px; }
.detail-head h2 { font-size: 22px; display: flex; align-items: center; gap: 10px; }
.detail-head .actions { display: flex; gap: 10px; flex-wrap: wrap; }

.line-section { font-weight: 800; font-size: 13px; text-transform: uppercase; letter-spacing: .5px; color: var(--muted); margin: 16px 0 8px; }
.totals { margin-top: 18px; border-top: 1px solid var(--border); padding-top: 14px; max-width: 320px; margin-left: auto; }
.totals > div { display: flex; justify-content: space-between; padding: 5px 0; font-size: 14px; }
.totals .grand { border-top: 1px solid var(--border); margin-top: 6px; padding-top: 12px; font-weight: 800; font-size: 18px; }
.totals .grand .num { color: var(--teal); }

.flash { margin-top: 14px; padding: 12px 16px; border-radius: 12px; font-size: 14px; font-weight: 600; }
.flash-ok  { background: color-mix(in srgb, var(--success) 14%, transparent); color: var(--success); border: 1px solid color-mix(in srgb, var(--success) 40%, transparent); }
.flash-err { background: color-mix(in srgb, var(--danger) 12%, transparent);  color: var(--danger);  border: 1px solid color-mix(in srgb, var(--danger) 40%, transparent); }

.modal-back { position: fixed; inset: 0; background: rgba(4, 8, 16, 0.55); backdrop-filter: blur(2px); display: flex; align-items: center; justify-content: center; z-index: 100; padding: 20px; }
.modal { width: 100%; max-width: 380px; background: var(--surface); border: 1px solid var(--border); border-radius: 16px; padding: 24px; box-shadow: var(--shadow-lg); }
/* These have to sit AFTER .modal: same specificity, so source order decides, and above it they
   lost to max-width:380px and every modal came out the same narrow width. */
.modal-wide { max-width: 460px; }
/* A two-column form needs room. At the picker's width the pairs stacked and every field read as
   cramped. */
.modal-form { max-width: 680px; padding: 28px 30px; }
.modal-form .grid-2 { gap: 18px; }
.modal-actions {
    display: flex; justify-content: flex-end; gap: 10px; margin-top: 24px;
    padding-top: 20px; border-top: 1px solid var(--border);
}
@media (max-width: 700px) {
    .modal-form .grid-2 { grid-template-columns: 1fr; }
    .modal-actions { flex-direction: column-reverse; }
    .modal-actions .btn { width: 100%; }
}
.modal h3 { font-size: 17px; }

.page-actions { display: flex; justify-content: flex-end; margin-bottom: 14px; }
tr.row-link { cursor: pointer; }
.back { color: var(--muted); font-size: 13px; }

textarea.input { resize: vertical; font-family: inherit; }
.btn-sm { padding: 8px 14px; font-size: 13px; }

/* line-item editor */
.editor-sec { border: 1px solid var(--border); border-radius: 12px; padding: 14px; margin-bottom: 14px; background: var(--surface-2); }
.editor-sec-head { display: flex; gap: 8px; align-items: center; margin-bottom: 10px; }
.editor-sec-head .sec-name { max-width: 320px; font-weight: 700; }
.editor-table th, .editor-table td { padding: 6px 8px; }
.editor-table .input { padding: 8px 10px; }
.editor-table td:first-child { padding-left: 0; }
.new-cust { display: grid; gap: 8px; }

/* ============================================================================
   Settings
   ============================================================================ */
.settings-nav { display: flex; gap: 6px; flex-wrap: wrap; border-bottom: 1px solid var(--border); padding-bottom: 12px; margin-bottom: 4px; }
.sub-item { padding: 8px 14px; border-radius: 9px; color: var(--muted); font-weight: 600; font-size: 13.5px; }
.sub-item:hover { background: var(--surface-2); color: var(--text); }
.sub-item.active { background: var(--teal-tint); color: var(--teal); }

label.check { display: flex; align-items: center; gap: 10px; font-size: 14px; font-weight: 600; cursor: pointer; padding: 6px 0; }
label.check input { width: 18px; height: 18px; accent-color: var(--teal); }
.colour-row { display: flex; gap: 8px; align-items: center; }
.colour-row input[type=color] { width: 42px; height: 42px; border: 1px solid var(--border); border-radius: 10px; background: var(--surface-2); padding: 3px; cursor: pointer; }

.team-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 14px; }
.team-card { display: flex; align-items: flex-start; gap: 14px; }
.avatar { width: 44px; height: 44px; border-radius: 50%; background: var(--teal-tint); color: var(--teal); font-weight: 800; display: flex; align-items: center; justify-content: center; flex-shrink: 0; }

.hours-row { display: flex; align-items: center; gap: 12px; padding: 8px 0; border-bottom: 1px solid var(--border); flex-wrap: wrap; }
.hours-row:last-child { border-bottom: 0; }
.hours-day { width: 100px; font-weight: 700; }

.chart-box { position: relative; height: 260px; }

/* ============================================================================
   Schedule — calendar, dispatch board, rail
   ============================================================================ */
.sched-layout { display: grid; grid-template-columns: minmax(0, 1fr) 340px; gap: 20px; align-items: start; }
.sched-rail { display: flex; flex-direction: column; gap: 20px; position: sticky; top: 92px; }
@media (max-width: 1200px) {
    .sched-layout { grid-template-columns: 1fr; }
    .sched-rail { position: static; }
}

/* The calendar is one white surface, not forty-two outlined boxes. Cells are separated by
   hairlines drawn with a gap over a border-coloured background, so the whole thing reads as a
   single sheet sitting on the branded backdrop. */
.sched-card { overflow: hidden; }
.sched-toolbar {
    display: flex; align-items: center; justify-content: space-between; gap: 14px; flex-wrap: wrap;
    padding: 18px 22px; border-bottom: 1px solid var(--border);
}
.sched-nav { display: flex; align-items: center; gap: 8px; }
.sched-title { font-weight: 800; font-size: 16px; min-width: 190px; text-align: center; }
.sched-filters { display: flex; gap: 10px; flex-wrap: wrap; }
.sched-filters .input.select-sm { width: auto; min-width: 150px; padding: 10px 12px; font-size: 13.5px; }

.segmented { display: inline-flex; background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 3px; }
.seg {
    border: 0; background: none; color: var(--muted); font-family: inherit; font-size: 13.5px;
    font-weight: 700; padding: 7px 16px; border-radius: 9px; cursor: pointer; transition: all .14s ease;
}
.seg:hover { color: var(--text); }
.seg.is-on { background: var(--teal); color: var(--teal-ink); }

.cal { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 1px; background: var(--border); }
.cal-dow {
    text-align: center; font-size: 10.5px; font-weight: 800; letter-spacing: 1px; color: var(--muted);
    text-transform: uppercase; padding: 12px 0; background: var(--surface);
}
.cal-cell {
    min-height: 122px; background: var(--surface); padding: 8px; cursor: pointer;
    display: flex; flex-direction: column; gap: 5px; transition: background .14s ease;
}
.cal-cell:hover { background: var(--surface-2); }
.cal-out { background: var(--surface-2); }
.cal-out .cal-num { opacity: .45; }
.cal-num { font-size: 12px; font-weight: 700; color: var(--muted); }
.cal-today .cal-num {
    color: var(--teal-ink); background: var(--teal); border-radius: 50%;
    width: 22px; height: 22px; display: inline-flex; align-items: center; justify-content: center;
}

/* An event carries what the job is, not just who it's for. Status is a coloured left edge rather
   than a fill, so a full week doesn't turn into a block of colour. */
.ev {
    border-left: 3px solid var(--info); border-radius: 7px; padding: 5px 8px;
    background: var(--surface-2); cursor: pointer; overflow: hidden;
    transition: background .14s ease, transform .14s ease;
}
.ev:hover { background: color-mix(in srgb, var(--teal) 10%, var(--surface-2)); transform: translateX(1px); }
.ev-blue  { border-left-color: var(--info); }
.ev-amber { border-left-color: var(--warning); }
.ev-green { border-left-color: var(--success); }
.ev-red   { border-left-color: var(--danger); }
.ev-top { display: flex; align-items: center; justify-content: space-between; gap: 4px; color: var(--muted); }
.ev-time { font-size: 11px; font-weight: 800; }
.ev-title {
    font-size: 12px; font-weight: 700; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.ev-who {
    display: flex; align-items: center; gap: 5px; margin-top: 3px; color: var(--muted);
    font-size: 11px; white-space: nowrap; overflow: hidden;
}
.avatar-xs { width: 18px; height: 18px; font-size: 9px; }
@media (max-width: 900px) {
    .cal-cell { min-height: 84px; }
    .ev-who { display: none; }
}

/* ── Week: the dispatch board ─────────────────────────────────────────────
   Technicians down the side, days across. An empty row is somebody idle. */
.board { display: grid; gap: 1px; background: var(--border); }
.board-corner { background: var(--surface); }
.board-dow {
    background: var(--surface); text-align: center; padding: 10px 4px;
    display: flex; flex-direction: column; gap: 1px;
}
.board-dow span { font-size: 10.5px; font-weight: 800; letter-spacing: 1px; text-transform: uppercase; color: var(--muted); }
.board-dow strong { font-size: 15px; }
.board-dow.is-today strong { color: var(--teal); }
.board-tech { background: var(--surface); padding: 12px 10px; display: flex; align-items: center; gap: 10px; }
.board-tech .who { min-width: 0; }
.board-tech .t { display: block; font-weight: 700; font-size: 13px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.board-tech .s { display: block; color: var(--muted); font-size: 11.5px; }
.board-cell {
    background: var(--surface); min-height: 78px; padding: 6px; cursor: pointer;
    display: flex; flex-direction: column; gap: 5px; transition: background .14s ease;
}
.board-cell:hover { background: var(--surface-2); }
.board-cell.is-today { background: color-mix(in srgb, var(--teal) 5%, var(--surface)); }

/* ── Day view ─────────────────────────────────────────────────────────── */
.dayview { padding: 22px; }
.day-row {
    display: flex; align-items: center; gap: 16px; padding: 14px 16px; margin-bottom: 10px;
    background: var(--surface-2); border-left: 3px solid var(--info); border-radius: var(--radius-sm);
    cursor: pointer; transition: background .14s ease;
}
.day-row:hover { background: color-mix(in srgb, var(--teal) 8%, var(--surface-2)); }
.day-time { min-width: 76px; }
.day-time .t { font-weight: 800; font-size: 15px; }
.day-time .s, .day-main .s { color: var(--muted); font-size: 12.5px; }
.day-main { flex: 1; min-width: 0; }
.day-main .t { font-weight: 700; font-size: 14.5px; display: flex; align-items: center; gap: 6px; }
.day-end { display: flex; flex-direction: column; align-items: flex-end; gap: 6px; }

.legend {
    display: flex; align-items: center; gap: 18px; flex-wrap: wrap;
    padding: 14px 22px; border-top: 1px solid var(--border); font-size: 12.5px; color: var(--muted);
    font-weight: 600;
}
.legend > span { display: inline-flex; align-items: center; gap: 6px; }
.dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; background: var(--muted); display: inline-block; }
.dot-scheduled { background: var(--info); }
.dot-in-progress { background: var(--warning); }
.dot-quality-check { background: var(--purple); }
.dot-completed { background: var(--success); }
.dot-overdue { background: var(--danger); }
.dot-cancelled, .dot-archived { background: var(--danger); }

/* ── Rail: utilisation + dispatch queue ──────────────────────────────── */
.util-list { display: flex; flex-direction: column; gap: 16px; }
.util { display: flex; align-items: flex-start; gap: 12px; }
.util-main { flex: 1; min-width: 0; }
.util-top { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
.util-top .t { font-weight: 700; font-size: 13.5px; }
.util .s { color: var(--muted); font-size: 12px; }
.util .meter { margin-top: 7px; max-width: none; }

.count-chip {
    display: inline-flex; align-items: center; justify-content: center; min-width: 20px; height: 20px;
    padding: 0 6px; border-radius: 999px; background: var(--teal-tint); color: var(--teal);
    font-size: 11.5px; font-weight: 800; margin-left: 6px;
}
.dispatch-list { display: flex; flex-direction: column; }
.dispatch {
    display: flex; align-items: center; gap: 12px; padding: 12px 0;
    border-bottom: 1px solid var(--border);
}
.dispatch:last-child { border-bottom: 0; }
.dispatch-main { flex: 1; min-width: 0; }
.dispatch .t { font-weight: 700; font-size: 13.5px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dispatch .s { color: var(--muted); font-size: 12px; }
.dispatch .is-danger { color: var(--danger); font-weight: 700; }

/* The event detail uses the shared .modal, so there is no drawer styling here any more. */

/* ── Responsive ─────────────────────────────────────────────────────────── */
@media (max-width: 780px) {
    :root { --sidebar-w: 0px; }
    .sidebar { transform: translateX(-100%); }
    .main { margin-left: 0; }
    .content { padding: 18px; }
}

/* ── Password field with a reveal ──────────────────────────────────────────
   The button sits inside the box rather than beside it, so the field keeps the
   same footprint as every other input on the page. */
.pw-wrap { position: relative; }
.pw-input { padding-right: 76px; }
.pw-reveal {
    position: absolute; top: 0; right: 0; height: 100%;
    padding: 0 14px; border: 0; background: none; border-radius: 0 11px 11px 0;
    color: var(--teal); font-family: inherit; font-size: 13px; font-weight: 600; cursor: pointer;
}
.pw-reveal:hover { text-decoration: underline; }

/* ── Label/value pairs on a detail card ────────────────────────────────────
   Used by the job card. Two columns so the values line up, collapsing to
   stacked rows on a phone. */
.kv { display: grid; gap: 7px; margin-top: 6px; }
.kv > div { display: flex; justify-content: space-between; gap: 16px; align-items: baseline; }
.kv > div > span:first-child { flex: 0 0 auto; }
.kv > div > span:last-child { text-align: right; }
@media (max-width: 560px) {
    .kv > div { flex-direction: column; gap: 2px; }
    .kv > div > span:last-child { text-align: left; }
}

/* ── Onboarding step pips ──────────────────────────────────────────────── */
.wizard-steps { display: flex; gap: 6px; margin: 0 0 20px; }
.wizard-step {
    flex: 1; height: 4px; border-radius: 2px;
    background: var(--border); transition: background .2s ease;
}
.wizard-step.on { background: var(--teal); }
