@import url("https://fonts.googleapis.com/css2?family=Urbanist:wght@400;500;600;700;800&display=swap");

/*
 * Portal/Admin design system — ported from klepon-marketing's confirmed
 * 2026 rebrand (see ../klepon-marketing/DESIGN.md and src/styles/global.css,
 * source: Klepon_2026_Huisstijl_v02A.pdf, confirmed 2026-08-28). This app
 * previously ran its own invented look (indigo/Inter, loosely modeled on a
 * third-party extension's UI); that's retired in favor of the real,
 * confirmed brand — navy ink + one signal green, Urbanist, pill buttons,
 * soft rounded cards. Token values below are copied from the marketing
 * repo's tokens, not re-derived — if that palette ever changes, re-copy
 * from there rather than hand-tuning here.
 *
 * The app-shell (sidebar/top bar) components at the bottom originate in
 * the licensing app's own dashboard — the marketing site has no dashboard
 * to draw that pattern from — but they read from the same tokens as
 * everything else in this file.
 *
 * COPIED VERBATIM from the licensing app's own klepon-theme.css into this
 * separate repo, deliberately — same confidentiality boundary that
 * justified the separate repo in the first place. If that file changes,
 * re-copy from there rather than hand-tuning here.
 */
:root {
  --color-bg: #f2f2f2;
  --color-surface: #e9e9ea;
  --color-text: #002e3a;
  --color-text-muted: color-mix(in srgb, var(--color-text) 55%, transparent);
  --color-divider: color-mix(in srgb, #002e3a 14%, transparent);

  --color-accent: #11a46e;
  --color-accent-100: #e6f8ef;
  --color-accent-200: #c2eeda;
  --color-accent-300: #8fdfb9;
  --color-accent-400: #55cb92;
  --color-accent-500: #2bb87a;
  --color-accent-600: #11a46e;
  --color-accent-700: #0c7d55;
  --color-accent-800: #095e40;
  --color-accent-900: #06432d;

  /* Not part of the brand palette — klepon-marketing's DESIGN.md is explicit that its secondary
     orange/amber are general-purpose accents, not semantic warning/error colors, so severity
     badges here use their own independent red/amber rather than borrowing those tokens. */
  --color-warning: #b45309;
  --color-warning-bg: #fef6e7;
  --color-danger: #dc2626;
  --color-danger-bg: #fdecec;

  --font-sans: "Urbanist", system-ui, sans-serif;
  --font-heading: "Urbanist", system-ui, sans-serif;

  --radius-sm: 2px;
  --radius-md: 4px;
  --radius-lg: 7px;
  --radius-2xl: 16px;
  --radius-3xl: 22px;
  --radius-full: 9999px;

  /* Flat, hairline-bordered surfaces (data tables, the project-info sidebar panel) — a
     deliberately different composition from .blueprint's floating rounded/shadowed cards, adopted
     for data-management views. See the "Tables" and "Flat panel" sections below. */
  --radius-panel: 10px;

  --shadow-elev-sm: 0 1px 2px color-mix(in srgb, #002e3a 10%, transparent);
  --shadow-elev-md: 0 3px 10px color-mix(in srgb, #002e3a 12%, transparent);
}

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  background: var(--color-bg);
  color: var(--color-text);
  font-family: var(--font-sans);
  font-size: 16px;
}

h1, h2, h3, h4 {
  font-family: var(--font-heading);
  font-weight: 800;
  line-height: 1.12;
  letter-spacing: -0.01em;
  margin: 0 0 0.5rem;
}

h1 { font-size: 1.875rem; }
h2 { font-size: 1.25rem; }
h3 { font-size: 1.0625rem; }

a {
  color: var(--color-accent-700);
  text-decoration: none;
}

a:hover {
  color: var(--color-accent-900);
}

:focus {
  outline: none;
}

:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}

::selection {
  background: color-mix(in srgb, var(--color-accent) 30%, transparent);
}

/* Eyebrow — the one place uppercase survives in this system: a small kicker line preceding a
   heading, or naming a stat-card's category. */
.eyebrow {
  display: block;
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: 0.8125rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--color-accent-700);
  margin-bottom: 0.35rem;
}

/* Card — white surface, generously rounded, resting shadow (not a hover-only state). Still
   named .blueprint so every page that already uses it picks up the new look with no markup
   changes; the old square-corner/hairline-border/corner-mark system it originally named is
   gone, same as on the marketing site. */
.blueprint {
  position: relative;
  border: none;
  border-radius: var(--radius-3xl);
  background: white;
  box-shadow: var(--shadow-elev-sm);
}

/* Corner registration marks — no equivalent in this system. Markup still emits the four .corner
   spans on every card (harmless, kept rather than touching every page) — this hides them. */
.corner {
  display: none;
}

/* Buttons — pill-shaped, always. .btn alone is the neutral/secondary action; .btn-primary is
   the one solid brand-green action; .btn-danger is reserved for the genuinely destructive one
   (Portal/DeleteAccount) — not part of the brand system, kept as this app's own addition. */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.4rem;
  cursor: pointer;
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: 1rem;
  line-height: 1.2;
  border-radius: var(--radius-full);
  border: 1px solid transparent;
  padding: 0.85rem 1.7rem;
  background: var(--color-surface);
  color: var(--color-text);
}

.btn:hover {
  background: color-mix(in srgb, var(--color-text) 7%, transparent);
  text-decoration: none;
}

/* Primary text is white, matching klepon-marketing's corrected convention (2026-08-31: an
   earlier version of that repo's DESIGN.md documented navy as the accessible default, but by
   then nearly every real button on the site already overrode it back to white to match the
   brand PDF's own swatch — white was the live convention, navy was the outlier. Ported here to
   match. The ~3.2:1 contrast at button-text size is a known, accepted tradeoff, not an
   oversight — see klepon-marketing/DESIGN.md's Buttons section before changing this back.) */
.btn-primary {
  background: var(--color-accent);
  border-color: var(--color-accent);
  color: white;
}

.btn-primary:hover {
  background: var(--color-accent-500);
  border-color: var(--color-accent-500);
  color: white;
}

.btn-primary:active {
  background: var(--color-accent-600);
  border-color: var(--color-accent-600);
}

.btn-sm {
  font-size: 0.875rem;
  padding: 0.6375rem 1.275rem;
}

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

.btn-danger:hover {
  background: color-mix(in srgb, var(--color-danger) 85%, black);
  border-color: color-mix(in srgb, var(--color-danger) 85%, black);
  color: #fff;
}

/* Inputs */
label {
  display: block;
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: 0.8125rem;
  color: var(--color-text);
  margin-bottom: 0.35rem;
}

input[type="text"], input[type="email"], input[type="password"], input[type="date"], select {
  width: 100%;
  font-family: var(--font-sans);
  font-size: 0.9375rem;
  color: var(--color-text);
  background: white;
  border: 1px solid var(--color-divider);
  border-radius: var(--radius-lg);
  padding: 0.65rem 0.85rem;
}

input[type="text"]:focus, input[type="email"]:focus, input[type="password"]:focus,
input[type="date"]:focus, select:focus {
  border-color: var(--color-accent);
}

input[type="checkbox"] {
  accent-color: var(--color-accent);
  margin-right: 0.35rem;
}

/* Notice bars — expiry warnings, pending-request status. Sit above the stat grid. */
.notice-bar {
  border-radius: var(--radius-3xl);
  padding: 1rem 1.25rem;
  margin-bottom: 1.25rem;
  font-size: 0.9375rem;
  background: var(--color-warning-bg);
}

.notice-bar-flat {
  border: none;
  background: var(--color-accent-100);
}

