/* ============================================================
   layout.css — app shell: sidebar, topbar, views, dashboard
   ============================================================ */
#app{display:grid;grid-template-columns:var(--sidebar-w) minmax(0,1fr);min-height:100vh}

/* ---- sidebar ---- */
.sidebar{background:var(--bg-elev);border-right:1px solid var(--border);display:flex;flex-direction:column;position:sticky;top:0;height:100vh}
.brand{display:flex;align-items:center;gap:var(--sp-3);padding:var(--sp-4) var(--sp-5);border-bottom:1px solid var(--border)}
.brand .logo{width:36px;height:36px;border-radius:var(--r-md);background:linear-gradient(135deg,var(--brand-500),var(--accent-500));display:grid;place-items:center;color:#fff;font-weight:var(--fw-heavy);font-size:16px;box-shadow:var(--shadow-md);flex:none}
.brand .name{font-weight:var(--fw-bold);font-size:15px;letter-spacing:-.02em}
.brand .sub{font-size:var(--fs-2xs);color:var(--text-3);font-weight:var(--fw-semibold);letter-spacing:.06em;text-transform:uppercase}
.sidebar-project{display:flex;align-items:center;gap:var(--sp-2);width:100%;padding:var(--sp-3) var(--sp-5);border:none;border-bottom:1px solid var(--border);background:var(--surface-2);color:var(--text);font-family:inherit;text-align:left;cursor:pointer;transition:background .13s}
.sidebar-project:hover{background:var(--surface-3)}
.sidebar-project svg{width:16px;height:16px;flex:none;color:var(--text-3)}
.sidebar-project .sp-text{min-width:0;flex:1}
.sidebar-project .sp-label{font-size:var(--fs-xs);font-weight:var(--fw-bold);color:var(--text-3);text-transform:uppercase;letter-spacing:.06em}
.sidebar-project .sp-name{font-size:var(--fs-sm);font-weight:var(--fw-semibold);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;margin-top:1px}
.nav{flex:1;overflow:auto;padding:12px 12px}
.nav-group{font-size:var(--fs-2xs);font-weight:var(--fw-bold);color:var(--text-3);text-transform:uppercase;letter-spacing:.07em;padding:14px 12px 6px}
.nav a{display:flex;align-items:center;gap:var(--sp-3);padding:var(--sp-2) var(--sp-3);border-radius:var(--r-sm);color:var(--text-2);font-weight:var(--fw-semibold);font-size:var(--fs-sm);cursor:pointer;transition:all .13s;margin-bottom:2px}
.nav a:hover{background:var(--surface-3);color:var(--text)}
.nav a.active{background:var(--brand-50);color:var(--brand-600)}
.nav a svg{width:18px;height:18px;flex:none}
.nav a .count{margin-left:auto;font-size:var(--fs-xs);background:var(--surface-3);color:var(--text-3);padding:1px 7px;border-radius:99px;font-weight:var(--fw-bold)}
.nav a.active .count{background:var(--brand-100);color:var(--brand-600)}
.nav a.nav-disabled{color:var(--text-disabled);pointer-events:none}
.nav a.nav-disabled svg{opacity:.5}
img.logo{width:36px;height:36px;border-radius:var(--r-md);border:1px solid var(--border)}
.sidebar-foot{padding:14px;border-top:1px solid var(--border);display:flex;align-items:center;gap:var(--sp-2);font-size:var(--fs-xs);color:var(--text-3)}

/* ---- topbar ---- */
.main{display:flex;flex-direction:column;min-width:0}
.topbar{height:var(--topbar-h);background:var(--bg-elev);border-bottom:1px solid var(--border);display:flex;align-items:center;gap:var(--sp-4);padding:0 var(--sp-6);position:sticky;top:0;z-index:20}
.topbar .title{font-weight:var(--fw-bold);font-size:var(--fs-md)}
.topbar .spacer{flex:1}
.save-state{display:flex;align-items:center;gap:7px;font-size:var(--fs-xs);color:var(--text-3);font-weight:var(--fw-semibold)}

/* ---- view ---- */
/* 1280px capped the app to ~65% of a 1920-wide screen, leaving a dead band on
   BOTH sides while the BOM table simultaneously overflowed on the right — the
   estimator lost space they already had. Data-dense screens (BOM, Gantt,
   materials) want the width; prose still gets a comfortable measure because
   .view-head p is capped at 68ch separately. */
.view{padding:var(--sp-8);max-width:1680px;width:100%;margin:0 auto;animation:fade .2s ease}
.view-head{margin-bottom:var(--sp-6)}
.view-head h1{font-size:var(--fs-2xl)}
.view-head p{color:var(--text-3);margin:6px 0 0;font-size:var(--fs-md)}

/* ---- dashboard ---- */
.kpi-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:var(--sp-4);margin-bottom:var(--sp-6)}
.kpi{background:var(--surface);border:1px solid var(--border);border-radius:var(--r-lg);padding:var(--sp-5);box-shadow:var(--shadow-sm);position:relative;overflow:hidden}
.kpi .ic{width:38px;height:38px;border-radius:var(--r-md);display:grid;place-items:center;margin-bottom:13px}
.kpi .ic svg{width:20px;height:20px}
.kpi .label{font-size:var(--fs-xs);color:var(--text-3);font-weight:var(--fw-bold);text-transform:uppercase;letter-spacing:.04em}
.kpi .val{font-size:var(--fs-2xl);font-weight:var(--fw-heavy);letter-spacing:-.02em;margin-top:3px;font-variant-numeric:tabular-nums}
.kpi .meta{font-size:var(--fs-xs);color:var(--text-3);margin-top:6px;font-weight:var(--fw-semibold)}
/* Gradient built from the FILL token, not the ink ramp — in dark mode the ink ramp
   lightens and white-on-gradient would fail on the app's largest number. */
