/* =========================================================================
   Mind AI - design system
   Engineered by DATXOC Limited

   One stylesheet for every page. The palette is cool and near-black: this is
   an instrument for reading engineering drawings, and a warm or bright shell
   competes with the drawing colours it has to display faithfully. Accent blue
   carries interaction, violet pairs with it for depth, and ember is reserved
   for work in progress - the forge running - so a glance at a list tells you
   what is moving without reading a word.

   Colour is never the only signal: every status pill carries a text label,
   and every chart series is distinguishable by position and label as well as
   by hue.
   ========================================================================= */

:root {
  color-scheme: dark;

  /* ---- surface ---- */
  --bg:            #07090d;
  --surface:       #0e1219;
  --raised:        #151b24;
  --overlay:       #1c242f;
  --hover:         #202a37;

  --border:        #232b38;
  --border-strong: #323d4d;

  /* ---- text ---- */
  --text:          #e8ecf3;
  --text-2:        #97a3b6;
  --text-3:        #5e6b80;

  /* ---- accent ---- */
  --accent:        #4c8dff;
  --accent-hi:     #6ba1ff;
  --accent-dim:    rgba(76, 141, 255, 0.14);
  --accent-line:   rgba(76, 141, 255, 0.42);
  --accent-2:      #8b5cf6;
  --gradient:      linear-gradient(135deg, #4c8dff 0%, #8b5cf6 100%);

  /* ---- state ---- */
  --ember:         #ff8a3d;              /* running / forging */
  --ember-dim:     rgba(255, 138, 61, 0.14);
  --ok:            #2dd4a7;
  --ok-dim:        rgba(45, 212, 167, 0.14);
  --warn:          #f5b841;
  --warn-dim:      rgba(245, 184, 65, 0.14);
  --danger:        #ff5c7a;
  --danger-dim:    rgba(255, 92, 122, 0.14);

  /* ---- type ---- */
  --font-display: 'Sora', -apple-system, 'Segoe UI', sans-serif;
  --font-body:    'Inter', -apple-system, 'Segoe UI', Arial, sans-serif;
  --font-mono:    'JetBrains Mono', ui-monospace, Consolas, monospace;

  /* ---- geometry ---- */
  --r-sm: 6px;
  --r:    10px;
  --r-lg: 16px;
  --sidebar-w: 236px;
  --shadow:    0 2px 8px rgba(0, 0, 0, .34);
  --shadow-lg: 0 18px 48px rgba(0, 0, 0, .52);
}

* { box-sizing: border-box; }

html, body { height: 100%; }

body {
  margin: 0;
  font-family: var(--font-body);
  font-size: 14px;
  line-height: 1.55;
  background: var(--bg);
  color: var(--text);
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3, h4 { font-family: var(--font-display); font-weight: 600; margin: 0; letter-spacing: -.01em; }
a { color: var(--accent); text-decoration: none; }
a:hover { color: var(--accent-hi); }
.mono { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }
.muted { color: var(--text-2); }
.faint { color: var(--text-3); }
.tnum  { font-variant-numeric: tabular-nums; }

/* Scrollbars are light enough to actually see.

   The thumb used to be --raised (#151b24) on --bg (#07090d): about five
   percent apart in lightness, which on a dark theme means an invisible
   scrollbar and no way to tell how far down a long page you are. The thumb now
   uses a text-weight grey so it reads as a control, and the track is a shade
   above the page so the trough is visible even when the thumb is elsewhere.

   The thumb is the accent blue, held back to about three quarters strength at
   rest: enough to read as a deliberate control and to show position at a
   glance, without a full-saturation bar shouting down the edge of every long
   page. Hover and drag take it to full accent, so grabbing it confirms itself.

   scrollbar-color covers Firefox, which ignores the ::-webkit- rules. */
:root {
  --scroll-thumb:       #4073cb;   /* accent at ~75%, 3.7:1 on the track */
  --scroll-thumb-hover: #4c8dff;   /* --accent */
  --scroll-thumb-active:#6ba1ff;   /* --accent-hi */
  --scroll-track:       #151c28;   /* the page, nudged toward the accent hue */
}

html {
  scrollbar-color: var(--scroll-thumb) var(--scroll-track);
  scrollbar-width: auto;
}

::-webkit-scrollbar { width: 13px; height: 13px; }
::-webkit-scrollbar-track { background: var(--scroll-track); }
::-webkit-scrollbar-thumb {
  background: var(--scroll-thumb);
  border-radius: 7px;
  /* Inset by the track colour so the thumb reads as a rounded bar with room
     around it rather than filling the gutter edge to edge. */
  border: 3px solid var(--scroll-track);
  background-clip: padding-box;
}
::-webkit-scrollbar-thumb:hover { background: var(--scroll-thumb-hover); }
::-webkit-scrollbar-thumb:active { background: var(--scroll-thumb-active); }
::-webkit-scrollbar-corner { background: var(--scroll-track); }

/* Visible focus for keyboard users, suppressed for pointer clicks. */
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 4px; }

.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

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

.sidebar {
  width: var(--sidebar-w); flex: none; background: var(--surface);
  border-right: 1px solid var(--border);
  display: flex; flex-direction: column; position: sticky; top: 0; height: 100vh;
  transition: width .22s cubic-bezier(.2, .9, .3, 1);
}

/* Collapsed: icons only. Labels are removed from the flow rather than hidden
   with opacity, so nothing reflows oddly at the narrow width, and every icon
   keeps a title attribute so the nav is still usable. */
.sidebar.collapsed { --sidebar-w: 66px; }
.sidebar.collapsed .brand-name,
.sidebar.collapsed .brand-sub,
.sidebar.collapsed .nav-label,
.sidebar.collapsed .nav a span:not(.badge),
.sidebar.collapsed .usercard .who,
.sidebar.collapsed .sidebar-foot { display: none; }
.sidebar.collapsed .brand { justify-content: center; padding-left: 0; padding-right: 0; }
.sidebar.collapsed .nav a { justify-content: center; padding-left: 0; padding-right: 0; }
.sidebar.collapsed .nav .badge {
  position: absolute; margin: 0; transform: translate(13px, -11px);
  padding: 0 5px; font-size: 10px;
}
.sidebar.collapsed .nav a { position: relative; }
.sidebar.collapsed .usercard { justify-content: center; }

@media (prefers-reduced-motion: reduce) { .sidebar { transition: none; } }

.collapse-btn {
  display: flex; align-items: center; justify-content: center; gap: 8px;
  width: calc(100% - 20px); margin: 0 10px 10px; padding: 7px;
  background: transparent; border: 1px solid transparent; border-radius: var(--r-sm);
  color: var(--text-3); font: inherit; font-size: 12px; cursor: pointer;
}
.collapse-btn:hover { background: var(--raised); color: var(--text-2); }
.collapse-btn svg { width: 15px; height: 15px; transition: transform .22s; }
.sidebar.collapsed .collapse-btn { width: calc(100% - 12px); margin: 0 6px 10px; }
.sidebar.collapsed .collapse-btn span { display: none; }
.sidebar.collapsed .collapse-btn svg { transform: rotate(180deg); }

.brand {
  display: flex; align-items: center; gap: 10px;
  padding: 18px 18px 16px; border-bottom: 1px solid var(--border);
}
/* The LionCraft crest, cropped from the supplied artwork (a 1.75:1 lockup of
   crest + "LION CRAFT" -- the wordmark half is not used anywhere).

   No tile behind it: the crest is white line art and reads on its own against
   the dark shell, where a filled square just boxed it in. It is sized a little
   larger than the old tile so it keeps the same visual weight without one. */
.brand-mark {
  width: 34px; height: 34px; flex: none;
  display: grid; place-items: center;
}
.brand-mark img {
  width: 100%; height: 100%; object-fit: contain; display: block;
}
.brand-mark.lg { width: 44px; height: 44px; }

.brand-name { font-family: var(--font-display); font-weight: 600; font-size: 15px; letter-spacing: -.02em; }
.brand-sub { font-size: 10px; color: var(--text-3); letter-spacing: .06em; text-transform: uppercase; }

.nav { flex: 1; padding: 12px 10px; overflow-y: auto; }
.nav-label {
  font-size: 10px; text-transform: uppercase; letter-spacing: .1em;
  color: var(--text-3); padding: 14px 10px 6px;
}
.nav a {
  display: flex; align-items: center; gap: 10px; padding: 9px 10px;
  border-radius: var(--r-sm); color: var(--text-2); font-size: 13.5px;
  font-weight: 500; margin-bottom: 2px; transition: background .12s, color .12s;
}
.nav a:hover { background: var(--raised); color: var(--text); }
.nav a.active { background: var(--accent-dim); color: var(--accent-hi); }
.nav a.active svg { color: var(--accent); }
.nav svg { width: 16px; height: 16px; flex: none; }
.nav .badge {
  margin-left: auto; background: var(--ember-dim); color: var(--ember);
  font-size: 10.5px; font-weight: 600; padding: 1px 6px; border-radius: 20px;
  font-variant-numeric: tabular-nums;
}

.sidebar-foot { padding: 12px 14px 14px; border-top: 1px solid var(--border); font-size: 11px; color: var(--text-3); }
.sidebar-foot a { color: var(--text-2); }

.usercard {
  display: flex; align-items: center; gap: 10px; padding: 10px 12px;
  border-top: 1px solid var(--border); cursor: pointer;
}
.usercard:hover { background: var(--raised); }
.avatar {
  width: 30px; height: 30px; border-radius: 50%; flex: none;
  background: var(--gradient); display: grid; place-items: center;
  font-size: 11.5px; font-weight: 700; color: #fff; overflow: hidden;
}
/* The same class is used for an uploaded image and for the initials
   fallback, so the two are always the same shape and size. */
img.avatar { object-fit: cover; display: block; }
.usercard .who { min-width: 0; }
.usercard .name { font-size: 13px; font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.usercard .org { font-size: 11px; color: var(--text-3); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

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

.topbar {
  display: flex; align-items: center; gap: 14px; padding: 16px 26px;
  border-bottom: 1px solid var(--border); background: var(--bg);
  position: sticky; top: 0; z-index: 20;
}
.topbar h1 { font-size: 19px; }
.topbar .sub { font-size: 12.5px; color: var(--text-3); margin-top: 1px; }
.topbar .spacer { flex: 1; }

.content { padding: 24px 26px 48px; flex: 1; min-width: 0; }
.content.flush { padding: 0; display: flex; min-height: 0; }

@media (max-width: 900px) {
  .sidebar { position: fixed; z-index: 60; transform: translateX(-100%); transition: transform .2s; }
  .sidebar.open { transform: none; box-shadow: var(--shadow-lg); }
  .content { padding: 18px 16px 40px; }
  .topbar { padding: 14px 16px; }
}
/* The nav toggle only exists below the breakpoint where the sidebar turns
   into an overlay. Qualified with `.btn` on purpose: a bare `.menu-btn` ties
   with `.btn`'s own `display`, and since `.btn` is defined later it would win
   and leave a hamburger sitting next to the page title at every width. */
.btn.menu-btn { display: none; }
@media (max-width: 900px) { .btn.menu-btn { display: inline-flex; } }

/* =========================================================================
   buttons
   ========================================================================= */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  padding: 9px 15px; border-radius: var(--r-sm); border: 1px solid transparent;
  background: var(--accent); color: #05080f; font: inherit; font-size: 13.5px;
  font-weight: 600; cursor: pointer; white-space: nowrap;
  transition: filter .13s, background .13s, border-color .13s, opacity .13s;
}
.btn:hover:not(:disabled) { filter: brightness(1.1); }
.btn:disabled { opacity: .45; cursor: not-allowed; }
.btn svg { width: 15px; height: 15px; }

.btn.secondary { background: var(--raised); color: var(--text); border-color: var(--border-strong); }
.btn.secondary:hover:not(:disabled) { background: var(--hover); border-color: var(--accent-line); filter: none; }
.btn.ghost { background: transparent; color: var(--text-2); border-color: transparent; }
.btn.ghost:hover:not(:disabled) { background: var(--raised); color: var(--text); filter: none; }
.btn.danger { background: var(--danger); color: #1a0409; }
.btn.ok { background: var(--ok); color: #03120d; }
.btn.sm { padding: 6px 11px; font-size: 12.5px; }
.btn.lg { padding: 12px 22px; font-size: 15px; }
.btn.block { width: 100%; }
.btn.gradient { background: var(--gradient); color: #fff; }

.btn-row { display: flex; gap: 8px; flex-wrap: wrap; }

/* segmented control */
.seg { display: inline-flex; gap: 4px; padding: 3px; background: var(--raised); border: 1px solid var(--border); border-radius: var(--r-sm); }
.seg button {
  padding: 6px 13px; border: none; background: transparent; color: var(--text-2);
  font: inherit; font-size: 12.5px; font-weight: 500; border-radius: 5px; cursor: pointer;
}
.seg button:hover { color: var(--text); }
.seg button[aria-pressed="true"] { background: var(--accent-dim); color: var(--accent-hi); }

/* =========================================================================
   forms
   ========================================================================= */
label { display: block; font-size: 12.5px; color: var(--text-2); margin-bottom: 5px; font-weight: 500; }
.field { margin-bottom: 14px; }
.field .hint { font-size: 11.5px; color: var(--text-3); margin-top: 4px; font-weight: 400; }
.field .hint.error { color: var(--danger); }

input[type=text], input[type=email], input[type=password], input[type=tel],
input[type=number], input[type=search], textarea, select {
  width: 100%; padding: 9px 11px; font: inherit; font-size: 13.5px;
  background: var(--raised); color: var(--text);
  border: 1px solid var(--border); border-radius: var(--r-sm);
  transition: border-color .13s, background .13s;
}
input::placeholder, textarea::placeholder { color: var(--text-3); }
input:focus, textarea:focus, select:focus {
  outline: none; border-color: var(--accent); background: var(--overlay);
}
input:disabled, textarea:disabled { opacity: .55; cursor: not-allowed; }
textarea { resize: vertical; min-height: 66px; line-height: 1.5; }
select {
  appearance: none; cursor: pointer; padding-right: 30px;
  background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath fill='%2397a3b6' d='M6 8.2 2.1 4.3l.8-.8L6 6.6l3.1-3.1.8.8z'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 10px center; background-size: 12px;
}
.grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 0 14px; }
.grid-3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: 0 14px; }
@media (max-width: 640px) { .grid-2, .grid-3 { grid-template-columns: 1fr; } }

/* =========================================================================
   cards & layout
   ========================================================================= */
.card {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r); padding: 18px;
}
.card.pad-0 { padding: 0; }
.card-head {
  display: flex; align-items: center; gap: 10px; margin-bottom: 14px;
}
.card-head h2, .card-head h3 { font-size: 14px; font-weight: 600; }
.card-head .spacer { flex: 1; }
.card-title {
  font-size: 10.5px; text-transform: uppercase; letter-spacing: .09em;
  color: var(--text-3); font-weight: 600; margin-bottom: 10px;
}

.cards { display: grid; gap: 14px; }
.cards.c2 { grid-template-columns: repeat(2, 1fr); }
.cards.c3 { grid-template-columns: repeat(3, 1fr); }
.cards.c4 { grid-template-columns: repeat(4, 1fr); }
@media (max-width: 1100px) { .cards.c4 { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 860px)  { .cards.c3, .cards.c2 { grid-template-columns: 1fr; } }
@media (max-width: 560px)  { .cards.c4 { grid-template-columns: 1fr; } }

/* stat tile */
.stat .k { font-size: 11px; color: var(--text-3); text-transform: uppercase; letter-spacing: .07em; font-weight: 600; }
.stat .v {
  font-family: var(--font-display); font-size: 27px; font-weight: 600;
  margin-top: 6px; letter-spacing: -.02em; font-variant-numeric: tabular-nums;
}
.stat .d { font-size: 12px; color: var(--text-2); margin-top: 3px; }
.stat .v.accent { color: var(--accent-hi); }
.stat .v.ok { color: var(--ok); }
.stat .v.ember { color: var(--ember); }

/* =========================================================================
   status pills
   ========================================================================= */
.pill {
  display: inline-flex; align-items: center; gap: 5px; padding: 2.5px 9px;
  border-radius: 20px; font-size: 11.5px; font-weight: 600; white-space: nowrap;
  border: 1px solid transparent; letter-spacing: .01em;
}
.pill::before { content: ""; width: 5px; height: 5px; border-radius: 50%; background: currentColor; flex: none; }
.pill.queued    { background: var(--raised);     color: var(--text-2); border-color: var(--border-strong); }
.pill.running   { background: var(--ember-dim);  color: var(--ember);  border-color: rgba(255,138,61,.4); }
.pill.succeeded { background: var(--ok-dim);     color: var(--ok);     border-color: rgba(45,212,167,.4); }
.pill.failed    { background: var(--danger-dim); color: var(--danger); border-color: rgba(255,92,122,.4); }
.pill.cancelled { background: var(--raised);     color: var(--text-3); border-color: var(--border); }
.pill.pending   { background: var(--warn-dim);   color: var(--warn);   border-color: rgba(245,184,65,.4); }
.pill.approved  { background: var(--ok-dim);     color: var(--ok);     border-color: rgba(45,212,167,.4); }
.pill.rejected  { background: var(--danger-dim); color: var(--danger); border-color: rgba(255,92,122,.4); }
.pill.not_required { display: none; }
.pill.info      { background: var(--accent-dim); color: var(--accent-hi); border-color: var(--accent-line); }
.pill.plain::before { display: none; }

/* A running pill breathes, so a long queue reads as alive at a glance. */
.pill.running::before { animation: pulse 1.6s ease-in-out infinite; }
@keyframes pulse { 0%,100% { opacity: 1; } 50% { opacity: .3; } }
@media (prefers-reduced-motion: reduce) { .pill.running::before { animation: none; } }

/* =========================================================================
   tables
   ========================================================================= */
.table-wrap { overflow-x: auto; border-radius: var(--r); }
table.data { width: 100%; border-collapse: collapse; font-size: 13px; }
table.data th {
  text-align: left; font-size: 10.5px; text-transform: uppercase; letter-spacing: .08em;
  color: var(--text-3); font-weight: 600; padding: 10px 12px;
  border-bottom: 1px solid var(--border); white-space: nowrap; background: var(--surface);
  position: sticky; top: 0; z-index: 1;
}
table.data td { padding: 11px 12px; border-bottom: 1px solid var(--border); vertical-align: middle; }
table.data tbody tr { transition: background .1s; }
table.data tbody tr:hover { background: var(--raised); }
table.data tbody tr.clickable { cursor: pointer; }
table.data tbody tr:last-child td { border-bottom: none; }
table.data td.num { text-align: right; font-variant-numeric: tabular-nums; font-family: var(--font-mono); font-size: 12px; }
table.data .primary { font-weight: 500; }
table.data .secondary { font-size: 11.5px; color: var(--text-3); margin-top: 1px; }

/* =========================================================================
   toasts
   ========================================================================= */
#toasts {
  position: fixed; bottom: 20px; right: 20px; z-index: 200;
  display: flex; flex-direction: column; gap: 9px; max-width: 380px;
}
.toast {
  background: var(--overlay); border: 1px solid var(--border-strong);
  border-left: 3px solid var(--accent); border-radius: var(--r-sm);
  padding: 11px 14px; font-size: 13px; box-shadow: var(--shadow-lg);
  animation: toast-in .22s cubic-bezier(.2,.9,.3,1);
}
.toast.error { border-left-color: var(--danger); }
.toast.ok    { border-left-color: var(--ok); }
.toast.warn  { border-left-color: var(--warn); }
.toast .t { font-weight: 600; margin-bottom: 2px; }
.toast .m { color: var(--text-2); font-size: 12.5px; }
.toast.has-action { display: flex; align-items: center; gap: 14px; }
.toast-text { min-width: 0; flex: 1; }
.toast-act {
  flex: none; background: none; border: 1px solid var(--border-strong); border-radius: var(--r-sm);
  color: var(--accent-hi); font: 600 12.5px var(--font-body); padding: 5px 10px; cursor: pointer;
}
.toast-act:hover { background: var(--accent-dim); border-color: var(--accent-line); }
@keyframes toast-in { from { opacity: 0; transform: translateY(10px); } }

/* =========================================================================
   modal
   ========================================================================= */
.modal {
  position: fixed; inset: 0; z-index: 100; display: none;
  align-items: center; justify-content: center; padding: 20px;
  background: rgba(3, 5, 9, .76); backdrop-filter: blur(3px);
}
.modal.open { display: flex; }
.modal-card {
  background: var(--surface); border: 1px solid var(--border-strong);
  border-radius: var(--r-lg); width: 100%; max-width: 560px;
  max-height: 86vh; display: flex; flex-direction: column;
  box-shadow: var(--shadow-lg); animation: modal-in .18s cubic-bezier(.2,.9,.3,1);
}
.modal-card.wide { max-width: 860px; }

/* Near-full-screen, for anything containing a viewer. A drawing sheet or a 3D
   room is the content, not an illustration next to it, so the dialog gets out
   of the way and gives it the window. */
.modal-card.full {
  max-width: none; width: 96vw; height: 94vh; max-height: 94vh;
}
.modal.full-host { padding: 0; }
.modal-card.full .modal-body {
  display: flex; flex-direction: column; min-height: 0;
  /* The inner panes own the scrolling in a full-screen dialog. Leaving the
     body scrollable too put two scrollbars side by side at the right edge. */
  overflow: hidden;
}
@keyframes modal-in { from { opacity: 0; transform: translateY(12px) scale(.985); } }
.modal-head {
  display: flex; align-items: center; gap: 12px; padding: 16px 20px;
  border-bottom: 1px solid var(--border);
}
.modal-head h2 { font-size: 15px; flex: 1; }
.modal-body { padding: 20px; overflow-y: auto; flex: 1; }
.modal-foot {
  display: flex; gap: 8px; justify-content: flex-end; padding: 14px 20px;
  border-top: 1px solid var(--border);
}
.x-btn {
  background: none; border: none; color: var(--text-3); cursor: pointer;
  font-size: 20px; line-height: 1; padding: 2px 6px; border-radius: 4px;
}
.x-btn:hover { color: var(--text); background: var(--raised); }

/* =========================================================================
   empty / loading
   ========================================================================= */
.empty { text-align: center; padding: 44px 20px; color: var(--text-3); }
.empty .icon { font-size: 30px; opacity: .4; margin-bottom: 10px; }
.empty h3 { font-size: 14.5px; color: var(--text-2); margin-bottom: 5px; }
.empty p { font-size: 13px; margin: 0 0 14px; }

.skeleton {
  background: linear-gradient(90deg, var(--raised) 25%, var(--overlay) 50%, var(--raised) 75%);
  background-size: 200% 100%; animation: shimmer 1.3s infinite; border-radius: var(--r-sm);
}
@keyframes shimmer { to { background-position: -200% 0; } }
.skeleton.line { height: 12px; margin-bottom: 8px; }
@media (prefers-reduced-motion: reduce) { .skeleton { animation: none; } }

.spinner {
  width: 15px; height: 15px; border: 2px solid var(--border-strong);
  border-top-color: var(--accent); border-radius: 50%;
  animation: spin .7s linear infinite; display: inline-block; vertical-align: -2px;
}
@keyframes spin { to { transform: rotate(360deg); } }

/* progress bar */
.bar { height: 4px; background: var(--raised); border-radius: 3px; overflow: hidden; }
.bar > span { display: block; height: 100%; background: var(--gradient); transition: width .3s; }

/* =========================================================================
   charts
   ========================================================================= */
.chart-box { position: relative; height: 240px; }
.chart-box.short { height: 190px; }
.legend { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 12px; font-size: 12px; color: var(--text-2); }
.legend .item { display: flex; align-items: center; gap: 6px; }
.legend .dot { width: 9px; height: 9px; border-radius: 3px; flex: none; }

/* =========================================================================
   marketing pages
   ========================================================================= */
.site { max-width: 1140px; margin: 0 auto; padding: 0 24px; }

.site-nav {
  display: flex; align-items: center; gap: 14px; padding: 20px 0;
  position: sticky; top: 0; background: rgba(7, 9, 13, .82);
  backdrop-filter: blur(10px); z-index: 30; border-bottom: 1px solid transparent;
}
.site-nav.stuck { border-bottom-color: var(--border); }
.site-nav .spacer { flex: 1; }
.site-nav a.link { color: var(--text-2); font-size: 13.5px; font-weight: 500; }
.site-nav a.link:hover { color: var(--text); }

.hero { padding: 72px 0 60px; position: relative; overflow: hidden; }
.hero::before {
  /* A soft field of light behind the headline, so the near-black page has a
     focal point without adding a decorative image to download. */
  content: ""; position: absolute; top: -220px; left: 50%; transform: translateX(-50%);
  width: 900px; height: 560px; pointer-events: none;
  background: radial-gradient(ellipse at center, rgba(76,141,255,.17), transparent 68%);
}
.hero > * { position: relative; }
.eyebrow {
  display: inline-flex; align-items: center; gap: 7px; padding: 5px 13px;
  border: 1px solid var(--accent-line); background: var(--accent-dim);
  border-radius: 20px; font-size: 12px; font-weight: 600; color: var(--accent-hi);
  margin-bottom: 22px;
}
.hero h1 {
  font-size: clamp(34px, 5.6vw, 58px); line-height: 1.05; letter-spacing: -.035em;
  max-width: 17ch; margin-bottom: 20px;
}
.hero h1 .grad {
  background: var(--gradient); -webkit-background-clip: text;
  background-clip: text; color: transparent;
}
.hero p.lede { font-size: 17.5px; color: var(--text-2); max-width: 60ch; margin: 0 0 30px; line-height: 1.6; }

.section { padding: 58px 0; }
.section-head { margin-bottom: 34px; max-width: 62ch; }
.section-head h2 { font-size: clamp(25px, 3.4vw, 34px); letter-spacing: -.025em; margin-bottom: 12px; }
.section-head p { color: var(--text-2); font-size: 15.5px; margin: 0; }

.feature {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-lg); padding: 24px; transition: border-color .15s, transform .15s;
}
.feature:hover { border-color: var(--border-strong); transform: translateY(-2px); }
.feature .ico {
  width: 38px; height: 38px; border-radius: 10px; background: var(--accent-dim);
  display: grid; place-items: center; margin-bottom: 14px; color: var(--accent);
}
.feature .ico svg { width: 19px; height: 19px; }
.feature h3 { font-size: 15.5px; margin-bottom: 7px; }
.feature p { color: var(--text-2); font-size: 13.5px; margin: 0; line-height: 1.6; }

.steps { counter-reset: step; display: grid; gap: 18px; }
.step { display: flex; gap: 16px; align-items: flex-start; }
.step .n {
  counter-increment: step; flex: none; width: 32px; height: 32px; border-radius: 9px;
  background: var(--raised); border: 1px solid var(--border-strong);
  display: grid; place-items: center; font-family: var(--font-mono);
  font-size: 13px; font-weight: 600; color: var(--accent-hi);
}
.step .n::before { content: counter(step, decimal-leading-zero); }
.step h3 { font-size: 15px; margin-bottom: 5px; }
.step p { color: var(--text-2); font-size: 13.5px; margin: 0; line-height: 1.6; }

.cta {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-lg); padding: 44px 34px; text-align: center;
  position: relative; overflow: hidden;
}
.cta::before {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: radial-gradient(ellipse at 50% 0%, rgba(139,92,246,.13), transparent 62%);
}
.cta > * { position: relative; }
.cta h2 { font-size: clamp(23px, 3.2vw, 31px); margin-bottom: 12px; letter-spacing: -.025em; }
.cta p { color: var(--text-2); max-width: 52ch; margin: 0 auto 24px; font-size: 15px; }