/* Stat cards (dashboard) */
.stat-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 1.25rem;
  margin: 1.5rem 0 2rem;
}

.stat-card {
  padding: 1.5rem;
}

.stat-value {
  font-family: var(--font-heading);
  font-size: 2.25rem;
  font-weight: 800;
  color: var(--color-text);
  line-height: 1;
}

.stat-value-muted {
  font-size: 1.125rem;
  font-weight: 500;
  color: var(--color-text-muted);
}

.stat-label {
  margin-top: 0.5rem;
  font-size: 0.8125rem;
  color: var(--color-text-muted);
}

/* Tables — flat and dense, not a floating card: hairline outer border, small radius, no shadow,
   tight row padding. Deliberately NOT .blueprint's language (22px radius + shadow) — this is the
   composition for data-management views (Projects list, Members), borrowed from Autodesk Forma's
   Data Management UI (composition only — palette/type/button-shape are unchanged, see
   klepon-marketing's confirmed rebrand). border-collapse:separate is what lets
   border-radius/overflow clip the header and last row's corners. */
table {
  width: 100%;
  border-collapse: separate;
  border-spacing: 0;
  background: white;
  border: 1px solid var(--color-divider);
  border-radius: var(--radius-panel);
  overflow: hidden;
}

th, td {
  text-align: left;
  padding: 0.7rem 0.9rem;
  border-bottom: 1px solid var(--color-divider);
  font-size: 0.875rem;
}

th {
  font-family: var(--font-sans);
  font-weight: 700;
  font-size: 0.75rem;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: var(--color-text-muted);
  background: var(--color-surface);
  white-space: nowrap;
}

tbody tr:last-child td {
  border-bottom: none;
}

tbody tr:hover td {
  background: var(--color-bg);
}

/* Flat panel — hairline-bordered, small radius, no shadow. Same surface language as tables,
   for non-tabular content that sits inside a data-management view (the sidebar's project-info
   card) rather than .blueprint's floating card look. */
.panel-flat {
  border: 1px solid var(--color-divider);
  border-radius: var(--radius-panel);
  background: white;
}

/* Spool drill-down uses the existing flat table/panel surfaces and brand tokens. */
.spool-status-editor { margin: 0 0 0.75rem; flex-wrap: wrap; align-items: center; }
.spool-status-editor h3 { margin: 0; }
.spool-select { width: auto; }
/* Select-all lives in the SELECT column's own header cell, next to what it selects, rather than
   as a separate labeled control off in the toolbar. */
.spool-select-all-cell { width: 2.5rem; text-align: center; }
.spool-select-all-cell input { width: auto; margin: 0; }

/* The toolbar's own bulk-status picker — deliberately plain/rectangular (the app's ordinary
   select style), not the pill shape below, so it doesn't read as another row's own status. It
   answers "apply this status to whatever's checked", not "this row's current status". */
.spool-bulk-target { width: auto; max-width: 100%; font-weight: 700; }

/* Inline per-row status editor: sits in the Prefab Status cell in place of the badge, sized to
   match it rather than a full-width form control. */
.spool-row-target {
  width: auto;
  padding: 0.25rem 0.5rem;
  font-family: var(--font-sans);
  font-weight: 700;
  font-size: 0.75rem;
  border-radius: var(--radius-2xl);
  border: 1px solid var(--color-divider);
  background: white;
  color: var(--color-text);
}

/* Floating "Save Changes" bar: fixed to the viewport bottom (not the panel), centered, so it
   only appears once a row (bulk-applied or inline-edited) is staged and always reads as a
   floating action rather than part of the table's own layout.
   The [hidden] rule must come after (or otherwise out-specificity) the unconditional display:flex
   below — an author stylesheet's normal-priority declaration beats the UA stylesheet's own
   `[hidden] { display: none }` at equal specificity, so without this the bar would ignore the
   `hidden` attribute entirely and show as soon as its panel is in the DOM, not once a change is
   actually staged. */
.spool-save-bar {
  position: fixed;
  left: 50%;
  bottom: 1.5rem;
  transform: translateX(-50%);
  z-index: 30;
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.75rem 1rem;
  background: white;
  border-radius: var(--radius-full);
  box-shadow: var(--shadow-elev-md);
}
.spool-save-bar[hidden] {
  display: none;
}
.spool-save-bar-count { font-size: 0.8125rem; color: var(--color-text-muted); }
.spool-save-bar-error { font-size: 0.8125rem; color: var(--color-danger); max-width: 22rem; }
.spool-save-bar-error[hidden] { display: none; }
.spool-empty { padding: 2rem; text-align: center; color: var(--color-text-muted); }
.spool-caption { font-size: 0.8125rem; color: var(--color-text-muted); }

/* Who/when changed this spool's status last — an (i) mark next to the badge/select, revealed as
   a tooltip on hover or keyboard focus, so the table stays one line per row. The button's own
   aria-label carries the full text for screen readers; the visual bubble is decorative. */
.spool-status-cell { display: flex; align-items: center; gap: 0.4rem; }
.spool-status-info {
  display: inline-flex; align-items: center; justify-content: center;
  width: 1.05rem; height: 1.05rem; border-radius: 50%; border: 1px solid var(--color-divider);
  background: transparent; color: var(--color-text-muted); font-size: 0.6875rem; font-weight: 700;
  font-family: var(--font-sans); cursor: pointer; padding: 0; position: relative; flex: none;
}
.spool-status-info:hover, .spool-status-info:focus-visible { color: var(--color-text); border-color: var(--color-text-muted); }

/* position: fixed, not absolute — deliberately escapes .spool-panel-inner's overflow:hidden (needed
   for the accordion's own expand/collapse animation) and the table wrap's clipping, so the bubble
   floats freely above whatever's underneath (including the next row, on a short table) instead of
   being clipped or squeezed into the panel's own box. _Spools.cshtml's JS sets top/left in viewport
   coordinates on every hover/focus, and toggles .is-below when there's no room above the trigger. */
.spool-status-tooltip {
  position: fixed; top: 0; left: 0; transform: translateX(-50%);
  display: flex; flex-direction: column; gap: 0.2rem; align-items: flex-start;
  background: var(--color-text); color: var(--color-bg); font-weight: 600; font-size: 0.75rem;
  padding: 0.5rem 0.65rem; border-radius: var(--radius-lg); white-space: nowrap;
  box-shadow: var(--shadow-elev-md); opacity: 0; visibility: hidden;
  transition: opacity 0.12s ease; pointer-events: none; z-index: 50;
}
.spool-status-tooltip strong { font-weight: 700; }
.spool-status-tooltip span, .spool-status-tooltip time { font-weight: 500; opacity: 0.85; }
.spool-status-tooltip::after {
  content: ""; position: absolute; left: 50%; transform: translateX(-50%);
  border: 5px solid transparent;
}
.spool-status-tooltip:not(.is-below)::after { top: 100%; border-top-color: var(--color-text); }
.spool-status-tooltip.is-below::after { bottom: 100%; border-bottom-color: var(--color-text); }
.spool-status-info:hover .spool-status-tooltip,
.spool-status-info:focus-visible .spool-status-tooltip { opacity: 1; visibility: visible; }
@media (prefers-reduced-motion: reduce) {
  .spool-status-tooltip { transition: none; }
}
.spool-table-wrap { max-width: 100%; overflow-x: auto; }
.spool-register { table-layout: fixed; }
.spool-register > thead th:first-child { width: 30%; }
.spool-register > thead th:nth-child(2) { width: 10%; }
.spool-register > thead th:nth-child(5) { width: 30%; }
.spool-mark-row { cursor: pointer; scroll-margin-top: 5rem; }
.spool-mark-row a { display: block; color: var(--color-text); font-weight: 700; text-decoration: none; }
.spool-mark-row a span { margin-right: 0.5rem; }
.spool-mark-row.active > td { background: var(--color-accent-100); }