.kpi.hero{grid-column:span 2;background:linear-gradient(135deg,var(--brand-fill),var(--brand-fill-hover));color:var(--on-brand);border:none}
.kpi.hero .label,.kpi.hero .meta{color:rgba(255,255,255,.8)}
.kpi.hero .ic{background:rgba(255,255,255,.18)}
.ic-brand{background:var(--brand-50);color:var(--brand-600)}
.ic-ok{background:var(--ok-50);color:var(--ok-500)}
.ic-warn{background:var(--warn-50);color:var(--warn-500)}
.ic-info{background:var(--brand-50);color:var(--info-500)}

.panel-grid{display:grid;grid-template-columns:1.4fr 1fr;gap:var(--sp-4)}
/* .card (components.css) and .panel already share one look (same bg/border/
   radius/shadow tokens) — the gap was never a visual divergence, just no
   written rule for which to reach for. Rule: titled content (a header +
   action slot) → .panel; bare/untitled surfaces (KPI tiles, empty states) →
   .card. .panel adds overflow:hidden so .panel-head's border meets the
   rounded corners cleanly. */
.panel{background:var(--surface);border:1px solid var(--border);border-radius:var(--r-lg);box-shadow:var(--shadow-sm);overflow:hidden}
.panel-head{display:flex;align-items:center;justify-content:space-between;padding:var(--sp-4) var(--sp-5);border-bottom:1px solid var(--border)}
.panel-head h3{font-size:var(--fs-md)}
.panel-body{padding:var(--sp-5)}
.chart-box{position:relative;height:260px}