.site-foot {
  border-top: 1px solid var(--border); margin-top: 58px; padding: 30px 0 38px;
  color: var(--text-3); font-size: 13px;
}
.site-foot .row { display: flex; gap: 18px; align-items: center; flex-wrap: wrap; }
.site-foot .spacer { flex: 1; }
.site-foot a { color: var(--text-2); }
.site-foot a:hover { color: var(--text); }

/* =========================================================================
   auth pages
   ========================================================================= */
.auth-wrap { min-height: 100vh; display: grid; place-items: center; padding: 34px 20px; position: relative; overflow: hidden; }
.auth-wrap::before {
  content: ""; position: absolute; top: -280px; left: 50%; transform: translateX(-50%);
  width: 840px; height: 620px; pointer-events: none;
  background: radial-gradient(ellipse at center, rgba(76,141,255,.14), transparent 68%);
}
.auth-card {
  position: relative; width: 100%; max-width: 420px;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-lg); padding: 32px; box-shadow: var(--shadow-lg);
}
.auth-card.wide { max-width: 490px; }
.auth-brand { display: flex; align-items: center; gap: 10px; justify-content: center; margin-bottom: 24px; }
.auth-card h1 { font-size: 21px; text-align: center; margin-bottom: 6px; letter-spacing: -.02em; }
.auth-card .sub { text-align: center; color: var(--text-2); font-size: 13.5px; margin: 0 0 24px; }
.auth-foot { text-align: center; margin-top: 20px; font-size: 13px; color: var(--text-3); }
.auth-credit { text-align: center; margin-top: 26px; font-size: 11.5px; color: var(--text-3); }
.auth-credit a { color: var(--text-2); }