/* Per-Mark status bar (_MarkStatusBar.cshtml) — one segment per status, furthest stage first. */
.mark-status { display: flex; flex-direction: column; gap: 0.3rem; }
.mark-status-bar { display: flex; height: 0.5rem; border-radius: 999px; overflow: hidden; background: var(--color-surface); border: 1px solid var(--color-divider); box-sizing: content-box; }
.mark-status-seg { flex-basis: 0; min-width: 0.2rem; }
.mark-status-installed { background: var(--color-accent-700); }
.mark-status-delivery { background: var(--color-accent-400); }
.mark-status-finish { background: #e9a23b; }
.mark-status-not-set { background: var(--color-surface); }
.mark-status-legend { display: flex; flex-wrap: wrap; gap: 0.4rem 1rem; margin: 0 0 0.75rem; padding: 0; list-style: none; font-size: 0.8125rem; color: var(--color-text-muted); }
.mark-status-legend li { display: flex; align-items: center; gap: 0.4rem; }
.mark-status-legend strong { color: var(--color-text); }
.mark-status-key { width: 0.75rem; height: 0.75rem; border-radius: 3px; box-shadow: inset 0 0 0 1px var(--color-divider); }
.mark-status-label { font-size: 0.75rem; color: var(--color-text-muted); }
.spool-register .spool-expanded-row > td { padding: 0; background: white; }

/* Accordion. grid-template-rows 0fr -> 1fr animates a panel whose height isn't known ahead of
   time; the grid lives inside the cell because a <tr>/<td> can't be a grid container without
   breaking table layout. */
.spool-panel { display: grid; grid-template-rows: 0fr; transition: grid-template-rows 0.28s ease; }
.spool-panel.open { grid-template-rows: 1fr; }
.spool-panel-inner { overflow: hidden; min-height: 0; }
.spool-mark-row[aria-busy="true"] { opacity: 0.6; }
.spool-mark-caret { display: inline-block; transition: transform 0.2s ease; }
.spool-mark-row a[aria-expanded="true"] .spool-mark-caret { transform: rotate(90deg); }

@media (prefers-reduced-motion: reduce) {
  .spool-panel, .spool-mark-caret, .app-sidebar, .app-sidebar-toggle svg { transition: none; }
  .tab-body-loading .spinner { animation: none; }
}
.spool-expanded { padding: 1.25rem; }
.spool-expanded > section + section { margin-top: 1.5rem; padding-top: 1.25rem; border-top: 1px solid var(--color-divider); }
.spool-expanded h3 { font-size: 1rem; margin: 0 0 0.75rem; }
.spool-section-image { padding: 1rem; }
.spool-section-heading { display: flex; justify-content: space-between; align-items: flex-start; gap: 1rem; flex-wrap: wrap; margin-bottom: 0.75rem; }
.spool-outlet-orientation { display: block; width: 128px; height: auto; max-width: 100%; margin-left: auto; flex-shrink: 0; }
.spool-section-image img { display: block; width: 100%; height: auto; }
.spool-stations th, .spool-stations td { text-align: center; padding: 0.4rem 0.6rem; white-space: nowrap; }
.spool-stations th { text-transform: none; }
.spool-detail-fields { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 1rem; margin: 0; }
.spool-detail-fields dt { font-size: 0.8125rem; color: var(--color-text-muted); }
.spool-detail-fields dd { margin: 0.2rem 0 0; overflow-wrap: anywhere; }
.spool-qr-toggle { white-space: nowrap; }
.spool-qr-list { display: flex; flex-wrap: wrap; gap: 1rem; padding: 0.75rem 0; }
.spool-qr-card { display: flex; align-items: center; gap: 1rem; margin: 0; padding: 0.75rem; }
.spool-qr-card img { display: block; width: 132px; height: auto; background: white; }
.spool-qr-card figcaption { min-width: 0; }
.spool-qr-label { font-weight: 700; overflow-wrap: anywhere; }
.spool-qr-text { margin: 0.25rem 0 0; font-size: 0.75rem; line-height: 1.5; color: var(--color-text-muted); white-space: pre-wrap; overflow-wrap: anywhere; }
@media (max-width: 700px) {
  .spool-detail-fields { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .spool-expanded { padding: 0.75rem; }
  .spool-register > thead th, .spool-mark-row > td { padding: 0.6rem 0.4rem; overflow-wrap: anywhere; }
  .spool-qr-card { flex-direction: column; align-items: flex-start; }
}

/* Toolbar — primary action (with its details/summary disclosure) + ghost utility actions + a
   view-toggle group, sitting directly above a data table. */
.toolbar {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  margin: 1.5rem 0 1rem;
}

.btn-ghost {
  background: white;
  border-color: var(--color-divider);
}

.btn-ghost:hover {
  background: var(--color-bg);
}

.btn:disabled, .btn-ghost:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.btn:disabled:hover { background: var(--color-surface); }
.btn-ghost:disabled:hover { background: white; }
.btn-primary:disabled, .btn-primary:disabled:hover {
  background: var(--color-accent);
  border-color: var(--color-accent);
}

.icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  border-radius: var(--radius-full);
  border: none;
  background: none;
  color: var(--color-text-muted);
  cursor: pointer;
}

.icon-btn:hover {
  background: var(--color-surface);
  color: var(--color-text);
}

.icon-btn:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}

.icon-btn:disabled:hover {
  background: none;
  color: var(--color-text-muted);
}

.view-toggle {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: 0.15rem;
  border: 1px solid var(--color-divider);
  border-radius: var(--radius-full);
  padding: 0.2rem;
  background: white;
}

.view-toggle .icon-btn.active {
  background: var(--color-accent-100);
  color: var(--color-accent-700);
}

/* Table row checkbox — plain and muted, distinct from the accent-colored checkboxes used in real
   forms elsewhere; disabled until bulk row actions exist. */
input.row-check {
  width: 16px;
  height: 16px;
  border-radius: 4px;
  accent-color: var(--color-text-muted);
  margin: 0;
}

/* Underline tabs — thin 2px active underline, no pill background. A second nav convention
   alongside .app-nav-item's pill highlight, used for sub-views within one page (e.g. Members'
   All/Invited filter) rather than for the sidebar. */
.tabs {
  display: flex;
  gap: 1.5rem;
  border-bottom: 1px solid var(--color-divider);
  margin-bottom: 1.25rem;
}

.tab {
  display: inline-flex;
  align-items: center;
  padding: 0 0 0.7rem;
  font-family: var(--font-sans);
  font-weight: 700;
  font-size: 0.9375rem;
  color: var(--color-text-muted);
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
}

.tab:hover {
  color: var(--color-text);
}

.tab.active {
  color: var(--color-text);
  border-bottom-color: var(--color-accent);
}

/* Shown in place of a tab's body while its async fetch is in flight — see Index.cshtml's
   fetch+swap script. Replaces the outgoing tab's content outright rather than dimming it in
   place, since the two tabs' content isn't related the way the Spools accordion's own panels are. */
.tab-body-loading {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.6rem;
  padding: 3rem 1rem;
  color: var(--color-text-muted);
  font-size: 0.9375rem;
}

.tab-body-loading .spinner {
  width: 1.1rem;
  height: 1.1rem;
  border-radius: var(--radius-full);
  border: 2px solid var(--color-divider);
  border-top-color: var(--color-accent);
  animation: tab-spin 0.7s linear infinite;
}

@keyframes tab-spin {
  to { transform: rotate(360deg); }
}