/* ---- progress / wizard ---- */
.steps{display:flex;align-items:center;gap:0;margin-bottom:var(--sp-8);flex-wrap:wrap}
.step{display:flex;align-items:center;gap:var(--sp-2);color:var(--text-3);font-weight:var(--fw-semibold);font-size:var(--fs-sm);cursor:pointer;border-radius:var(--r-sm);padding:2px 4px;margin:-2px -4px;transition:background .13s}
.step:hover{background:var(--surface-3)}
.step .num{width:28px;height:28px;border-radius:50%;display:grid;place-items:center;background:var(--surface-3);color:var(--text-3);font-size:var(--fs-xs);font-weight:var(--fw-bold);border:1px solid var(--border)}
.step.active{color:var(--text)}
.step.active .num{background:var(--brand-fill);color:var(--on-brand);border-color:var(--brand-fill)}
.step.done .num{background:var(--ok-500);color:#fff;border-color:var(--ok-500)}
.step-line{flex:1;height:2px;background:var(--border);min-width:24px;margin:0 12px}
.step-line.done{background:var(--ok-500)}

/* ---- trade grid ---- */
.trade-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(240px,1fr));gap:var(--sp-3)}
.trade-card{display:flex;align-items:flex-start;gap:12px;padding:var(--sp-4);border:1px solid var(--border);border-radius:var(--r-md);background:var(--surface);cursor:pointer;transition:all .15s;position:relative}
.trade-card:hover{border-color:var(--brand-300);box-shadow:var(--shadow-md);transform:translateY(-1px)}
.trade-card.on{border-color:var(--brand-500);background:var(--brand-50)}
.trade-card .tc-ic{width:36px;height:36px;border-radius:10px;background:var(--surface-3);display:grid;place-items:center;flex:none;color:var(--text-2)}
.trade-card .tc-ic svg{width:19px;height:19px}
.trade-card.on .tc-ic{background:var(--brand-100)}
.trade-card.on .tc-ic svg{color:var(--brand-600)}
.trade-ic{width:17px;height:17px;flex:none;vertical-align:-3px;color:var(--text-2)}
.trade-ic-lg{width:22px;height:22px;vertical-align:-4px;color:var(--brand-600)}
.trade-card .tc-name{font-weight:var(--fw-bold);font-size:var(--fs-sm);color:var(--text)}
.trade-card .tc-meta{font-size:var(--fs-xs);color:var(--text-3);margin-top:2px}
.trade-card .tc-check{position:absolute;top:10px;right:10px;width:18px;height:18px;border-radius:50%;border:2px solid var(--border-strong);display:grid;place-items:center}
.trade-card.on .tc-check{background:var(--brand-fill);border-color:var(--brand-fill);color:var(--on-brand)}
.trade-card.on .tc-check svg{width:11px;height:11px;color:#fff}

/* ---- trade editor ---- */
/* minmax(0,…) not 1fr: a grid column's default min-width is auto, so a wide child
   (the element-schema table on tie-beam/column/footing) forces the column past its
   share and scrolls the whole page sideways. Capping the min lets the table scroll
   inside its own .table-wrap instead of blowing out the layout. */
.editor-layout{display:grid;grid-template-columns:340px minmax(0,1fr);gap:var(--sp-5);align-items:start}
.trade-list{display:flex;flex-direction:column;gap:4px;position:sticky;top:calc(var(--topbar-h) + var(--sp-5))}
.trade-list .tli{display:flex;align-items:center;gap:var(--sp-2);padding:10px 12px;border-radius:var(--r-sm);cursor:pointer;font-size:var(--fs-sm);font-weight:var(--fw-semibold);color:var(--text-2);border:1px solid transparent}
.trade-list .tli:hover{background:var(--surface-2)}
.trade-list .tli.active{background:var(--surface);border-color:var(--border);color:var(--text);box-shadow:var(--shadow-sm)}
.trade-list .tli .tli-amt{margin-left:auto;font-family:var(--mono);font-size:var(--fs-xs);color:var(--text-3)}
.trade-list .tli .tli-ic{flex:none;display:inline-flex}
.trade-list .tli .tli-ic svg{width:16px;height:16px}
.input-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(150px,1fr));gap:var(--sp-3)}
.section-label{font-size:var(--fs-xs);font-weight:var(--fw-bold);color:var(--text-3);text-transform:uppercase;letter-spacing:.05em;margin:var(--sp-5) 0 var(--sp-3)}

/* ---- gantt ---- */
/* width:100% so the table FILLS a wide screen instead of sitting at its content width.
   Combined with the capped metadata columns below, every surplus pixel on a large monitor
   lands in the month cells — which is the whole point of the screen. Without this the
   surplus went to the Activity column (auto layout gives it to the longest text), which
   is how the chart ended up as a narrow strip on the right. */
.gantt{border-collapse:separate;border-spacing:0;width:100%}
/* 30px could not hold "16-31" or "100%" without the glyphs touching. 38px is
   the smallest width where every label this table actually renders fits with
   air around it. Per-period PESO figures were removed from the footer instead
   of widened — see schedule.js: at ~7 characters they can never fit a
   half-month column, and the table below already shows them properly. */