.alert {
  padding: 10px 13px; border-radius: var(--r-sm); font-size: 13px;
  margin-bottom: 16px; border: 1px solid transparent; display: none;
}
.alert.show { display: block; }
.alert.error { background: var(--danger-dim); border-color: rgba(255,92,122,.4); color: #ffb3c1; }
.alert.ok    { background: var(--ok-dim);     border-color: rgba(45,212,167,.4);  color: #8ceed3; }
.alert.info  { background: var(--accent-dim); border-color: var(--accent-line);   color: var(--accent-hi); }

/* =========================================================================
   3D viewer
   ========================================================================= */
.viewer-pane {
  flex: 1; min-width: 0; position: relative; overflow: hidden;
  background: var(--bg); display: flex;
}
.viewer-pane canvas {
  display: block; cursor: grab;
  /* A renderer that mis-sizes the canvas must never widen the layout around
     it; clamping here keeps a sibling column from being pushed off-screen. */
  max-width: 100%; max-height: 100%;
}
.viewer-pane canvas:active { cursor: grabbing; }

.viewer-hover {
  position: absolute; pointer-events: none; z-index: 5;
  background: var(--overlay); border: 1px solid var(--accent-line);
  color: var(--text); font-size: 12px; padding: 4px 9px; border-radius: 5px;
  transform: translate(-50%, -140%); white-space: nowrap;
  opacity: 0; transition: opacity .1s; box-shadow: var(--shadow);
}

.viewer-tr {
  position: absolute; top: 14px; right: 14px; z-index: 4;
  display: flex; flex-direction: column; align-items: flex-end; gap: 8px;
  max-width: min(340px, 60%);
}
.viewer-br {
  position: absolute; bottom: 14px; right: 14px; z-index: 4;
  display: flex; flex-direction: column; align-items: flex-end; gap: 8px;
}
.viewer-bl {
  position: absolute; left: 16px; bottom: 16px; z-index: 3;
  font-size: 11.5px; color: var(--text-3); pointer-events: none; max-width: 52%;
}

.viewer-status {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-sm); padding: 10px 13px; font-size: 12.5px;
  color: var(--text-2); line-height: 1.55; white-space: pre-wrap;
  text-align: right; box-shadow: var(--shadow); display: none;
}
.viewer-status.show { display: block; }
.viewer-status b { color: var(--text); font-weight: 600; }

.viewer-tools { display: flex; gap: 6px; }
.viewer-tools .btn { background: var(--surface); border-color: var(--border); color: var(--text-2); }
.viewer-tools .btn.on { border-color: var(--accent); color: var(--accent-hi); background: var(--accent-dim); }

.viewer-empty {
  position: absolute; inset: 0; display: grid; place-items: center;
  text-align: center; padding: 30px; color: var(--text-3); z-index: 2;
}
.viewer-empty .big { font-size: 40px; opacity: .25; margin-bottom: 12px; }

/* flag / refine popover */
.flag-pop {
  background: var(--surface); border: 1px solid var(--border-strong);
  border-radius: var(--r); padding: 14px; width: 290px;
  box-shadow: var(--shadow-lg); display: none;
}
.flag-pop.show { display: block; }
.flag-pop label { font-size: 11.5px; }
.flag-pop .chips { display: flex; flex-wrap: wrap; gap: 4px; margin-bottom: 10px; max-height: 96px; overflow-y: auto; }
.flag-chip {
  font-size: 11px; background: var(--danger-dim); color: var(--danger);
  border: 1px solid rgba(255,92,122,.4); border-radius: 20px; padding: 2px 8px;
  max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* =========================================================================
   wall / box result list
   ========================================================================= */
.result-panel {
  width: 380px; flex: none; border-left: 1px solid var(--border);
  background: var(--surface); display: flex; flex-direction: column; min-height: 0;
}
.result-panel.left { border-left: none; border-right: 1px solid var(--border); }
.result-panel .head { padding: 16px 18px 12px; border-bottom: 1px solid var(--border); }
.result-panel .body { flex: 1; overflow-y: auto; padding: 16px 18px; }
.result-panel .foot { padding: 12px 18px; border-top: 1px solid var(--border); }

.reasoning {
  font-size: 12.5px; line-height: 1.6; color: var(--text-2);
  background: var(--raised); border: 1px solid var(--border);
  border-radius: var(--r-sm); padding: 12px 13px; margin-bottom: 16px;
}
.reasoning .eyebrow {
  display: block; font-size: 10px; letter-spacing: .09em; text-transform: uppercase;
  color: var(--accent); margin-bottom: 7px; font-weight: 600;
}

.wall-group { margin-bottom: 14px; }
.wall-head {
  display: flex; align-items: baseline; justify-content: space-between; gap: 8px;
  padding: 7px 9px; border-radius: var(--r-sm); cursor: pointer;
  background: var(--raised); border: 1px solid var(--border); margin-bottom: 4px;
}
.wall-head:hover { border-color: var(--accent-line); }
.wall-head.active { border-color: var(--accent); background: var(--accent-dim); }
.wall-head .wname { font-size: 12px; font-weight: 600; color: var(--text); }
.wall-head .wsrc {
  font-size: 10px; color: var(--text-3); font-family: var(--font-mono); white-space: nowrap;
}
.box-list { list-style: none; margin: 0; padding: 0; }
.box-row {
  display: flex; align-items: center; gap: 9px; padding: 6px 9px 6px 17px;
  border-radius: var(--r-sm); cursor: pointer; border: 1px solid transparent;
}
.box-row:hover { background: var(--raised); }
.box-row.active { background: var(--accent-dim); border-color: var(--accent-line); }
.box-row.flagged { background: var(--danger-dim); border-color: rgba(255,92,122,.45); }
.box-row .swatch {
  width: 11px; height: 11px; border-radius: 3px; flex: none;
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.16);
}
.box-row .label {
  font-size: 12px; color: var(--text); flex: 1; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.box-row .dims {
  font-size: 10.5px; color: var(--text-3); flex: none;
  font-family: var(--font-mono); font-variant-numeric: tabular-nums;
}

.kv { display: grid; grid-template-columns: auto 1fr; gap: 4px 12px; font-size: 12.5px; }
.kv dt { color: var(--text-3); }
.kv dd { margin: 0; color: var(--text-2); }

/* Stacked variant for a narrow column: a label/value grid squeezed to 400px
   wraps every value onto three lines. One under the other stays readable. */
.kv.stacked { display: block; }
.kv.stacked dt {
  font-size: 10.5px; text-transform: uppercase; letter-spacing: .07em;
  color: var(--text-3); font-weight: 600; margin-top: 11px;
}
.kv.stacked dt:first-child { margin-top: 0; }
.kv.stacked dd { margin: 2px 0 0; color: var(--text); font-size: 13px; }

/* sheet chips on the schedule form */
.chips { display: flex; flex-wrap: wrap; gap: 5px; }
.chip {
  font-size: 11px; font-family: var(--font-mono); background: var(--raised);
  border: 1px solid var(--border-strong); border-radius: 20px; padding: 2px 9px;
  color: var(--text-2);
}
.chip.ok { background: var(--ok-dim); border-color: rgba(45,212,167,.4); color: var(--ok); }
.chip.bad { background: var(--danger-dim); border-color: rgba(255,92,122,.4); color: var(--danger); }

/* two-pane app layout (playground, review) */
.split { display: flex; flex: 1; min-height: 0; min-width: 0; }
@media (max-width: 1000px) {
  .split { flex-direction: column; }
  .result-panel { width: auto; border-left: none; border-top: 1px solid var(--border); max-height: 46vh; }
  .viewer-pane { min-height: 46vh; }
}

/* =========================================================================
   sheet viewer

   Architectural sheets are read by zooming in: a 3/32"-scale dimension string
   is a handful of pixels even at full render resolution, so panning and
   magnifying are the primary interaction, not an extra.
   ========================================================================= */
.sheet-stage {
  position: relative; background: #0b0e13; border: 1px solid var(--border);
  border-radius: var(--r-sm); overflow: hidden; height: 62vh; min-height: 340px;
  cursor: grab; touch-action: none;
}
/* A dialog whose whole content is the sheet: the stage takes the space and the
   thumbnail strip gets what is left. `vstack` marks the wrapper that has to
   pass the height down -- without it the flex chain breaks at that div. */
.vstack { display: flex; flex-direction: column; min-height: 0; flex: 1; }
.modal-card.full .vstack .sheet-stage { height: auto; flex: 1; min-height: 240px; }
.modal-card.full .thumb-grid { max-height: 172px; flex: none; }

/* Review and history dialogs put a viewer above a column of detail that has to
   stay readable, so the viewer takes a generous fixed slice and the rest
   scrolls under it rather than being squeezed. */
.modal-card.full .review-viewer,
.modal-card.full .history-viewer { height: 62vh; min-height: 340px; }
.sheet-stage.dragging { cursor: grabbing; }
.sheet-stage img {
  position: absolute; top: 0; left: 0; transform-origin: 0 0;
  /* Sheets are line art: smoothing them on zoom turns crisp rules into mush,
     so magnify with hard pixels instead. */
  image-rendering: pixelated;
  background: #fff; user-select: none; -webkit-user-drag: none;
}
.sheet-stage .loading {
  position: absolute; inset: 0; display: grid; place-items: center;
  color: var(--text-2); font-size: 13px; gap: 8px; z-index: 2;
  background: var(--surface);
}
.sheet-stage .loading .sub { color: var(--text-3); font-size: 11.5px; }

.sheet-bar {
  display: flex; align-items: center; gap: 8px; margin-top: 10px; flex-wrap: wrap;
}
.sheet-bar .zoom {
  font-family: var(--font-mono); font-size: 11.5px; color: var(--text-3);
  min-width: 52px; text-align: center; font-variant-numeric: tabular-nums;
}
.sheet-bar .spacer { flex: 1; }
.sheet-bar .res {
  font-family: var(--font-mono); font-size: 11px; color: var(--text-3);
}

/* thumbnail grid */
.thumb-grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(132px, 1fr));
  gap: 10px; max-height: 300px; overflow-y: auto; padding: 2px;
}
.thumb {
  border: 1px solid var(--border); border-radius: var(--r-sm); overflow: hidden;
  background: var(--raised); cursor: pointer; padding: 0; text-align: left;
  transition: border-color .12s;
}
.thumb:hover { border-color: var(--accent); }
.thumb.active { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }
.thumb img { width: 100%; aspect-ratio: 1.4; object-fit: cover; object-position: top center;
             display: block; background: #fff; }
.thumb .cap {
  padding: 5px 7px; font-family: var(--font-mono); font-size: 11px;
  color: var(--text-2); display: flex; justify-content: space-between; gap: 6px;
}
.thumb .cap .pg { color: var(--text-3); }

/* pager controls in the sheet toolbar */
.sheet-bar .pager { display: inline-flex; align-items: center; gap: 6px; }
.page-input {
  width: 56px; padding: 5px 7px; text-align: center;
  font-family: var(--font-mono); font-size: 12px;
  background: var(--raised); color: var(--text);
  border: 1px solid var(--border); border-radius: var(--r-sm);
}
.page-input:focus { outline: none; border-color: var(--accent); }
.sheet-jump {
  width: auto; max-width: 190px; padding: 5px 26px 5px 9px;
  font-size: 12px; font-family: var(--font-mono);
}
.sheet-bar .btn:disabled { opacity: .35; }

/* =========================================================================
   collapsible side panel (the playground's query form)

   The viewer wants every pixel once a room is loaded, but the form has to come
   back without a page change - so the panel collapses to a rail with a reopen
   handle rather than disappearing.
   ========================================================================= */
.result-panel {
  transition: width .24s cubic-bezier(.2, .9, .3, 1);
  position: relative;
}
.result-panel.collapsed { width: 40px !important; }
.result-panel.collapsed > .body,
.result-panel.collapsed > .head,
.result-panel.collapsed > .foot { display: none; }

.panel-toggle {
  position: absolute; top: 10px; z-index: 6;
  width: 24px; height: 40px; padding: 0;
  display: grid; place-items: center; cursor: pointer;
  background: var(--raised); border: 1px solid var(--border);
  color: var(--text-3); border-radius: var(--r-sm);
  transition: color .12s, border-color .12s;
}
.panel-toggle:hover { color: var(--accent-hi); border-color: var(--accent-line); }
.panel-toggle svg { width: 13px; height: 13px; transition: transform .24s; }
.result-panel.left  .panel-toggle { right: 8px; }
.result-panel.right .panel-toggle { left: 8px; }
.result-panel.collapsed .panel-toggle { right: 8px; left: 8px; margin: 0 auto; }
.result-panel.left.collapsed .panel-toggle svg { transform: rotate(180deg); }
.result-panel.right .panel-toggle svg { transform: rotate(180deg); }
.result-panel.right.collapsed .panel-toggle svg { transform: none; }

/* A vertical label on the rail, so a collapsed panel still says what it is. */
.panel-rail-label {
  position: absolute; top: 60px; left: 50%; transform: translateX(-50%) rotate(180deg);
  writing-mode: vertical-rl; font-size: 10.5px; letter-spacing: .1em;
  text-transform: uppercase; color: var(--text-3); pointer-events: none;
  white-space: nowrap; display: none;
}
.result-panel.collapsed .panel-rail-label { display: block; }

@media (prefers-reduced-motion: reduce) { .result-panel { transition: none; } }

/* =========================================================================
   batch scheduler
   ========================================================================= */
.sched-head, .sched-row {
  display: grid;
  grid-template-columns: 26px 1.1fr 1fr 1.25fr 1.25fr 1fr 30px;
  gap: 8px; align-items: end;
}
.sched-head {
  padding: 0 0 6px; border-bottom: 1px solid var(--border); margin-bottom: 10px;
}
.sched-head .h { font-size: 11.5px; font-weight: 600; color: var(--text-2); }
.sched-head .eg {
  font-size: 10.5px; color: var(--text-3); font-family: var(--font-mono);
  margin-top: 2px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.sched-row { margin-bottom: 4px; }
.sched-row .n {
  font-family: var(--font-mono); font-size: 11.5px; color: var(--text-3);
  padding-bottom: 9px; text-align: right;
}
.sched-row input { font-size: 13px; }
.sched-row input.unknown { border-color: var(--danger); }

/* The resolved-pages strip under each row. This is the whole point of
   resolving as you type: the page a sheet number lands on is the thing you can
   actually check, and clicking it opens that page. */
.sched-resolved {
  grid-column: 2 / 7; display: flex; flex-wrap: wrap; gap: 4px;
  margin: 2px 0 10px; min-height: 18px;
}
.pg-chip {
  display: inline-flex; align-items: center; gap: 4px;
  font-family: var(--font-mono); font-size: 10.5px; padding: 1px 7px;
  border-radius: 20px; border: 1px solid transparent; cursor: pointer;
  background: var(--ok-dim); color: var(--ok); border-color: rgba(45,212,167,.35);
}
.pg-chip:hover { background: rgba(45,212,167,.24); }
.pg-chip .role { opacity: .65; }
.pg-chip.bad {
  background: var(--danger-dim); color: var(--danger);
  border-color: rgba(255,92,122,.4); cursor: default;
}
.pg-chip.bad:hover { background: var(--danger-dim); }
.sched-resolved .hint { font-size: 10.5px; color: var(--text-3); align-self: center; }

@media (max-width: 1000px) {
  .sched-head { display: none; }
  .sched-row { grid-template-columns: 1fr 1fr; }
  .sched-row .n { display: none; }
  .sched-resolved { grid-column: 1 / 3; }
}

/* =========================================================================
   two-column review layout

   The model on the left, everything written about it on the right. Stacked,
   the reasoning paragraph - which runs to a solid page on a complex room -
   pushed the viewer into a letterbox and put the remarks below the fold. Side
   by side, the geometry keeps the height it needs and the prose scrolls
   independently beside it, which is how the two are actually read: look at the
   model, read why it is that shape, look again.
   ========================================================================= */
.split-review { display: flex; gap: 0; flex: 1; min-height: 0; }

.split-review .pane-model {
  flex: 1; min-width: 0; display: flex; flex-direction: column; min-height: 0;
  overflow: hidden;      /* the model never spills into the column beside it */
}
.split-review .pane-info {
  width: 400px; flex: none; overflow-y: auto; min-height: 0;
  border-left: 1px solid var(--border); padding-left: 18px; margin-left: 18px;
}
.split-review .pane-info > :first-child { margin-top: 0; }

/* The reasoning is the longest thing here and the most worth reading, so it
   gets room to breathe rather than being boxed like an aside. */
.split-review .pane-info .reasoning { margin-bottom: 16px; }

@media (max-width: 1100px) {
  .split-review { flex-direction: column; }
  .split-review .pane-info {
    width: auto; border-left: none; padding-left: 0; margin-left: 0;
    border-top: 1px solid var(--border); padding-top: 16px; margin-top: 16px;
    max-height: 46vh;
  }
  .split-review .pane-model { min-height: 44vh; }
}

/* ---------------------------------------------------------- drawings view
   The plan and elevations, drawn to scale from the same geometry the 3D
   viewer uses. It sits over the canvas rather than beside it: the two are
   alternative readings of one room, and showing both at once would halve
   each and help neither. */
.drawings-pane {
  position: absolute; inset: 0; z-index: 2;
  overflow: auto; padding: 18px 20px 28px;
  background: var(--bg);
}
.drawings-pane .dv-section { margin: 0 0 22px; }
.drawings-pane .dv-section h3 {
  font-size: 14px; font-weight: 600; color: var(--text);
  margin: 0 0 6px;
}
.drawings-pane .dv-svg { display: block; max-width: 100%; height: auto; }
/* Paper and rules are set as presentation attributes so an exported or
   printed drawing survives without this stylesheet; these override them for
   the dark UI. */
.drawings-pane .dv-paper { fill: var(--raised); stroke: var(--border); }
.drawings-pane .dv-label { fill: var(--text); font-size: 12px; font-weight: 600; }
.drawings-pane .dv-cap { fill: var(--text-2); font-size: 11.5px; }

/* ------------------------------------------------------- materials table */
.mat-row {
  display: flex; align-items: baseline; gap: 8px;
  padding: 5px 0; border-bottom: 1px solid var(--border);
  font-size: 12.5px;
}
.mat-row:last-child { border-bottom: none; }
.mat-row .swatch {
  width: 11px; height: 11px; border-radius: 2px; flex: none;
  align-self: center; border: 1px solid rgba(0, 0, 0, .35);
}
.mat-row .mat-code {
  font-family: var(--font-mono);
  color: var(--text); flex: none; min-width: 52px;
}
.mat-row .mat-spec { color: var(--text-2); line-height: 1.45; }

/* ------------------------------------------------------------ steps view
   What the model did, in order, each step beside the image it rendered.
   The crop is the evidence: a reviewer checking a dimension sees the pixels
   it was read from instead of taking the number on trust. */
.steps-pane {
  position: absolute; inset: 0; z-index: 2;
  overflow: auto; padding: 18px 20px 28px; background: var(--bg);
}
.step-row {
  display: flex; gap: 10px; align-items: flex-start;
  padding: 9px 0; border-bottom: 1px solid var(--border);
}
.step-row:last-child { border-bottom: none; }
.step-n {
  font-family: var(--font-mono); font-size: 11px; color: var(--text-2);
  min-width: 26px; text-align: right; padding-top: 2px; flex: none;
}
.step-main { flex: 1; min-width: 0; }
.step-what { font-size: 13px; color: var(--text); }
.step-said {
  font-size: 12.5px; color: var(--text-2); margin-top: 3px; line-height: 1.5;
}
.step-at {
  font-family: var(--font-mono); font-size: 11px;
  color: var(--text-2); margin-top: 3px;
}
/* Small enough to scan a whole run, large enough to tell which drawing it
   is; the full-size image is one click away. */
.step-crop { flex: none; line-height: 0; }
.step-crop img {
  width: 132px; height: auto; max-height: 96px; object-fit: cover;
  border: 1px solid var(--border); border-radius: var(--r-sm);
  background: #fff;
}

/* What a section detail says an element is built from. Sits under its box
   row because it belongs to that box, not to the room: layers, hardware and
   services that no 3D view can show and that the shop works from. */
.box-spec {
  list-style: none; margin: -2px 0 6px 26px;
  display: flex; flex-direction: column; gap: 2px;
}
.box-spec .spec-line {
  font-size: 11.5px; color: var(--text-2); line-height: 1.45;
  padding-left: 9px; border-left: 2px solid var(--border);
}

/* A provisional dimension must not read like settled spec, and neither must
   work somebody else supplies -- so `verify` and `scope` lines are marked
   rather than blended into the construction list above them. */
.box-spec .spec-line.verify { border-left-color: var(--warn, #c9903f); color: var(--text); }
.box-spec .spec-line.scope { border-left-color: var(--accent); }
.box-spec .spec-line.note { font-style: italic; }

/* The crop the model is reading, shown where the room will be.
   Contained rather than cropped: a drawing that has had its edges cut off is
   not the drawing the model read, and the whole point is that this is the
   real thing. */
.live-pane {
  position: absolute; inset: 0; z-index: 3;
  display: flex; flex-direction: column; align-items: center;
  justify-content: center; gap: 12px; padding: 26px 26px 34px;
  background: var(--bg);
}
.live-pane img {
  max-width: 100%; min-height: 0; flex: 1;
  object-fit: contain; background: #fff;
  border: 1px solid var(--border); border-radius: var(--r-sm);
}
.live-cap {
  font-size: 12.5px; color: var(--text-2); text-align: center;
  flex: none; max-width: 80%;
}

/* ---------------------------------------------------------- room picker
   A row per finished reconstruction. Buttons rather than list items because
   choosing one is the action the page exists for, and it should be reachable
   by keyboard without any extra work. */
.pick-row {
  display: flex; align-items: center; gap: 12px; width: 100%;
  padding: 10px 12px; margin-bottom: 6px; text-align: left;
  background: var(--raised); border: 1px solid var(--border);
  border-radius: var(--r-sm); color: var(--text); cursor: pointer;
}
.pick-row:hover { border-color: var(--accent-line); }
.pick-row[class*="on"] { border-color: var(--accent); background: var(--accent-dim); }
.pick-main { display: flex; flex-direction: column; gap: 2px; flex: 1; min-width: 0; }
.pick-room { font-weight: 600; font-size: 13.5px; }

/* =========================================================================
   global progress bar (driven by api() in shell.js)
   ========================================================================= */
.app-progress {
  position: fixed; inset: 0 0 auto 0; height: 3px; z-index: 10000;
  pointer-events: none; opacity: 0; transition: opacity .3s ease .25s;
}
.app-progress.active { opacity: 1; transition: none; }
.app-progress-fill {
  position: absolute; inset: 0; transform-origin: left center; transform: scaleX(0);
  background: var(--gradient);
  box-shadow: 0 0 12px rgba(76, 141, 255, .7), 0 0 4px rgba(139, 92, 246, .6);
  overflow: hidden;
}
/* A light sweeping along the fill, so a long wait still looks alive. */
.app-progress-fill::after {
  content: ""; position: absolute; inset: 0; width: 40%;
  background: linear-gradient(90deg, transparent, rgba(255, 255, 255, .55), transparent);
  animation: progress-sweep 1.4s cubic-bezier(.4, 0, .2, 1) infinite;
}
/* Fast at first, then ever slower toward 90%: early movement says the
   request went out, and the long tail never claims it is nearly done. */
.app-progress.active .app-progress-fill {
  transform: scaleX(.9);
  transition: transform 20s cubic-bezier(.08, .82, .17, 1);
}
.app-progress.done .app-progress-fill { transform: scaleX(1); transition: transform .22s ease-out; }
.app-progress.done { opacity: 0; transition: opacity .3s ease .22s; }
@keyframes progress-sweep { from { transform: translateX(-100%); } to { transform: translateX(260%); } }
@media (prefers-reduced-motion: reduce) {
  .app-progress-fill::after { animation: none; }
  .app-progress.active .app-progress-fill { transition-duration: .01s; }
}

/* =========================================================================
   shared bits for model views (history, version history)
   ========================================================================= */
.viewer-wait {
  position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
  gap: 10px; font-size: 13px; color: var(--text-3);
}
.spinner {
  width: 18px; height: 18px; border-radius: 50%; flex: none;
  border: 2px solid var(--border-strong); border-top-color: var(--accent);
  animation: spin .8s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .spinner { animation-duration: 2.4s; } }
.dot-sep::before { content: "·"; margin-right: 6px; }
.delta { font: 500 10.5px/1 var(--font-mono); margin-left: 4px; }
.delta.up { color: var(--ok); }
.delta.down { color: var(--warn); }

/* =========================================================================
   version history (static/js/versions.js)
   ========================================================================= */
.vh { display: grid; grid-template-columns: 340px minmax(0, 1fr); flex: 1; min-height: 0; }

/* ---- timeline ---- */
.vh-timeline {
  border-right: 1px solid var(--border); display: flex; flex-direction: column;
  min-height: 0; background: var(--bg);
}
.vh-pad { position: relative; height: 120px; }
.vh-head { padding: 16px 18px 10px; }
.vh-title { font: 600 14px var(--font-display); }
.vh-sub { font-size: 12px; color: var(--text-3); margin-top: 2px; }
.vh-notice {
  margin: 0 14px 10px; padding: 9px 11px; border-radius: var(--r-sm); font-size: 12px;
  background: var(--warn-dim); color: var(--warn); border: 1px solid rgba(245,184,65,.35);
}
.vh-list {
  list-style: none; margin: 0; padding: 4px 10px 10px; overflow-y: auto; flex: 1;
  position: relative; outline: none;
}
.vh-list:focus-visible { box-shadow: inset 0 0 0 2px var(--accent-line); }
.vh-item {
  position: relative; display: grid; grid-template-columns: 22px minmax(0, 1fr);
  gap: 8px; padding: 10px 10px 12px 6px; border-radius: var(--r-sm); cursor: pointer;
  border: 1px solid transparent; transition: background .1s, border-color .1s;
}
.vh-item:hover { background: var(--raised); }
.vh-item.on { background: var(--accent-dim); border-color: var(--accent-line); }
.vh-item.against { border-color: rgba(245,184,65,.45); border-style: dashed; }
/* The rail: one line threading every node, so the list reads as a history
   rather than a stack of cards. */
.vh-item::before {
  content: ""; position: absolute; left: 16px; top: 0; bottom: 0; width: 2px;
  background: var(--border-strong);
}
.vh-item:first-child::before { top: 17px; }
.vh-item:last-child::before { bottom: calc(100% - 17px); }
.vh-item:only-child::before { display: none; }
.vh-node {
  position: relative; z-index: 1; width: 12px; height: 12px; margin: 5px 0 0 5px;
  border-radius: 50%; background: var(--surface); border: 2px solid var(--accent);
}
.vh-item.src-refine .vh-node { border-color: var(--accent-2); }
.vh-item.src-restore .vh-node { border-color: var(--ok); }
.vh-item.on.src-generate .vh-node { background: var(--accent); }
.vh-item.on.src-refine .vh-node { background: var(--accent-2); }
.vh-item.on.src-restore .vh-node { background: var(--ok); }
.vh-body { min-width: 0; }
.vh-row1 { display: flex; align-items: center; gap: 6px; min-width: 0; }
.vh-no { font: 600 13px var(--font-mono); }
.vh-when { margin-left: auto; font-size: 11px; color: var(--text-3); white-space: nowrap; }
.vh-tag {
  font-size: 10px; font-weight: 600; letter-spacing: .04em; text-transform: uppercase;
  padding: 2px 6px; border-radius: 4px; white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis; max-width: 120px;
}
.vh-tag.current { background: var(--ok-dim); color: var(--ok); }
.vh-tag.name {
  background: var(--raised); color: var(--text); border: 1px solid var(--border-strong);
  text-transform: none; letter-spacing: 0; font-weight: 500; font-size: 11px;
}
.vh-tag.name::before { content: "◆ "; color: var(--accent-hi); font-size: 8px; vertical-align: 1px; }
.vh-tag.cmp { background: var(--warn-dim); color: var(--warn); }
.vh-row2 { font-size: 11.5px; color: var(--text-3); margin-top: 3px; display: flex; gap: 6px; flex-wrap: wrap; }
.vh-src.refine { color: #a98bff; }
.vh-src.restore { color: var(--ok); }
.vh-msg {
  margin-top: 6px; font-size: 12.5px; color: var(--text-2); line-height: 1.45;
  border-left: 2px solid var(--border-strong); padding-left: 8px;
  display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden;
}
.vh-flags { margin-top: 6px; display: flex; flex-wrap: wrap; gap: 4px; align-items: center; font-size: 11px; }
.vh-flags .chip { max-width: 140px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.vh-stats { margin-top: 6px; font-size: 11.5px; color: var(--text-3); font-variant-numeric: tabular-nums; }
.vh-foot { padding: 10px 18px 14px; font-size: 11.5px; border-top: 1px solid var(--border); }

/* ---- stage ---- */
.vh-stage { display: flex; flex-direction: column; min-width: 0; min-height: 0; }
.vh-stage-head {
  display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
  padding: 12px 18px; border-bottom: 1px solid var(--border);
}
.vh-stage-title { min-width: 0; flex: 1; }
.vh-stage-title > div:first-child { display: flex; align-items: center; gap: 8px; }
.vh-stage-no { font: 600 18px var(--font-display); }
.vh-stage-meta { font-size: 12px; color: var(--text-3); margin-top: 2px; }
.vh-actions { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; }
.vh-compare-pick { width: auto; padding: 6px 30px 6px 10px; font-size: 12.5px; }
.vh-viewer { position: relative; flex: 1; min-height: 260px; background: var(--bg); }
.vh-why { border-top: 1px solid var(--border); padding: 10px 18px; font-size: 13px; color: var(--text-2); }
.vh-why summary { cursor: pointer; font-size: 12px; color: var(--text-3); font-weight: 500; }
.vh-why p { margin: 8px 0 2px; line-height: 1.55; max-height: 22vh; overflow-y: auto; }

.vh-compare { display: grid; grid-template-columns: 1fr 1fr; flex: 1.35; min-height: 0; }
.vh-pane { position: relative; display: flex; flex-direction: column; min-width: 0; }
.vh-pane + .vh-pane { border-left: 1px solid var(--border); }
.vh-pane-cap {
  position: absolute; z-index: 2; top: 10px; left: 12px; display: flex; gap: 8px; align-items: center;
  padding: 5px 10px; border-radius: 20px; font-size: 12px;
  background: rgba(10, 13, 19, .82); border: 1px solid var(--border-strong); backdrop-filter: blur(4px);
}
.vh-pane .vh-viewer { min-height: 200px; }

.vh-changes { flex: 1; min-height: 0; overflow-y: auto; border-top: 1px solid var(--border); position: relative; }
.vh-sumrow {
  display: flex; gap: 8px; align-items: center; flex-wrap: wrap; padding: 10px 18px;
  position: sticky; top: 0; background: var(--surface); border-bottom: 1px solid var(--border); z-index: 1;
}
.vh-sumrow .spacer { flex: 1; }
.vh-sum { font-size: 12px; padding: 3px 9px; border-radius: 20px; border: 1px solid; }
.vh-sum.added { color: var(--ok); background: var(--ok-dim); border-color: rgba(45,212,167,.4); }
.vh-sum.changed { color: var(--warn); background: var(--warn-dim); border-color: rgba(245,184,65,.4); }
.vh-sum.removed { color: var(--danger); background: var(--danger-dim); border-color: rgba(255,92,122,.4); }
.vh-chlist { padding: 4px 10px 14px; }
.vh-group-h { font-size: 11px; font-weight: 600; color: var(--text-2); padding: 10px 8px 4px; }
.vh-ch {
  display: grid; grid-template-columns: 20px minmax(0, .9fr) minmax(0, 1.6fr); gap: 10px;
  align-items: center; width: 100%; text-align: left; padding: 6px 8px; border-radius: var(--r-sm);
  background: none; border: none; color: var(--text); font: inherit; font-size: 12.5px; cursor: pointer;
}
.vh-ch:hover { background: var(--raised); }
.vh-ch.static { cursor: default; }
.vh-ch.static:hover { background: none; }
.vh-ch-ic {
  width: 18px; height: 18px; border-radius: 4px; display: flex; align-items: center; justify-content: center;
  font: 600 12px var(--font-mono);
}
.vh-ch.added .vh-ch-ic { background: var(--ok-dim); color: var(--ok); }
.vh-ch.changed .vh-ch-ic { background: var(--warn-dim); color: var(--warn); }
.vh-ch.removed .vh-ch-ic { background: var(--danger-dim); color: var(--danger); }
.vh-ch.removed .vh-ch-l { text-decoration: line-through; color: var(--text-2); }
.vh-ch-l, .vh-ch-d { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.vh-ch-d { color: var(--text-3); font-size: 11.5px; font-variant-numeric: tabular-nums; }
.vh-same { padding: 16px 18px; font-size: 13px; }
.vh-warn {
  font-size: 12.5px; color: var(--warn); background: var(--warn-dim);
  border: 1px solid rgba(245,184,65,.35); border-radius: var(--r-sm); padding: 8px 10px;
}

@media (max-width: 900px) {
  .vh { grid-template-columns: 1fr; grid-template-rows: minmax(0, 38%) minmax(0, 1fr); }
  .vh-timeline { border-right: none; border-bottom: 1px solid var(--border); }
  .vh-compare { grid-template-columns: 1fr; }
  .vh-pane + .vh-pane { border-left: none; border-top: 1px solid var(--border); }
}