/* Badges — small compact chips (16px rounding, per DESIGN.md's chip radius — distinct from the
   full-pill radius reserved for buttons), colored by severity/state. */
.badge {
  display: inline-flex;
  align-items: center;
  padding: 0.25rem 0.7rem;
  font-family: var(--font-sans);
  font-weight: 700;
  font-size: 0.75rem;
  border-radius: var(--radius-2xl);
  border: none;
}

.badge-active, .badge-ok { background: var(--color-accent-100); color: var(--color-accent-700); }
.badge-suspended { background: var(--color-warning-bg); color: var(--color-warning); }
.badge-revoked, .badge-locked { background: var(--color-danger-bg); color: var(--color-danger); }

/* .badge-role-* — this repo's own addition, not part of the ported source file. Roles are a
   category, not a severity — unlike .badge-suspended/.badge-revoked and the Prefab Status chips
   below, no role is more "wrong" than another, so this deliberately does NOT reuse
   warning/danger/accent: those are this file's severity scale (ok → warning → danger) and
   borrowing them here would make an Installer or Expeditor badge read as risky/problematic next
   to an actually-suspended Status badge in the same row. Each role instead gets its own
   same-weight categorical color (light wash background + solid-ish text, matching the existing
   badge recipe), all picked outside the warning/danger/accent hues. Member stays neutral gray —
   it is the unmarked/default role, which gray already conveys correctly.
   Note: .badge-role-* is a display-only label — it carries no authorization on its own. Actual
   role permissions live in Services/PrefabStatusPolicy.cs. */
.badge-role-admin { background: #eaeafc; color: #4338ca; }
.badge-role-member { background: var(--color-surface); color: var(--color-text-muted); }
.badge-role-fabricator { background: #f3e8fc; color: #7e22ce; }
.badge-role-installer { background: #e3f0fc; color: #0369a1; }
.badge-role-expeditor { background: #e1f5f2; color: #0f766e; }
.badge-role-modeller { background: #fce7f0; color: #be185d; }

/* .badge-prefab-* — Prefab Status chips (Models/PrefabStatuses.cs), this repo's own addition same
   as .badge-role-*: reuses existing tokens rather than inventing new colors. Not Set/Finish
   Prefab/Delivery on Site read as a progression (neutral → warning → light accent); Already
   Installed is the one state that gets the solid accent fill, so the completed state reads as
   visually "heavier" than the merely-in-transit one rather than reusing the same light green. */
.badge-prefab-not-set { background: var(--color-surface); color: var(--color-text-muted); }
.badge-prefab-finish { background: var(--color-warning-bg); color: var(--color-warning); }
.badge-prefab-delivery { background: var(--color-accent-100); color: var(--color-accent-700); }
.badge-prefab-installed { background: var(--color-accent); color: white; }

/* Utilization bar — seats in use vs total, for offering/stat cards. Fill color signals how close
   to capacity: brand green, amber warning, red once no seat is left at all. */
.util-bar {
  margin-top: 0.6rem;
  height: 8px;
  width: 100%;
  background: var(--color-surface);
  border-radius: var(--radius-full);
  overflow: hidden;
}

.util-bar-fill {
  height: 100%;
  border-radius: var(--radius-full);
}

.util-bar-fill-ok { background: var(--color-accent); }
.util-bar-fill-warn { background: var(--color-warning); }
.util-bar-fill-full { background: var(--color-danger); }

/* Needs-attention panel (Admin dashboard) — a stack of compact, linkable rows grouped under a
   heading, each capped short with a "N more" link rather than repeating the full table. */
.attention-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 1.25rem;
  margin: 1.5rem 0 2rem;
}

.attention-panel {
  padding: 1.375rem;
}

.attention-panel h3 {
  font-size: 0.9375rem;
  margin: 0 0 0.7rem;
}

.attention-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
}

.attention-list li {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.75rem;
  font-size: 0.875rem;
  padding-bottom: 0.6rem;
  border-bottom: 1px solid var(--color-divider);
}

.attention-list li:last-child {
  border-bottom: none;
  padding-bottom: 0;
}

.attention-more {
  display: block;
  margin-top: 0.7rem;
  font-size: 0.8125rem;
}

.error {
  color: var(--color-danger);
  font-size: 0.875rem;
}

/* ============================================================
   App shell — sidebar + top bar chrome. This app's own addition
   (the marketing site has no dashboard to draw the pattern from);
   reads from the same tokens as everything else above.
   ============================================================ */
.app-shell {
  display: flex;
  min-height: 100vh;
  background: var(--color-bg);
}

.app-sidebar {
  width: 260px;
  transition: width 0.18s ease;
  flex-shrink: 0;
  position: sticky;
  top: 0;
  height: 100vh;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  padding: 1.5rem 1rem;
  background: white;
  border-right: 1px solid var(--color-divider);
}

.app-brand {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0 0.5rem;
  font-family: var(--font-heading);
  font-weight: 800;
  font-size: 1.0625rem;
  color: var(--color-text);
}

.app-brand:hover {
  color: var(--color-text);
  text-decoration: none;
}

.app-brand img {
  width: 24px;
  height: 24px;
}

/* Distinguishes the internal Admin area from the customer-facing Portal at a glance — also used
   standalone on the Admin login/access-denied cards (outside the sidebar). */
.wordmark-tag {
  font-family: var(--font-heading);
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--color-accent-700);
  background: var(--color-accent-100);
  padding: 0.15rem 0.55rem;
  border-radius: var(--radius-2xl);
}

.app-nav {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
}

.app-nav-item {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  padding: 0.55rem 0.7rem;
  border-radius: var(--radius-2xl);
  font-family: var(--font-sans);
  font-size: 0.875rem;
  font-weight: 600;
  color: var(--color-text-muted);
}

.app-nav-item:hover {
  background: var(--color-bg);
  color: var(--color-text);
  text-decoration: none;
}

.app-nav-item.active {
  background: var(--color-accent-100);
  color: var(--color-accent-700);
}

.app-nav-item svg {
  width: 18px;
  height: 18px;
  flex-shrink: 0;
}

.app-nav-badge {
  margin-left: auto;
  font-family: var(--font-sans);
  font-size: 0.625rem;
  font-weight: 700;
  color: #fff;
  background: var(--color-accent);
  padding: 0.1rem 0.45rem;
  border-radius: var(--radius-full);
}

.app-content {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
}

.app-topbar {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 1rem;
  padding: 1rem 2rem;
  background: white;
  box-shadow: var(--shadow-elev-sm);
  position: sticky;
  top: 0;
  z-index: 20;
}

/* Breadcrumb — a project page's "where am I" trail in the top bar, replacing a sidebar back-link.
   margin-right:auto pushes it to the left edge while identity/sign-out stay right (.app-topbar is
   justify-content:flex-end). */
.breadcrumb {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-right: auto;
  font-family: var(--font-sans);
  font-size: 0.8125rem;
  font-weight: 600;
}

.breadcrumb a {
  color: var(--color-text-muted);
}

.breadcrumb a:hover {
  color: var(--color-text);
}

.breadcrumb-sep {
  color: var(--color-text-muted);
  opacity: 0.5;
}

.breadcrumb > span:last-child {
  color: var(--color-text);
}

.app-identity {
  display: flex;
  align-items: center;
  gap: 0.6rem;
}

.app-identity-avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  border-radius: var(--radius-full);
  background: var(--color-accent);
  color: var(--color-text);
  font-family: var(--font-heading);
  font-weight: 800;
  font-size: 0.8125rem;
  flex-shrink: 0;
}

.app-identity-value {
  font-size: 0.8125rem;
  font-weight: 600;
  color: var(--color-text);
}

/* Portal's identity menu — a native <details>/<summary> disclosure (identity-menu.js only
   handles closing it on outside click/Escape, since <details> doesn't do that natively). Needs
   its own positioning: .app-identity-dropdown is absolutely placed off the wrapping <details>,
   not inline in the topbar's flex flow, or it just falls into the page below the topbar as
   plain text — which is what it did before this rule existed. */
.app-identity-menu {
  position: relative;
}

.app-identity-menu summary {
  list-style: none;
  cursor: pointer;
}

.app-identity-menu summary::-webkit-details-marker {
  display: none;
}

.app-identity-dropdown {
  position: absolute;
  top: calc(100% + 0.75rem);
  right: 0;
  z-index: 20;
  min-width: 240px;
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
  padding: 1rem;
  background: white;
  border-radius: var(--radius-2xl);
  box-shadow: var(--shadow-elev-md);
}

.app-identity-dropdown-header {
  padding-bottom: 0.75rem;
  border-bottom: 1px solid var(--color-divider);
}

.app-identity-dropdown-email {
  font-size: 0.875rem;
  font-weight: 700;
  color: var(--color-text);
  word-break: break-all;
}

.app-identity-dropdown-role {
  margin-top: 0.15rem;
  font-size: 0.75rem;
  color: var(--color-text-muted);
}

.app-identity-dropdown a {
  font-size: 0.875rem;
  font-weight: 600;
}

.app-identity-dropdown .app-logout {
  width: 100%;
}

/* Notification bell — same <details>/<summary> + .app-identity-dropdown pattern as the identity
   menu right next to it, just its own trigger icon and dropdown content. */
.app-notification-bell {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  border-radius: var(--radius-full);
  color: var(--color-text);
}

.app-notification-bell:hover,
.app-identity-menu[open] > summary.app-notification-bell {
  background: color-mix(in srgb, var(--color-text) 6%, transparent);
}

.app-notification-dot {
  position: absolute;
  top: 6px;
  right: 7px;
  width: 8px;
  height: 8px;
  border-radius: var(--radius-full);
  background: var(--color-danger);
  border: 1.5px solid white;
}

.app-notification-dropdown {
  width: 320px;
  max-height: 380px;
  overflow-y: auto;
  padding: 0;
  gap: 0;
}

.app-notification-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0.7rem 0.9rem;
  border-bottom: 1px solid var(--color-divider);
}