/* --- the Gantt's own scrollport --- */
/* sticky thead needs a scrolling ancestor. Without max-height the wrap was as tall
   as its content, so nothing scrolled inside it, the header rode up with the page and
   covered the first activities. Both axes now scroll here, which is also what pins
   the Activity column and the header at the same time. */
.table-wrap--gantt{max-height:calc(100vh - 260px);overflow:auto;border:none}
/* Month abbreviation only ("Jul"), year carried once on the row above — 30px instead
   of the ~64px "JUL 2026" needed. On a 24-period schedule that is ~816px reclaimed,
   which is the difference between seeing the whole timeline and scrolling for it. */
/* THE CHART IS THE POINT OF THIS SCREEN.
   Auto layout hands surplus width to whichever column holds the longest text — the
   Activity names — so on a wide monitor the metadata block ate the screen and the
   timeline was squeezed into a strip on the right. max-width on a table cell does NOT
   fix this: auto layout treats it as a hint and still grew Activity from 170px to 319px
   when tested at 1750px.
   table-layout:fixed was tried and is WRONG here: combined with width:100% the table can
   never exceed its container, so a 24-month schedule crushed every month column to 16px
   instead of scrolling. The table must stay on auto layout so it sizes to its content and
   the wrapper scrolls.
   The working fix is to compete for the surplus rather than try to forbid it: auto layout
   shares extra width roughly in proportion to what each column already asks for, so giving
   the month cells a substantial width claim means THEY absorb a wide monitor, while the
   Activity column is pinned to a width it has no reason to exceed. */
.gantt th:first-child,.gantt td:first-child{width:230px;overflow-wrap:anywhere;white-space:normal}
/* Caps the activity NAME's text block so it wraps into a tidy column on a wide screen.
   See schedule.js for why this does not (and cannot, without table-layout:fixed) pin the
   column width itself. */
.gantt .gantt-activity{max-width:230px}
/* Start / Months / Weight / Value / Done / Progress / Earned / Remaining — numeric, so
   they need only their digits. 76px fits the widest real value ("₱926.0K") with air. */
.gantt thead tr:first-child th[rowspan]:not(:first-child){width:76px;max-width:76px}
/* The chart's claim on the table. Large enough that surplus lands here first, and a tick
   becomes a real target instead of a speck. */
.gantt th.m,.gantt td.m{width:64px;min-width:44px;padding:0 2px;text-align:center}
.gantt th.m-year{text-align:center;font-size:var(--fs-2xs);font-weight:var(--fw-heavy);
  letter-spacing:.08em;color:var(--text-2);border-left:1px solid var(--border-strong);
  padding:4px 2px;position:sticky;top:0;background:var(--surface-2);z-index:2}
.gantt thead tr:first-child th.m-year + th.m-year{border-left:1px solid var(--border-strong)}
.gantt th.m{font-size:var(--fs-2xs);color:var(--text-3);font-weight:var(--fw-semibold);white-space:nowrap;transform:rotate(-0deg)}
.gantt td.m .cell{height:20px;margin:3px 0;border-radius:0}
/* ---- cost-loaded checkpoints ----
   Each scheduled period is a clickable checkpoint carrying its share of the
   contract. Colour AND glyph/pattern carry the state, so the chart still reads
   in greyscale and for colour-blind users:
     done      solid green + check
     current   blue ring (this month)
     delayed   red (a period that has passed unticked — real money not earned)
     future    neutral outline
     partial   striped, with the percent shown                                */
.gantt td.period-cell{padding:0;text-align:center}
.gantt td.period-cell .chk{
  width:22px;height:22px;margin:3px auto;display:grid;place-items:center;
  border:1.5px solid var(--border-strong);border-radius:5px;background:var(--surface);
  color:var(--text-3);font-size:11px;font-weight:var(--fw-bold);font-family:var(--mono);
  cursor:pointer;padding:0;transition:transform .1s,background .15s,border-color .15s}