.app-notification-header-title {
  font-size: 0.75rem;
  font-weight: 700;
  color: var(--color-text-muted);
  text-transform: uppercase;
  letter-spacing: 0.03em;
}

.app-notification-mark-read button {
  font-size: 0.75rem;
  font-weight: 600;
  color: var(--color-accent-700);
  background: none;
  border: none;
  cursor: pointer;
  padding: 0;
  font-family: var(--font-sans);
}

.app-notification-empty {
  padding: 1.25rem 0.9rem;
  font-size: 0.8125rem;
  color: var(--color-text-muted);
}

.app-notification-item {
  display: block;
  padding: 0.65rem 0.9rem;
  text-decoration: none;
  border-bottom: 1px solid var(--color-divider);
}

.app-notification-item:last-child {
  border-bottom: none;
}

.app-notification-item:hover {
  background: color-mix(in srgb, var(--color-text) 4%, transparent);
}

.app-notification-item.unread {
  background: var(--color-accent-100);
}

.app-notification-message {
  display: block;
  font-size: 0.8125rem;
  font-weight: 600;
  color: var(--color-text);
  margin-bottom: 0.15rem;
}

.app-notification-time {
  display: block;
  font-size: 0.75rem;
  color: var(--color-text-muted);
}

.app-logout {
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: 0.8125rem;
  border-radius: var(--radius-full);
  border: 1px solid color-mix(in srgb, var(--color-text) 15%, transparent);
  background: transparent;
  color: var(--color-text);
  padding: 0.45rem 0.9rem;
  cursor: pointer;
}

.app-logout:hover {
  background: color-mix(in srgb, var(--color-text) 5%, transparent);
}

.app-main {
  flex: 1;
  width: 100%;
  max-width: 1120px;
  margin: 0 auto;
  padding: 2.5rem 2rem;
}

/* Two-tier sidebar — a secondary/utility nav group pinned near the bottom via margin-top:auto,
   separated from the primary group by a hairline divider. */
.app-nav-secondary {
  margin-top: auto;
  padding-top: 1rem;
}

.app-nav-secondary-divider {
  height: 1px;
  background: var(--color-divider);
  margin-bottom: 0.6rem;
}

.app-nav-item.disabled {
  opacity: 0.55;
  cursor: not-allowed;
}

/* The sidebar's project-info panel doubles as a link to Settings — a subtle hover state signals
   that without touching .panel-flat itself (also used for non-clickable panels elsewhere). */
.panel-flat-link:hover {
  border-color: color-mix(in srgb, var(--color-text) 25%, transparent);
}

/* ---- Sidebar collapse ----------------------------------------------------
   Two states only: the full 260px panel, or a 64px icon rail. The state is a
   data-sidebar attribute on <html> (see _ProjectLayout) rather than a class on
   .app-sidebar, so the script in <head> can set it before this stylesheet
   paints — every project page is a full load, and a class applied after paint
   would flash the wide panel on each navigation. */
.app-sidebar-head {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-bottom: 1.75rem;
}

.app-sidebar-toggle {
  margin-left: auto;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  padding: 0;
  border: 1px solid var(--color-divider);
  border-radius: var(--radius-2xl);
  background: white;
  color: var(--color-text-muted);
  cursor: pointer;
}

.app-sidebar-toggle:hover {
  background: var(--color-bg);
  color: var(--color-text);
}

.app-sidebar-toggle svg {
  width: 16px;
  height: 16px;
  transition: transform 0.18s ease;
}

.app-project-card {
  display: block;
  padding: 0.85rem 0.95rem;
  margin-bottom: 1.25rem;
}

.app-project-initial {
  display: none;
}

/* Labels are clipped rather than display:none so the icon rail keeps its
   accessible names for screen readers; sighted users get the title tooltip. */
[data-sidebar="collapsed"] .app-sidebar .collapse-hide {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

[data-sidebar="collapsed"] .app-sidebar {
  width: 64px;
  padding-left: 0.5rem;
  padding-right: 0.5rem;
}

[data-sidebar="collapsed"] .app-sidebar-head {
  flex-direction: column;
  gap: 0.75rem;
}

[data-sidebar="collapsed"] .app-brand {
  padding: 0;
  gap: 0;
}

[data-sidebar="collapsed"] .app-sidebar-toggle {
  margin-left: 0;
}

[data-sidebar="collapsed"] .app-sidebar-toggle svg {
  transform: rotate(180deg);
}

[data-sidebar="collapsed"] .app-project-card {
  padding: 0;
  margin-bottom: 1rem;
  border: 0;
  background: transparent;
}

[data-sidebar="collapsed"] .app-project-initial {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  margin: 0 auto;
  border-radius: var(--radius-2xl);
  background: var(--color-accent-100);
  color: var(--color-accent-700);
  font-family: var(--font-heading);
  font-weight: 800;
}

[data-sidebar="collapsed"] .app-nav-item {
  position: relative;
  justify-content: center;
  gap: 0;
  padding-left: 0;
  padding-right: 0;
}

.audit-log-entry { display:flex; gap:0.875rem; align-items:center; padding:1rem 1.15rem; border-bottom:1px solid var(--color-divider); }
.audit-log-entry:last-child { border-bottom:none; }
.audit-log-icon { width:2rem; height:2rem; border-radius:50%; display:grid; place-items:center; flex:0 0 auto; background:var(--color-accent-100); color:var(--color-accent-700); font-family:var(--font-heading); font-weight:800; font-size:0.8rem; }
.audit-log-meta, .audit-log-time { color:var(--color-text-muted); font-size:0.78rem; margin-top:0.18rem; }
.audit-log-time { text-align:right; white-space:nowrap; }

.spool-prefab-mark {
  margin-top: 0.3rem;
  color: var(--color-text);
  font-family: var(--font-heading);
  font-size: 1.75rem;
  font-weight: 900;
  letter-spacing: -0.025em;
  line-height: 1.1;
}

/* Dashboard fabrication-data tabs. Material Summary keeps the source desktop dashboard's four
   tables, but uses the Hub's underline-tab convention and responsive table wrapper. */
.material-tabs {
  display: flex;
  gap: 1.25rem;
  overflow-x: auto;
  border-bottom: 1px solid var(--color-divider);
  margin: 0.5rem 0 1rem;
}

.material-tab {
  flex: 0 0 auto;
  cursor: pointer;
  padding: 0 0 0.65rem;
  margin-bottom: -1px;
  border: none;
  border-bottom: 2px solid transparent;
  background: transparent;
  color: var(--color-text-muted);
  font: inherit;
  font-size: 0.875rem;
  font-weight: 700;
}

.material-tab:hover,
.material-tab.active { color: var(--color-text); }
.material-tab.active { border-bottom-color: var(--color-accent); }
.material-panel[hidden] { display: none; }

/* Docs is a read-only view of generated files embedded in each zone folder of the imported
   snapshot. Native details/summary keeps even a very large QR folder cheap to scan. */
.docs-explorer { display: grid; grid-template-columns: 230px minmax(0, 1fr); min-height: 34rem; overflow: hidden; }
.docs-navigation { padding: 0.75rem 0; border-right: 1px solid var(--color-divider); background: var(--color-surface); }
.docs-nav-item {
  display: flex;
  width: 100%;
  align-items: center;
  padding-left: calc(0.5rem + var(--docs-depth) * 0.8rem);
  background: transparent;
  color: var(--color-text);
  font-size: 0.8125rem;
}
.docs-nav-item[hidden] { display: none; }
.docs-nav-item:hover { background: var(--color-bg); }
.docs-nav-item.active { background: var(--color-accent-100); color: var(--color-accent-700); font-weight: 700; }
.docs-tree-toggle,
.docs-tree-spacer { width: 1.45rem; height: 2rem; flex: 0 0 1.45rem; }
.docs-tree-toggle { display: grid; place-items: center; padding: 0; border: 0; background: transparent; color: var(--color-text-muted); font: inherit; cursor: pointer; }
.docs-tree-toggle:hover { color: var(--color-text); }
.docs-nav-open { min-width: 0; flex: 1; padding: 0.42rem 0.75rem 0.42rem 0; overflow: hidden; border: 0; background: transparent; color: inherit; font: inherit; text-align: left; text-overflow: ellipsis; white-space: nowrap; cursor: pointer; }
.docs-browser { min-width: 0; background: white; }
.docs-toolbar { display: flex; align-items: center; justify-content: space-between; gap: 1rem; min-height: 4rem; padding: 0.75rem 1rem; border-bottom: 1px solid var(--color-divider); }
.docs-navigation-tools { display: flex; min-width: 0; flex: 1; align-items: center; gap: 0.75rem; }
.docs-history { display: inline-flex; flex: 0 0 auto; align-items: center; }
.docs-history-button {
  display: inline-grid;
  width: 2rem;
  height: 2rem;
  place-items: center;
  border: 1px solid transparent;
  border-radius: 0.35rem;
  background: transparent;
  color: var(--color-text);
  font: inherit;
  font-size: 1.15rem;
  cursor: pointer;
}
.docs-history-button:hover:not(:disabled) { border-color: var(--color-divider); background: var(--color-bg); }
.docs-history-button:disabled { color: var(--color-text-muted); cursor: default; opacity: 0.4; }
.docs-location { display: flex; min-width: 0; align-items: center; overflow-x: auto; color: var(--color-text-muted); font-size: 0.8125rem; white-space: nowrap; }
.docs-path-segment { flex: 0 0 auto; padding: 0.35rem 0.45rem; border: 0; border-radius: 0.3rem; background: transparent; color: inherit; font: inherit; cursor: pointer; }
.docs-path-segment:hover { background: var(--color-bg); color: var(--color-text); }
.docs-path-segment:last-child { color: var(--color-text); font-weight: 600; }
.docs-path-separator { flex: 0 0 auto; color: var(--color-text-muted); font-size: 1rem; }
.docs-table { border: 0; border-radius: 0; }
.docs-table th, .docs-table td { padding: 0.58rem 0.75rem; }
.docs-table th:first-child, .docs-table td:first-child { width: 42px; }
.docs-table th:nth-child(2) { width: 42%; }
.docs-table td { color: var(--color-text-muted); }
.docs-table td:nth-child(2) { color: var(--color-text); }
.docs-check-cell { text-align: center; }
.docs-check-cell input { width: 16px; height: 16px; margin: 0; }
.docs-name { display: inline-flex; align-items: center; gap: 0.55rem; padding: 0; border: 0; background: transparent; color: var(--color-text); font: inherit; font-weight: 600; cursor: pointer; }
.docs-name:hover { color: var(--color-accent-700); }
.docs-name span { width: 1.1rem; text-align: center; }
.docs-row[hidden] { display: none; }

@media (max-width: 700px) {
  .docs-explorer { grid-template-columns: 1fr; }
  .docs-navigation { max-height: 15rem; overflow-y: auto; border-right: 0; border-bottom: 1px solid var(--color-divider); }
  .docs-toolbar { align-items: stretch; flex-direction: column; }
  .docs-download { align-self: flex-end; }
}

/* Login / signup / access-denied cards */
.auth-shell {
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--color-bg);
}

.auth-card {
  width: 340px;
  padding: 2.25rem 2rem;
}

.auth-card form {
  display: flex;
  flex-direction: column;
  gap: 1rem;
  margin-top: 1.25rem;
}

.auth-card button[type="submit"] {
  margin-top: 0.5rem;
}

/* ---------------------------------------------------------------------------------------------
   Construction (Pages/Projects/Index.cshtml) — zone list, zone Overview, and the 3D status tab.
   --------------------------------------------------------------------------------------------- */
.app-main.app-main-dashboard { max-width: none; margin: 0; padding: 2rem clamp(24px, 2vw, 32px); container-type: inline-size; }

/* The zone tab bar scrolls sideways on a phone instead of wrapping or overflowing the page. */
.tabs-scroll { overflow-x: auto; scrollbar-width: none; }
.tabs-scroll .tab { white-space: nowrap; flex-shrink: 0; }

.zone-crumb { display: flex; align-items: center; gap: 0.5rem; font-size: 0.8125rem; font-weight: 600; margin-bottom: 0.35rem; }
.zone-crumb a { color: var(--color-text-muted); }
.zone-crumb > span:last-child { color: var(--color-text); }

/* Zone list — every card opens that zone. */
.zone-card { display: block; color: inherit; text-decoration: none; transition: box-shadow 0.15s ease, transform 0.15s ease; }
.zone-card:hover { text-decoration: none; box-shadow: var(--shadow-elev-md); transform: translateY(-1px); }
.zone-card:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 2px; }
.zone-card-head { display: flex; justify-content: space-between; align-items: center; gap: 0.5rem; }
.zone-card-head h3 { font-size: 1.25rem; margin: 0; }
.zone-card-go { font-size: 1.5rem; line-height: 1; color: var(--color-text-muted); }
.zone-card:hover .zone-card-go { color: var(--color-accent-700); }
.zone-kpis { grid-template-columns: repeat(4, 1fr); gap: 0.6rem; margin: 1rem 0; }