.gantt td.period-cell .chk:hover{transform:scale(1.14);border-color:var(--brand-500)}
.gantt td.period-cell .chk:focus-visible{outline:none;box-shadow:var(--ring)}
.gantt td.is-done .chk{background:var(--ok-500);border-color:var(--ok-700);color:#fff}
.gantt td.is-current .chk{border-color:var(--brand-600);border-width:2px;background:var(--brand-50);color:var(--brand-700)}
.gantt td.is-delayed .chk{background:var(--danger-50);border-color:var(--danger-500);color:var(--danger-700)}
.gantt td.is-delayed .chk::after{content:"!";}
.gantt td.is-delayed-partial .chk,.gantt td.is-partial .chk{
  background:repeating-linear-gradient(45deg,var(--warn-100) 0 4px,var(--warn-50) 4px 8px);
  border-color:var(--warn-700);color:var(--warn-700)}
.gantt td.is-future .chk{background:var(--surface-2)}
/* A finished activity recedes; a behind one is flagged on the name. */
.gantt tbody tr.is-done td.cell-name{color:var(--ok-700)}
.gantt tbody tr.is-behind td.cell-name{color:var(--danger-700)}
/* Inline progress meter in the Progress column. */
.prog-mini{display:inline-block;width:46px;height:6px;border-radius:99px;background:var(--surface-3);overflow:hidden;vertical-align:middle}
.prog-mini>span{display:block;height:100%;background:var(--ok-500);border-radius:99px}
.gantt .sched-in{width:46px;padding:4px 6px}
.gantt tbody tr:hover td.m.on .cell{filter:brightness(1.08)}
/* THIS MONTH — the anchor for "am I on time?". A faint tint down the whole column
   plus a solid top marker, so it is findable at a glance in a 24-column chart
   instead of being two hairlines you have to hunt for. */
.gantt th.m.today-col,.gantt td.m.today-col{background:var(--brand-50);box-shadow:inset 1px 0 0 var(--brand-400),inset -1px 0 0 var(--brand-400)}
.gantt thead th.m.today-col{background:var(--brand-100);color:var(--brand-700);font-weight:var(--fw-bold);box-shadow:inset 0 3px 0 var(--brand-600),inset 1px 0 0 var(--brand-400),inset -1px 0 0 var(--brand-400)}
[data-theme="dark"] .gantt th.m.today-col,[data-theme="dark"] .gantt td.m.today-col{background:rgba(99,102,241,.12)}
.gantt-legend{display:flex;gap:16px;flex-wrap:wrap;align-items:center;font-size:var(--fs-xs);color:var(--text-2)}
.gantt-legend .k{display:inline-flex;align-items:center;gap:6px}
.gantt-legend .sw{width:22px;height:12px;border-radius:3px;flex:none}
.gantt-legend .sw-todo{background:transparent;border:1.5px dashed var(--brand-400)}
.gantt-legend .sw-doing{background:repeating-linear-gradient(45deg,var(--warn-500) 0 6px,var(--warn-100) 6px 12px)}
.gantt-legend .sw-done{background:linear-gradient(180deg,var(--ok-500),var(--ok-700))}
.gantt-legend .sw-today{background:var(--brand-100);box-shadow:inset 0 3px 0 var(--brand-600)}
.today-dot{display:inline-block;width:6px;height:6px;border-radius:50%;background:var(--brand-600)}
.gantt td.m[data-task]{cursor:pointer;user-select:none}
.gantt td.m.drag-preview .cell{background:var(--brand-300)}
/* 2-row grouped header (month spanning colspan=2 over its two half-month
   periods): the blanket table.data thead th{top:0} rule would otherwise pin
   BOTH rows to the same sticky offset, stacking them instead of the second
   row sitting below the first. ~38px mirrors the same approximation .row-sub
   already uses elsewhere for "one thead row's rendered height". */
.gantt thead tr:last-child th{top:38px}

/* Pinned Activity column. This chart is ~2000px wide against a ~1600px window, so
   reaching a later period always means scrolling — and that used to carry the row's
   only identifier off-screen, leaving a grid of anonymous bars. Same sticky-column
   convention as table.data .col-actions (components.css §4), mirrored to the left
   edge. Gantt body cells are transparent, so the pinned column has to carry its own
   opaque ground or the timeline scrolls visibly underneath the text. z-index sits
   above the sticky thead's 2 so the corner cell wins where the two axes cross. */
.gantt thead th:first-child{left:0;z-index:4}
.gantt tbody td.cell-name,.gantt tfoot tr>*:first-child{position:sticky;left:0;z-index:3}
.gantt tbody td.cell-name{background:var(--surface)}
.gantt tfoot tr>*:first-child{background:var(--surface-2)}
.gantt tbody tr:hover td.cell-name{background:var(--surface-2)}
/* Keep the "done" tint on the pinned cell — an opaque background would otherwise
   erase the one row-level state the name column carries. */
.gantt tbody tr.is-done td.cell-name{background:var(--ok-50)}
[data-theme="dark"] .gantt tbody tr.is-done td.cell-name{background:var(--surface)}
/* Seam between the fixed identity column and the scrolling timeline. */
.gantt thead th:first-child,.gantt tbody td.cell-name,.gantt tfoot tr>*:first-child{
  box-shadow:inset -1px 0 0 var(--border)}

/* responsive */
@media (max-width:1100px){
  .panel-grid{grid-template-columns:minmax(0,1fr)}
  .kpi.hero{grid-column:span 2}
  .editor-layout{grid-template-columns:minmax(0,1fr)}
  /* A wrapping row of ragged variable-width chips loses the rail's one useful
     property — a scannable column of per-trade totals + status. A single
     horizontally-scrolling row of fixed-width cards keeps amounts aligned and
     the readiness dot visible instead. */
  .trade-list{position:static;flex-direction:row;flex-wrap:nowrap;overflow-x:auto;padding-bottom:4px}
  .trade-list .tli{flex:0 0 220px}
  .trade-list .tli .tli-amt{margin-left:auto}
}
/* BOM table gets its own scroll region so the sticky header pins to the top
   of the TABLE, not the page (where it was hiding behind the topbar once the
   user had scrolled far enough down a 70-row trade to need it). */
/* Alias of .table-wrap--pinned (components.css §4) with the BOM's own offset — it
   already made the wrap the scrollport; the shared sticky-tfoot rule is inherited
   by listing it in the same selector there. */
.table-wrap--bom{max-height:calc(100vh - 320px);overflow:auto}
.table-wrap--bom tfoot td,.table-wrap--bom tfoot th{
  position:sticky;bottom:0;background:var(--surface-2);z-index:2;border-top:1px solid var(--border)}
@media (max-width:840px){
  #app{grid-template-columns:1fr}
  .sidebar{position:fixed;left:0;top:0;z-index:60;transform:translateX(-100%);transition:transform .2s;width:260px}
  .sidebar.open{transform:none}
  .view{padding:var(--sp-4)}
  .kpi.hero{grid-column:span 1}
  .menu-btn{display:inline-flex!important}
  /* Drawer scrim: without it the open sidebar floats over still-visible,
     still-interactive content — tapping outside now dims the page and closes
     it (data-act="menu", same handler the hamburger button uses). */
  .drawer-scrim{display:none;position:fixed;inset:0;z-index:59;background:rgba(10,12,20,.45);animation:fade .15s ease}
  .sidebar.open ~ .drawer-scrim{display:block}
}
@media (min-width:841px){ .drawer-scrim{display:none!important} }
/* Topbar's own content (title, tier toggle, grand total, save dot, theme + report
   buttons) only needs ~550px unwrapped — this is its own, much narrower breakpoint
   than the 1100/840px layout ones above, so normal desktop/laptop windows never wrap
   it. Below this it wraps onto a second line rather than overflowing: the spacer is
   forced to its own full-width (zero-height) line, splitting "identity" (menu/title)
   from "actions" (tier/total/save/buttons). */
@media (max-width:700px){
  .topbar{flex-wrap:wrap;height:auto;min-height:var(--topbar-h);padding:10px var(--sp-4);row-gap:8px}
  .topbar .title{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .topbar .spacer{flex-basis:100%;width:0;height:0}
}
.menu-btn{display:none}

/* ============================================================
   POLISH LAYER — hierarchy & nav clarity
   ============================================================ */
/* Active nav: left accent bar (no layout shift) + clear fill */
.nav a{transition:background .13s,color .13s,box-shadow .13s}
.nav a.active{box-shadow:inset 3px 0 0 var(--brand-600);font-weight:var(--fw-bold)}
.nav-group{padding-top:18px}
.nav-group:first-child{padding-top:6px}

/* Topbar: hairline depth, generous gap */
.topbar{box-shadow:var(--shadow-sm);gap:var(--sp-3)}
.topbar .title{font-weight:var(--fw-bold);letter-spacing:-.01em}

/* KPI hierarchy: number is the hero, label is quiet */
.kpi .label{font-size:11px;letter-spacing:.06em;color:var(--text-3)}
.kpi .val{font-size:var(--fs-2xl);font-weight:var(--fw-heavy);letter-spacing:-.02em}
.kpi.hero .val{font-size:var(--fs-3xl)}
.kpi:hover{box-shadow:var(--shadow-md);transform:translateY(-2px);border-color:var(--border-strong)}
.kpi.hero:hover{border-color:transparent}

/* View header rhythm */
.view-head h1{letter-spacing:-.02em}
.view-head p{max-width:68ch}

/* Panels: clearer section headers */
.panel-head h3{font-weight:var(--fw-bold);letter-spacing:-.01em}

/* Cards in grids lift slightly on hover when actionable */
.trade-card:hover,.card[data-act]:hover{transform:translateY(-2px)}

/* Brand mark a touch more refined */
.brand .logo{box-shadow:var(--shadow-md)}

/* REP-6 — the Edit/Preview tab only exists where the layout has actually collapsed;
   on a wide screen both panes are visible and a tab would be noise. */
.rep-tabs{display:none;margin-bottom:12px}
@media (max-width:1100px){
  .rep-tabs{display:block}
  .editor-layout.rep-pane-edit > :nth-child(2){display:none}
  .editor-layout.rep-pane-preview > :nth-child(1){display:none}
}

/* Compact Gantt: hide the four derived numeric columns (Weight, Done, Earned,
   Remaining) and give the width to the timeline. Value = weight x contract,
   Progress = Done as a percentage, Remaining = Value - Earned — none of the four
   carry information the remaining five don't. Hidden with display:none rather than
   removed from the markup so print, CSV and the a11y tree keep the full table. */
.gantt--compact thead tr:first-child th:nth-child(4),
.gantt--compact thead tr:first-child th:nth-child(6),
.gantt--compact thead tr:first-child th:nth-child(8),
.gantt--compact thead tr:first-child th:nth-child(9),
.gantt--compact tbody tr td:nth-child(4),
.gantt--compact tbody tr td:nth-child(6),
.gantt--compact tbody tr td:nth-child(8),
.gantt--compact tbody tr td:nth-child(9),
.gantt--compact tfoot tr > *:nth-child(4),
.gantt--compact tfoot tr > *:nth-child(6),
.gantt--compact tfoot tr > *:nth-child(8),
.gantt--compact tfoot tr > *:nth-child(9){display:none}

/* ---- tier comparison (Reports) ----
   Three full documents abreast. Each column scrolls on its own so a long BOM in one
   tier cannot drag the other two out of alignment, and the whole strip scrolls
   horizontally on narrow screens rather than crushing three A4 pages into a phone. */
.tier-compare{display:grid;grid-template-columns:repeat(3,minmax(300px,1fr));gap:14px;
  align-items:start;overflow-x:auto;padding-bottom:6px}
.tier-col{background:var(--surface-3);border-radius:var(--r-lg);padding:12px;
  border:1px solid transparent;min-width:0}
/* The tier that will actually export is the one worth finding at a glance. */
.tier-col.is-active{border-color:var(--brand-fill);background:var(--brand-50)}
.tier-head{padding:2px 4px 10px}
.tier-doc{background:#fff;border-radius:6px;box-shadow:var(--shadow-md);
  max-height:calc(100vh - 300px);overflow:auto}
/* The report stylesheet is built for a full-width page; inside a third of the screen its
   38px side padding and 860px max-width would leave almost no room for the table. */
.tier-doc .au-report{padding:16px 14px;max-width:none}
@media (max-width:1100px){
  .tier-compare{grid-template-columns:repeat(3,minmax(280px,1fr))}
}

/* ---- COSTING TIER on the shell ----
   These live in layout.css, NOT components.css, on purpose: .topbar is defined in this
   file and layout.css is the last stylesheet loaded, so a tier rule sitting in
   components.css lost to it at equal specificity and the border stayed neutral grey.
   The tier hue itself comes from --tier, resolved off <html data-tier> in components.css. */
.topbar{border-bottom:2px solid var(--tier)}
/* Keyed off the ROOT's data-tier, not the button's .active class, and this matters:
   the tier switcher re-renders through the QUIET morph path, which was measured leaving the
   paint one step behind the class — after switching Direct→Internal the Direct button still
   had the old fill while carrying no .active class, and Internal had .active but no fill.
   A full notify() render resolves correctly, so the class itself is fine; the style just
   does not always follow it on that path. data-tier is stamped synchronously in
   store.setMode, so keying the fill to it cannot go stale. Safety signal — it has to be
   right on the first frame, not the second. */
[data-tier="direct"]   .segmented button[data-act="mode:direct"],
[data-tier="internal"] .segmented button[data-act="mode:internal"],
[data-tier="client"]   .segmented button[data-act="mode:client"]{
  background:var(--tier);color:#fff;border-color:var(--tier)}

/* ═══════════════════════════════════════════════════════════════════════════
   Trade detail — work column + reference rail
   ---------------------------------------------------------------------------
   Labour, past-project history, schedule activities and engineering notes were
   four collapsed accordions stacked under Quantities: present, but invisible
   unless you knew to open them. They now sit in a rail beside the Bill of
   Materials, under the trade's estimating tutorial, so the reference you need
   while typing is simply on screen.

   Below 1180px the grid collapses to one column and the rail falls BELOW the
   work column — on a narrow screen the BOM must not be pushed off the first
   screen by reference material.
   ═══════════════════════════════════════════════════════════════════════════ */
.trade-split{display:grid;grid-template-columns:minmax(0,1fr) 340px;gap:16px;align-items:start}
.trade-work{min-width:0}
.trade-rail{display:flex;flex-direction:column;gap:12px;min-width:0}
@media (max-width:1180px){ .trade-split{grid-template-columns:minmax(0,1fr)} }

.rail-card{background:var(--surface);border:1px solid var(--border);border-radius:var(--r-lg);overflow:hidden}

/* ---- tutorial ---- */
.tut-card{padding:14px}
.tut-art{background:var(--surface-2);border:1px solid var(--border);border-radius:var(--r-md,8px);
  padding:10px;margin-bottom:12px;display:flex;align-items:center;justify-content:center}
.tut-art svg{max-width:100%;height:auto;display:block}
.tut-title{margin:0 0 2px;font-size:var(--fs-md,15px);font-weight:800;letter-spacing:-.01em;
  text-transform:uppercase}
.tut-lead{margin:0 0 12px;font-size:var(--fs-xs);color:var(--text-3);line-height:1.5}
.tut-sec{margin-top:12px}
.tut-h{font-size:var(--fs-xs);font-weight:800;text-transform:uppercase;letter-spacing:.04em;
  color:var(--text-3);margin-bottom:5px}
.tut-h.tut-warn{color:var(--warn-700,#8a6100)}
.tut-steps{margin:0;padding-left:18px;font-size:var(--fs-xs);line-height:1.55;color:var(--text-2)}
.tut-steps li{margin-bottom:7px}
.tut-steps b{display:block;color:var(--text);font-weight:700}
.tut-steps span{display:block;margin-top:1px}
.tut-list{margin:0;padding-left:18px;font-size:var(--fs-xs);line-height:1.55;color:var(--text-2)}
.tut-list li{margin-bottom:4px}
.tut-eg{color:var(--text-2);font-variant-numeric:tabular-nums}
.tut-bad li::marker{color:var(--warn-700,#8a6100)}

/* The rail's accordions are narrower than the main column — tighten them so a
   long summary wraps instead of overflowing. */
.trade-rail .disclose-head{padding:10px 12px}
.trade-rail .disclose-title{font-size:var(--fs-sm);min-width:0;overflow-wrap:anywhere}
.trade-rail .disclose-meta{font-size:var(--fs-xs)}