/* Cumulative stage progress (_StageProgress.cshtml) and the all-zone Total Progress panel. */
.stage-progress { display: flex; flex-direction: column; gap: 0.7rem; }
.stage-row-head { display: flex; align-items: center; gap: 0.5rem; font-size: 0.8125rem; font-weight: 600; margin-bottom: 0.3rem; }
.stage-row-head .badge { font-variant-numeric: tabular-nums; }
.stage-row-head strong { margin-left: auto; font-variant-numeric: tabular-nums; }
.stage-track { height: 8px; border-radius: var(--radius-full); background: var(--color-surface); overflow: hidden; }
.stage-track > span { display: block; height: 100%; border-radius: inherit; }
.all-zone-progress { margin-top: 1.5rem; padding: 1.25rem; }
.all-zone-progress h2 { margin-bottom: 1rem; }
.all-zone-progress .stage-progress { gap: 1rem; }
.all-zone-stage-label { margin-bottom: 0.35rem; font-size: 0.875rem; font-weight: 600; }
.all-zone-stage-bar { display: flex; align-items: center; gap: 0.75rem; }
.all-zone-stage-bar .stage-track { flex: 1; height: 12px; }
.all-zone-stage-bar strong { min-width: 3rem; text-align: right; font-size: 0.875rem; font-variant-numeric: tabular-nums; }

/* Prefab Status fills for bars and charts, matching the .badge-prefab-* chips. */
.seg-not-set { background: color-mix(in srgb, var(--color-text) 16%, transparent); }
.seg-prefab { background: var(--color-warning); }
.seg-delivery { background: var(--color-accent-300); }
.seg-installed { background: var(--color-accent); }

/* Zone Overview (_ZoneOverview.cshtml) */
.zone-ov-kpis { grid-template-columns: repeat(5, 1fr); gap: 0.75rem; margin: 1rem 0; }
.zone-ov-panel { padding: 1.25rem; margin-top: 1rem; min-width: 0; }
.zone-ov-panel-head { display: flex; justify-content: space-between; align-items: baseline; gap: 1rem; margin-bottom: 1rem; }
.zone-ov-panel-head h3 { margin: 0; font-size: 1rem; }
.zone-ov-panel-head a { font-size: 0.8125rem; font-weight: 600; }
.zone-ov-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0 1rem; }
.zone-ov-wide { grid-column: 1 / -1; }
.mark-matrix { display: flex; flex-direction: column; gap: 0.25rem; overflow-x: auto; }
.mark-matrix-row { display: grid; grid-template-columns: minmax(6.5rem, 1.3fr) repeat(4, minmax(0, 1fr)); min-width: 30rem; gap: 0.25rem; align-items: center; color: inherit; font-size: 0.8125rem; border-radius: var(--radius-md); padding: 0.15rem 0.25rem; }
a.mark-matrix-row:hover { background: var(--color-bg); text-decoration: none; }
.mark-matrix-head { font-size: 0.6875rem; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; color: var(--color-text-muted); }
.mark-matrix-head > span:not(:first-child) { text-align: center; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.mark-matrix-mark { font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.mark-matrix-cell { display: flex; align-items: center; justify-content: center; height: 1.6rem; border-radius: 4px; font-weight: 700; font-variant-numeric: tabular-nums; color: white; }
.mark-matrix-cell.seg-not-set, .mark-matrix-cell.seg-delivery { color: var(--color-text); }
.mark-matrix-cell.is-zero { opacity: 0.3; }
.mark-chart { display: flex; flex-direction: column; gap: 0.4rem; }
.mark-chart-row { display: grid; grid-template-columns: 7.5rem 1fr auto; align-items: center; gap: 0.75rem; font-size: 0.8125rem; padding: 0.15rem 0.25rem; }
.mark-chart-label { font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.mark-chart-track { height: 14px; }
.mark-chart-bar { display: flex; height: 100%; border-radius: 3px; overflow: hidden; min-width: 4px; }
.material-bar { background: var(--color-accent-400); }
.mark-chart-value { font-variant-numeric: tabular-nums; font-weight: 600; text-align: right; white-space: nowrap; }
.activity-chart { display: block; width: 100%; height: auto; }
.activity-chart.seg-prefab, .activity-chart.seg-delivery, .activity-chart.seg-installed { background: transparent; }
.activity-chart .ac-bar { fill: var(--color-accent); }
.activity-chart.seg-prefab .ac-bar { fill: var(--color-warning); }
.activity-chart.seg-delivery .ac-bar { fill: var(--color-accent-300); }
.activity-chart .ac-axis { stroke: var(--color-divider); stroke-width: 1; }
.activity-chart .ac-grid { stroke: var(--color-divider); stroke-dasharray: 3 3; }
.activity-chart .ac-tick { font: 500 10px var(--font-sans); fill: var(--color-text-muted); }
.activity-chart .ac-value { font: 700 10px var(--font-sans); fill: var(--color-text); }

/* 3D status (_Viewer.cshtml, wwwroot/js/prefab-viewer.js). The status colours are the viewer's
   own: they have to read on a white WebGL background with lighting applied. */
.model-viewer-panel { margin-top: 1.25rem; overflow: hidden; }
.viewer-toolbar { display: flex; align-items: center; justify-content: flex-end; flex-wrap: wrap; gap: 0.75rem; padding: 1rem; border-bottom: 1px solid var(--color-divider); }
.model-viewer { position: relative; height: clamp(360px, 62vh, 760px); background: #fff; }
.model-viewer canvas { display: block; width: 100%; height: 100%; touch-action: none; }
.model-viewer canvas:focus-visible { outline: 2px solid var(--color-accent); outline-offset: -3px; }
.viewer-message { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; padding: 2rem; text-align: center; color: var(--color-text-muted); }
.viewer-footer { padding: 1rem; }
.viewer-footer p { margin: 0.6rem 0 0; }
.viewer-legend { display: flex; flex-wrap: wrap; gap: 0.6rem 1.25rem; font-size: 0.85rem; }
.viewer-legend span { display: inline-flex; align-items: center; gap: 0.4rem; }
.viewer-legend i { display: inline-block; width: 0.75rem; height: 0.75rem; border-radius: 50%; }
.viewer-legend .seg-not-set { background: #d1d7de; opacity: 0.45; }
.viewer-legend .seg-prefab { background: #e88713; }
.viewer-legend .seg-delivery { background: #35b5e5; }
.viewer-legend .seg-installed { background: #11a46e; }
.viewer-legend .seg-modified { background: #dc2626; }
.viewer-properties { position: absolute; top: 1rem; right: 1rem; z-index: 1; width: 220px; padding: 1rem; border: 1px solid var(--color-divider); border-radius: var(--radius-lg); background: #fff; box-shadow: var(--shadow-elev-md); }
.viewer-properties[hidden] { display: none; }
.viewer-properties dl { margin: 0 0 0.8rem; }
.viewer-properties dl div + div { margin-top: 0.75rem; }
.viewer-properties dt { font-size: 0.75rem; color: var(--color-text-muted); }
.viewer-properties dd { margin: 0.2rem 0 0; font-weight: 600; overflow-wrap: anywhere; }

/* Measured against the content area (.app-main-dashboard is the container), so an expanded
   sidebar collapses the grid as readily as a narrow window does. */
@container (max-width: 1100px) {
  .zone-ov-kpis { grid-template-columns: repeat(3, 1fr); }
}
@container (max-width: 900px) {
  .zone-ov-grid { grid-template-columns: minmax(0, 1fr); }
}
@media (max-width: 700px) {
  .app-main.app-main-dashboard { padding: 1.25rem 1rem; }
  .zone-kpis, .zone-ov-kpis { grid-template-columns: repeat(2, 1fr); }
  .mark-chart-row { grid-template-columns: 5.5rem 1fr auto; }
  .mark-matrix-row { grid-template-columns: minmax(5rem, 1.2fr) repeat(4, minmax(0, 1fr)); }
}

/* Prefab Work (_PrefabWork.cshtml, wwwroot/js/prefab-work.js) and the step / stage progress
   cells it shares with Prefab Mark Status (_MarkStatusBar.cshtml). */
.prefab-work-tabs { margin-top: 0; margin-bottom: 1rem; }
.work-step-pipeCut { background: var(--color-text); }
.work-step-endPreparation { background: var(--color-accent-400); }
.work-step-finish { background: var(--color-accent-700); }
.work-status-progress { display: flex; flex-direction: column; gap: 0.5rem; }
.work-status-step-head { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 0.5rem; font-size: 0.75rem; margin-bottom: 0.2rem; }
.work-status-step-head strong { font-variant-numeric: tabular-nums; }
.work-status-progress .stage-track { height: 6px; }
.work-table-status { min-width: 9rem; }
.work-table-status .stage-track { height: 6px; }
.work-register-row { cursor: pointer; }
.work-register-row.active > td { background: var(--color-accent-100); }
.work-register-detail > td { padding: 0; background: white; }
.work-row-toggle { border: 0; background: none; padding: 0; color: var(--color-text); font: inherit; font-weight: 700; cursor: pointer; text-align: left; }
.work-row-toggle:focus-visible { outline: 2px solid var(--color-text); outline-offset: 4px; }
.work-row-toggle[aria-expanded="true"] .spool-mark-caret { transform: rotate(90deg); }
.work-quantity-input { width: 6rem; }
.spool-work-list th:nth-child(n+3):nth-child(-n+5), .spool-work-check { text-align: center; }
.spool-work-check input { accent-color: var(--color-accent); width: 1rem; height: 1rem; cursor: pointer; }
.spool-work-check input:disabled { cursor: default; }
.work-step-header { display: inline-flex; align-items: center; justify-content: center; gap: 0.5rem; margin: 0; font: inherit; color: inherit; }
.work-step-header input { flex-shrink: 0; width: 1rem; height: 1rem; accent-color: var(--color-accent); }
.spool-row-dirty > td { background: var(--color-warning-bg); }
.spool-action { white-space: nowrap; }
.spool-detail-work { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.spool-detail-work > div:nth-child(n+5) { grid-column: span 2; }
@media (max-width: 700px) {
  .spool-detail-work { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .spool-detail-work > div:nth-child(n+5) { grid-column: auto; }
}

/* Modified on Site: an Installed spool adjusted on site after prefabrication (HubSpoolStatus.ModifiedOnSite). */
.badge-prefab-modified { background: #fee2e2; color: #b91c1c; }
.seg-modified { background: #dc2626; }
.modified-label { color: #dc2626; font-weight: 700; }
.stage-track.installed-track { display: flex; }
.installed-track > span { border-radius: 0; flex-shrink: 0; }
.mark-matrix-cell.seg-installed { gap: 0.35rem; }
.mark-matrix-modified { background: #dc2626; color: white; padding: 0 0.4rem; border-radius: 3px; }
.mark-matrix-head > span:last-child { white-space: normal; overflow: visible; text-overflow: clip; line-height: 1.4; }

/* Prefab Mark Status 3D side panel (_MarkStatus.cshtml): table left, viewer right at about
   63% / 37%, split by a draggable separator. One column (viewer on top) once the content area is
   900px or narrower — measured on .app-main-dashboard, so an open sidebar counts too. */
.mark-status-layout { display: grid; grid-template-columns: minmax(0, 1fr); align-items: start; gap: 0.5rem; }
.mark-status-layout.has-viewer { grid-template-columns: minmax(0, 1fr) 12px clamp(320px, var(--viewer-width, 37%), 50%); }
.mark-status-table-panel { min-width: 0; }
.mark-status-resizer { position: sticky; top: 5rem; height: calc(100vh - 6rem); cursor: col-resize; touch-action: none; display: grid; place-items: center; border-radius: 6px; outline-offset: 2px; }
.mark-status-resizer[hidden], .mark-status-viewer[hidden] { display: none; }
.mark-status-resizer span { height: 48px; width: 4px; background: var(--color-divider); border-radius: 4px; transition: background 0.15s; }
.mark-status-resizer:hover, .mark-status-resizer.is-resizing, .mark-status-resizer:focus-visible { background: var(--color-accent-100); }
.mark-status-resizer:hover span, .mark-status-resizer.is-resizing span, .mark-status-resizer:focus-visible span { background: var(--color-accent); }
.mark-status-resizer.is-resizing { user-select: none; }
.mark-status-viewer { position: sticky; top: 5rem; min-width: 0; max-height: calc(100vh - 6rem); border: 1px solid var(--color-divider); border-radius: 16px; background: white; overflow: auto; box-shadow: var(--shadow-elev-md); }
.mark-status-viewer-header { display: flex; justify-content: space-between; align-items: flex-start; gap: 0.75rem; padding: 1rem 1.25rem; border-bottom: 1px solid var(--color-divider); }
.mark-status-viewer-header h3 { margin: 0.25rem 0; font-size: 1.1rem; }
.mark-status-viewer-header p { margin: 0; font-size: 0.75rem; color: var(--color-text-muted); }
.mark-status-viewer-close { display: grid; place-items: center; width: 32px; height: 32px; flex-shrink: 0; border: 1px solid var(--color-divider); border-radius: 50%; background: white; color: var(--color-text-muted); cursor: pointer; font-size: 1.4rem; line-height: 1; }
.mark-status-viewer-close:hover { background: var(--color-bg); color: var(--color-text); }
.mark-status-viewer .model-viewer-panel { border: 0; border-radius: 0; margin: 0; padding: 1rem; box-shadow: none; }
.mark-status-viewer .viewer-toolbar { padding: 0 0 0.75rem; border-bottom: 0; }
.mark-status-viewer .model-viewer { height: clamp(230px, 36vh, 420px); min-height: 230px; }
.mark-status-viewer .viewer-properties { top: auto; bottom: 0.5rem; left: 0.5rem; right: 0.5rem; width: auto; display: flex; align-items: center; justify-content: space-between; gap: 0.5rem; padding: 0.65rem 0.75rem; }
.mark-status-viewer .viewer-properties[hidden] { display: none; }
.mark-status-viewer .viewer-properties dl { display: flex; gap: 1rem; margin: 0; }
.mark-status-viewer .viewer-properties dl div + div { margin-top: 0; }
.mark-status-viewer .viewer-properties dt { font-size: 0.625rem; }
.mark-status-viewer .viewer-properties dd { font-size: 0.75rem; }
.mark-status-viewer .viewer-footer { padding: 0.75rem 0 0; }
.mark-status-viewer .viewer-footer .spool-caption { font-size: 0.6875rem; margin: 0.4rem 0 0; }
.mark-status-viewer [data-viewer-summary] { display: none; }
.spool-viewer-selected > td { background: #edf7ff; }
.spool-viewer-selected > td:first-child { box-shadow: inset 3px 0 #2563eb; }
@container (max-width: 900px) {
  .mark-status-layout.has-viewer { grid-template-columns: minmax(0, 1fr); }
  .mark-status-layout.has-viewer .mark-status-viewer { grid-row: 1; position: static; max-height: none; }
  .mark-status-layout.has-viewer .mark-status-resizer { display: none; }
}
