/* ==========================================================================
   CFB BOILER FURNACE WALL WELD MAP - STYLES.CSS
   Theme: Sleek Dark-Mode Engineering Dashboard
   ========================================================================== */

/* Design Tokens & Variables */
:root {
  /* Dark by default; light is each person's choice (ADR 0043), and
     shell.css's light block sets color-scheme: light with it. Saying so lets
     the browser draw native controls - dropdowns, scrollbars, date pickers -
     to match, instead of dropping a white Windows control onto a dark card. */
  color-scheme: dark;

  --bg-primary: #0f172a;
  --bg-secondary: #1e293b;
  --bg-card: #0f172a;
  --bg-input: #1e293b;
  
  --border-color: #334155;
  --border-focus: #3b82f6;
  
  --text-primary: #f8fafc;
  --text-secondary: #94a3b8;
  --text-muted: #64748b;
  
  --accent-blue: #3b82f6;
  --accent-cyan: #06b6d4;
  --accent-green: #10b981;
  --accent-yellow: #eab308;
  --accent-red: #ef4444;
  --accent-purple: #a855f7;
  
  --font-main: 'Outfit', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  --radius-sm: 6px;
  --radius-md: 10px;
  --radius-lg: 16px;
  
  --shadow-sm: 0 1px 3px rgba(0,0,0,0.12);
  --shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.2), 0 2px 4px -1px rgba(0, 0, 0, 0.1);
  --shadow-glow: 0 0 15px rgba(59, 130, 246, 0.25);
  --transition-fast: 0.15s ease;
  --transition-normal: 0.25s ease;

  /* Shell space budget — see docs/ui-shell-rework.md.
     Every constant the chrome spends is declared HERE and nowhere else, so the
     canvas share is one edit to change and one place to audit. The layout grew
     by accretion before precisely because these numbers were scattered. */
  --shell-pad: 0px;      /* was 16px — the card borders already separate things */
  /* ONE gap, used everywhere. App bar to main, canvas to rail, and between the
     stacked panels in the rail are all this number, so no seam is wider than
     another. */
  --shell-gap: 8px;
  --panel-gap: var(--shell-gap);
  --appbar-h: 48px;      /* was 70px */
  --rail-min: 260px;     /* below this the form controls in the rail start to clip */
}

/* Reset & Base Styles */
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

body {
  font-family: var(--font-main);
  background-color: var(--bg-primary);
  color: var(--text-primary);
  line-height: 1.5;
  height: 100vh;
  overflow: hidden;
  -webkit-font-smoothing: antialiased;
}

/* Scrollbar styling */
::-webkit-scrollbar {
  width: 6px;
  height: 6px;
}
::-webkit-scrollbar-track {
  background: var(--bg-primary);
}
::-webkit-scrollbar-thumb {
  background: var(--border-color);
  border-radius: var(--radius-sm);
}
::-webkit-scrollbar-thumb:hover {
  background: var(--text-muted);
}

/* Main Dashboard Layout */
/* The application shell. Named regions, per ADR 0008 — the layout engine owns the
   space and features are placed INTO it, rather than each new feature competing
   with the canvas at the edges. That accretion is what ADR 0008 named as the root
   cause, and a grid is what makes it structural rather than a matter of care.

   Two nested grids rather than one flat grid with `display: contents` on
   .dashboard-main: that element is a <main>, and `display: contents` would strip
   its landmark role from the accessibility tree.

   Spacing is tightened, not removed. ADR 0008's design target is 1366x768 -
   plant laptops and field tablets - where 16px of padding plus 16px of gaps
   costs roughly a whole KPI row. Density is a feature in engineering software;
   the reported problem was collision, not volume. */
.dashboard-container {
  display: grid;
  grid-template-areas:
    "appbar"
    "main";
  grid-template-rows: auto minmax(0, 1fr);
  /* Short by whatever the EXAMPLE BOILER bar occupies, if there is one.
     entropy_errors.js fixes that bar to the bottom of the viewport and publishes
     its height; without this the shell is a full 100vh underneath it and the bar
     covers the last ~21px of the rail - which is where Save All Records sits.
     Unset on a real tenant's deployment, so it costs nothing there. */
  height: calc(100vh - var(--example-mark-h, 0px));
  width: 100vw;
  padding: var(--shell-pad);
  gap: var(--shell-gap);

}

/* ---- Findings panel (was the KPI banner in the app bar) ----------------
   Six counts of what needs attention, each a filter that opens the list of tubes
   it counts. They spent 90px of every screen looking like passive decoration;
   here they are in the rail, styled as the controls they always were.

   Nothing was deleted -- the seventh, Total Logs, is the only passive readout of
   the seven and went to the status bar under ADR 0008's ambient-readout rule. */

/* Not the rail's Findings cards, which take the prototype's .kpi rules. */
:not(.kpi) > .stat-label {
  font-size: 0.6rem;
  line-height: 1.2;
  text-transform: uppercase;
  color: var(--text-secondary);
  letter-spacing: 0.5px;
  font-weight: 500;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

:not(.kpi) > .stat-value {
  font-size: 0.95rem;
  line-height: 1.2;
  font-weight: 700;
  color: var(--text-primary);
  margin-top: 1px;
  font-variant-numeric: tabular-nums;
}

/* ---- Docked tool panels (ADR 0008) -------------------------------------
   layout-panel, measure-panel and erosion-forecast-panel used to be absolutely
   positioned over the canvas and dragged by hand. When one grew taller than the
   canvas, the drag clamp's maximum fell below its minimum, the panel pinned to
   top: 16px, and its action footer went off the bottom edge where it could not be
   reached or scrolled to (app.js:5000) -- the reported "Revert to UTG button falls
   off-screen".

   As rail panels owned by the grid they cannot clip anything and cannot be
   dragged anywhere they do not fit. Generalises the scrolling-body pattern the
   Area Tool already proved for #measure-panel. */
.dock-panel {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  min-height: 0;
  /* Never let one tool panel swallow the rail and squeeze the details panel out. */
  max-height: 46%;
  padding: 12px;
  background: var(--bg-secondary);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-md);
  overflow: hidden;
}
/* ── The Area Tool is a window ──────────────────────────────────────────────
   Superseded the docked layout for this one panel. See
   docs/adr/0014-the-area-tool-is-a-window.md. Docking it shared 616px of rail
   with Findings and Tube Inspection Details: the tool got 283px, its body 67px
   of 502px of content, and yielding the rail to it left the other two visibly
   mangled - an orphaned Findings header and a Details panel clipped mid-control.

   F-03 IS FIXED AT ITS CAUSE, not reintroduced. The old defect was a panel
   taller than its parent: the drag clamp pinned it to the top and its action
   footer sat below the viewport, unreachable. `max-height` here makes a window
   that is taller than the viewport impossible, so the footer is always on
   screen, and the drag clamp keeps the header reachable rather than the whole
   panel inside its parent. */
.dock-panel--floating {
  position: fixed;
  z-index: 60;
  width: 392px;
  max-width: calc(100vw - 32px);
  /* The F-03 fix. Never taller than the viewport. */
  max-height: calc(100vh - 32px);
  /* Free resize by the corner. Needs a non-visible overflow to work at all. */
  resize: both;
  overflow: hidden;

  /* A FLOOR, measured rather than guessed. Dragged smaller than this the window
     became useless in two ways a screenshot caught:

       - Below ~254px of inner width the header ran out of room and the Draw and
         Pan labels clipped, losing Pan entirely. The header's children need
         230px plus 24px of panel padding.
       - Dragged short, the pinned actions block - 147px holding Commit, Export,
         Undo, Clear and Exit - was cut off by the panel's own overflow:hidden.
         Only Commit and Export survived, and there was no way to reach the rest.

     So the minimum is header (35) + actions (147) + padding (24) + a body worth
     having (~115), and a width with room to spare over the header's 230. At the
     floor everything is reachable: the header is intact, all five actions are
     visible, and only the body scrolls. */
  min-width: 320px;
  min-height: 320px;
  box-shadow: 0 18px 48px rgba(0,0,0,calc(0.55 * var(--shadow-k)));
}
.dock-panel--floating.is-maximised {
  left: 16px !important;
  top: 16px !important;
  width: calc(100vw - 32px) !important;
  height: calc(100vh - 32px) !important;
  /* !important because place() writes an inline max-height to keep the footer on
     screen while dragging, and an inline max-height outranks a stylesheet height.
     Without this, maximising reached full width and stayed at its dragged height. */
  max-height: calc(100vh - 32px) !important;
  resize: none;
}
.dock-panel--floating .dock-panel__header { cursor: move; user-select: none; }
.dock-panel--floating .dock-panel__header button { cursor: pointer; }

/* Header and actions pinned, only the body scrolls, so a commit button can never
   leave the panel however long its content grows.

   Named parts, NOT :nth-child. The positional version looked equivalent and was
   wrong: layout-panel has a <p> between its header and its body, so the rules
   landed on the paragraph and on the body respectively, crushing the paragraph to
   zero and pinning the body — which then overflowed its own panel by 32px and was
   clipped. Structure that markup is free to change is not a selector. */
.dock-panel__header { flex: 0 0 auto; }

.dock-panel__body {
  /* basis 0, not auto: with `auto` the body keeps its content height as its flex
     basis and will not give it all back. */
  flex: 1 1 0%;
  min-height: 0;              /* required, or the flex child refuses to shrink */
  overflow-y: auto;
  overscroll-behavior: contain;
}

.dock-panel__actions {
  flex: 0 0 auto;
  padding-top: 10px;
  border-top: 1px solid var(--border-color);
}

/* General Layout Main Panels */
.dashboard-main {
  grid-area: main;
  display: grid;
  grid-template-areas: "canvas rail";
  /* minmax(0, 1fr) not 1fr: a bare 1fr track has an `auto` minimum and refuses to
     shrink below its content, so a wide canvas would push the rail off-screen. */
  grid-template-columns: minmax(0, 1fr) 370px;
  gap: 0;
  min-height: 0;
}

.card {
  background-color: var(--bg-secondary);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-md);
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

/* The actions never scroll away: they are pinned while the tab strip scrolls. */
.viewport-actions { flex-shrink: 0; }

/* Left Side: Viewport Visualizer */
.viewport-section {
  grid-area: canvas;
  min-width: 0; /* the grid track can shrink; the card must be allowed to follow */
}

.wall-tabs {
  display: flex;
  gap: 6px;
  flex-wrap: nowrap;
  flex: 1 1 auto;               /* take what is left; scroll internally, not the header */
  min-width: 0;
  overflow-x: auto;
  scrollbar-width: none;        /* Firefox */
  -ms-overflow-style: none;     /* IE/Edge */
}

.wall-tabs::-webkit-scrollbar {
  display: none;                /* Chrome/Safari */
}

.wall-tab {
  background: none;
  border: 1px solid transparent;
  color: var(--text-secondary);
  font-family: var(--font-main);
  font-size: 0.85rem;
  font-weight: 500;
  padding: 6px 14px;
  border-radius: var(--radius-sm);
  cursor: pointer;
  display: flex;
  align-items: center;
    gap: 8px;
  transition: var(--transition-fast);
  flex-shrink: 0;
  white-space: nowrap;
}

.wall-tab:hover {
  background: rgba(var(--hi-rgb),0.03);
  color: var(--text-primary);
}

.wall-tab.active {
  background: var(--bg-primary);
  border-color: var(--border-color);
  color: var(--accent-cyan);
  box-shadow: inset 0 -2px 0 var(--accent-cyan);
}

.wall-tab .badge {
  background: rgba(var(--hi-rgb),0.08);
  font-size: 0.7rem;
  padding: 2px 6px;
  border-radius: 4px;
  color: var(--text-muted);
}

.wall-tab.active .badge {
  background: rgba(6, 182, 212, 0.15);
  color: var(--accent-cyan);
}

/* Unfolded box-net tab — visually set apart from the per-wall tabs */
.wall-tab-unfolded {
  margin-left: 4px;
  border-left: 1px solid var(--border-color);
  padding-left: 14px;
}
.wall-tab-unfolded .badge {
  background: rgba(168, 85, 247, 0.15);
  color: var(--accent-purple);
}
.wall-tab-unfolded.active .badge {
  background: rgba(6, 182, 212, 0.15);
  color: var(--accent-cyan);
}

/* Internal panel navigation (Evaporator / Superheater) — grouped dropdowns + face toggle */
.panel-nav {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-left: 6px;
  padding-left: 10px;
  border-left: 1px solid var(--border-color);
  flex-shrink: 0;
}
.panel-group {
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.panel-group-label {
  font-size: 0.58rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-muted);
  padding-left: 2px;
}
.panel-select {
  background: var(--bg-input);
  border: 1px solid var(--border-color);
  color: var(--text-secondary);
  font-family: var(--font-main);
  font-size: 0.78rem;
  font-weight: 500;
  padding: 4px 8px;
  border-radius: var(--radius-sm);
  cursor: pointer;
  min-width: 92px;
}
.panel-select:hover {
  border-color: var(--text-muted);
}
.panel-select.is-active {
  border-color: var(--accent-cyan);
  color: var(--accent-cyan);
  box-shadow: inset 0 -2px 0 var(--accent-cyan);
}

/* Modify Layout toolbar button — active (editing) state */
#btn-modify-layout.active-layout-btn {
  background: rgba(6, 182, 212, 0.15);
  color: var(--accent-cyan);
  border-radius: 4px;
  box-shadow: inset 0 0 0 1px rgba(6, 182, 212, 0.4);
}

.viewport-actions {
  display: flex;
  gap: 4px;
  align-items: center;
  flex-shrink: 0;               /* Keep actions at natural width; header scrolls instead of clipping */
}

/* Interactive Canvas viewport wrapper */
.canvas-wrapper {
  flex: 1;
  position: relative;
  background-color: #0b0f19;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: grab;
}

.canvas-wrapper:active {
  cursor: grabbing;
}

#wall-canvas {
  display: block;
  touch-action: none; /* Safely kills default browser pinch-zooming patterns over the viewport matrix */
}

/* Canvas loading spinner */
.canvas-loader {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: rgba(11, 15, 25, 0.8);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 12px;
  z-index: 10;
  transition: opacity 0.3s ease;
}

.canvas-loader.hidden {
  opacity: 0;
  pointer-events: none;
}

.spinner {
  width: 36px;
  height: 36px;
  border: 3px stroke rgba(var(--hi-rgb),0.1);
  border-top: 3px solid var(--accent-cyan);
  border-radius: 50%;
  animation: spin 1s linear infinite;
}

@keyframes spin {
  0% { transform: rotate(0deg); }
  100% { transform: rotate(360deg); }
}

/* The hover card is the prototype's, in shell.css (5j). */
.canvas-tooltip.hidden {
  display: none;
}

.legend-group {
  display: flex;
  align-items: center;
  gap: 10px;
}

.legend-title {
  font-weight: 600;
  color: var(--text-secondary);
}

.legend-item {
  display: flex;
  align-items: center;
  gap: 6px;
}

.color-safe { background-color: var(--accent-green); }
.color-yellow { background-color: var(--accent-yellow); }
.color-orange { background-color: #f97316; }
.color-critical { background-color: var(--accent-red); }

.border-spray {
  background-color: rgba(168, 85, 247, 0.2);
  border: 1.5px solid var(--accent-purple);
}

/* Right Side: Sidebar Panels */
/* The rail scrolls as a last resort. Three tool panels can be open at once
   (erosion and measure legitimately overlap), and without this the details panel
   was crushed to 2px and the last panel's footer sat 28px below the window — the
   very failure docking was meant to end. Nothing in a docked panel may ever be
   unreachable. */
.sidebar-section {
  overflow-y: auto;
  overscroll-behavior: contain;
  grid-area: rail;
  /* No width here on purpose: the grid track is the single source of the rail's
     width. */
  min-width: 0;
  display: flex;
  flex-direction: column;
  /* A flex gap, and only since the rail's divider was removed. While a divider
     sat between two panels it had to BE the gap rather than sit inside one, or
     that one seam was twice as wide as every other; the rail has no divider
     now, so the container can simply space its own children. */
  gap: var(--panel-gap);
  min-height: 0;
  min-width: 0;
}

/* The seam between panels used to be a top margin on `* + *`, with overrides so
   the divider took none of its own. That was all scaffolding around the divider,
   and it hid a real trap: `+` is DOM adjacency, not RENDERED adjacency, so both
   sides needed `:not(.hidden)` and the overrides needed it too, purely to
   outweigh the general rule. A flex gap has neither problem - a `display: none`
   panel is not a flex item and a `position: fixed` tool window is out of flow,
   so neither takes a gap, which is what the selector kept getting wrong.

   Measured, because removing the divider is exactly how this breaks: the divider
   was the previous sibling of Tube Inspection Details and carried the seam in its
   own height, so taking it out left findings.bottom and details.top both at 280px
   - no seam at all, reported from a screenshot. */

.sidebar-panel {
  flex: 1;
  min-height: 0;
}

/* Keep the selected-tube panel usable however many tools are open. */

.panel-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 14px 18px;
  border-bottom: 1px solid var(--border-color);
  background-color: rgba(var(--ground-rgb),0.2);
}

.panel-header h2 {
  font-size: 0.95rem;
  font-weight: 600;
  display: flex;
  align-items: center;
  gap: 8px;
}

.panel-header h2 i {
  color: var(--accent-cyan);
  width: 18px;
  height: 18px;
}

.panel-body {
  padding: 16px;
  overflow-y: auto;
  flex: 1;
  display: flex;
  flex-direction: column;
}

/* Empty State Styling */
.empty-state {
  align-items: center;
  justify-content: center;
  text-align: center;
  height: 100%;
}

.empty-icon {
  width: 44px;
  height: 44px;
  color: var(--text-muted);
  margin-bottom: 12px;
}

.empty-state h3 {
  font-size: 0.95rem;
  font-weight: 600;
  margin-bottom: 6px;
}

.empty-state p {
  font-size: 0.8rem;
  color: var(--text-secondary);
  max-width: 250px;
  margin-bottom: 24px;
}


/* Direct coordinate search inputs */
.inline-inputs {
  display: grid;
  grid-template-columns: 1fr 1.3fr auto;
  gap: 8px;
  align-items: flex-end;
}

.inline-inputs label {
  font-size: 0.65rem;
  color: var(--text-muted);
  margin-bottom: 4px;
  display: block;
}

.inline-inputs input {
  width: 100%;
  background: var(--bg-primary);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-sm);
  color: var(--text-primary);
  padding: 6px 8px;
  font-size: 0.75rem;
}

.compact-btn {
  padding: 7px 12px;
  font-size: 0.75rem;
  height: 30px;
}

/* Two separate reasons this row was crooked, and the second is the real one:

   1. .compact-btn pins height:30px, but the input beside it is its own padding
      plus font size, which is not 30px.
   2. .input-field carries margin-bottom:12px for stacked forms. In this inline
      row that margin sits UNDER the input, so the input's box ends 12px above
      the row's bottom edge while the bottom-aligned button sits flush with it.
      No amount of adjusting the button could have fixed that. */
.inline-inputs .input-field {
  margin-bottom: 0;
}
.inline-inputs .compact-btn {
  height: auto;
  /* Matched to .input-field input, which wins the cascade here: 8px 12px at
     0.8rem. Guessing a smaller padding is what left it short the first time. */
  padding: 8px 14px;
  font-size: 0.8rem;
  align-self: end;
  line-height: 1.35;
}

/* Selected details display */
.selected-state {
  height: 100%;
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.selected-coords {
  background: rgba(var(--ground-rgb),0.4);
  border: 1px solid var(--border-color);
  padding: 10px;
  border-radius: var(--radius-sm);
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.wall-name-badge {
  font-size: 0.65rem;
  font-weight: 700;
  background-color: var(--accent-cyan);
  color: #000;
  padding: 2px 8px;
  border-radius: 10px;
}

.coords-numbers {
  font-size: 0.85rem;
  color: var(--text-secondary);
}

.coords-numbers strong {
  color: var(--text-primary);
}

/* Activity history timeline */
.timeline-container {
  flex: 1;
  overflow-y: auto;
  margin-bottom: 4px;
  display: flex;
  flex-direction: column;
}

.timeline-container h3 {
  font-size: 0.8rem;
  color: var(--text-secondary);
  text-transform: uppercase;
  letter-spacing: 0.5px;
  margin-bottom: 10px;
}

.timeline {
  list-style: none;
  position: relative;
  padding-left: 14px;
  font-size: 0.8rem;
}

.timeline::before {
  content: '';
  position: absolute;
  top: 4px;
  left: 4px;
  bottom: 4px;
  width: 1px;
  background: var(--border-color);
}

.timeline-item {
  position: relative;
  padding-bottom: 14px;
}

.timeline-item:last-child {
  padding-bottom: 0;
}

.timeline-dot {
  position: absolute;
  top: 5px;
  left: -14px;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--text-muted);
  border: 2px solid var(--bg-secondary);
}

.timeline-item.replaced .timeline-dot { background-color: var(--accent-blue); }
.timeline-item.sprayed .timeline-dot { background-color: var(--accent-purple); }
.timeline-item.welded .timeline-dot { background-color: var(--accent-yellow); }
.timeline-item.inspected .timeline-dot { background-color: var(--accent-green); }

.timeline-meta {
  display: flex;
  justify-content: space-between;
  margin-bottom: 2px;
  color: var(--text-secondary);
}

.timeline-year {
  font-weight: 700;
  color: var(--text-primary);
}

.timeline-type {
  font-size: 0.75rem;
  font-weight: 600;
}

.timeline-item.replaced .timeline-type { color: var(--accent-blue); }
.timeline-item.sprayed .timeline-type { color: var(--accent-purple); }
.timeline-item.welded .timeline-type { color: var(--accent-yellow); }
.timeline-item.inspected .timeline-type { color: var(--accent-green); }

.timeline-desc {
  font-size: 0.75rem;
  color: var(--text-muted);
  word-break: break-word;
}

.timeline-desc span {
  font-weight: 600;
  color: var(--text-primary);
}

/* Form inputs & styling */
.form-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 12px;
}

/* A grid item defaults to min-width:auto, which means a 1fr column CANNOT
   shrink below its content's intrinsic width - and an <input> carries one of
   about twenty characters. So in a narrow drawer the two columns refused to
   fit and the row overflowed the panel instead: Run Length was clipped at the
   edge and Notes ran out past it. min-width:0 is what makes 1fr actually mean
   "share the space available". */
.form-row > * { min-width: 0; }
.input-field input,
.input-field select,
.input-field textarea {
  width: 100%;
  min-width: 0;
}

.input-field {
  margin-bottom: 12px;
  display: flex;
  flex-direction: column;
}

.input-field label {
  font-size: 0.75rem;
  font-weight: 500;
  color: var(--text-secondary);
  margin-bottom: 4px;
}

.input-field select,
.input-field input,
.input-field textarea {
  background-color: var(--bg-input);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-sm);
  color: var(--text-primary);
  padding: 8px 12px;
  font-family: var(--font-main);
  font-size: 0.8rem;
  outline: none;
  transition: var(--transition-fast);
}

.input-field select:focus,
.input-field input:focus,
.input-field textarea:focus {
  border-color: var(--border-focus);
  box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.15);
}

.input-help {
  font-size: 0.65rem;
  color: var(--text-muted);
  margin-top: 3px;
}

.required {
  color: var(--accent-red);
}

/* Button UI Components */
.primary-button {
  background-color: var(--accent-blue);
  color: white;
  border: none;
  border-radius: var(--radius-sm);
  font-family: var(--font-main);
  font-size: 0.85rem;
  font-weight: 600;
  padding: 10px 18px;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  transition: var(--transition-fast);
}

.primary-button:hover {
  background-color: #2563eb;
  box-shadow: var(--shadow-glow);
}

.secondary-button {
  background: none;
  border: 1px solid var(--border-color);
  color: var(--text-primary);
  border-radius: var(--radius-sm);
  font-family: var(--font-main);
  font-size: 0.85rem;
  font-weight: 600;
  padding: 8px 14px;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  transition: var(--transition-fast);
}

.secondary-button:hover {
  background: rgba(var(--hi-rgb),0.04);
  border-color: var(--text-muted);
}

.danger-button {
  background-color: rgba(239, 68, 68, 0.1);
  border: 1px solid rgba(239, 68, 68, 0.4);
  color: var(--accent-red);
  border-radius: var(--radius-sm);
  font-family: var(--font-main);
  font-size: 0.85rem;
  font-weight: 600;
  padding: 8px 14px;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  transition: var(--transition-fast);
}

.danger-button:hover {
  background-color: var(--accent-red);
  color: white;
}

.icon-button {
  background: none;
  border: 1px solid var(--border-color);
  color: var(--text-secondary);
  border-radius: var(--radius-sm);
  padding: 8px;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: var(--transition-fast);
}

.icon-button:hover {
  background: rgba(var(--hi-rgb),0.04);
  color: var(--text-primary);
  border-color: var(--text-muted);
}

.icon-button.compact {
  padding: 4px;
}

.text-button {
  background: none;
  border: none;
  color: var(--text-secondary);
  padding: 6px;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 4px;
  transition: var(--transition-fast);
}

.text-button:hover {
  background: rgba(var(--hi-rgb),0.05);
  color: var(--text-primary);
}

.w-full {
  width: 100%;
}

.hidden {
  display: none !important;
}

/* Modals & Dialog overlays */
.modal-overlay {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: rgba(15, 23, 42, 0.75); /* a scrim: dark in either theme */
  backdrop-filter: blur(4px);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 100;
  padding: 16px;
}

.modal-card {
  background-color: var(--bg-secondary);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-lg);
  max-width: 500px;
  width: 100%;
  box-shadow: 0 20px 25px -5px rgba(0,0,0,calc(0.5 * var(--shadow-k)));
  display: flex;
  flex-direction: column;
  max-height: 90vh;
}

.modal-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 16px 24px;
  border-bottom: 1px solid var(--border-color);
}

.modal-header h2 {
  font-size: 1.05rem;
  font-weight: 700;
  display: flex;
  align-items: center;
  gap: 10px;
}

.modal-header h2 i {
  color: var(--accent-cyan);
}

.modal-body {
  padding: 24px;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 20px;
}

.settings-group {
  border-bottom: 1px solid var(--border-color);
  padding-bottom: 16px;
}

.settings-group:last-child {
  border-bottom: none;
  padding-bottom: 0;
}

.settings-group h3 {
  font-size: 0.85rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  margin-bottom: 6px;
  color: var(--accent-cyan);
}

.settings-desc {
  font-size: 0.75rem;
  color: var(--text-secondary);
  margin-bottom: 12px;
  line-height: 1.4;
}

.settings-actions {
  display: flex;
  gap: 12px;
  margin-top: 8px;
}

.modal-footer {
  padding: 16px 24px;
  border-top: 1px solid var(--border-color);
  display: flex;
  justify-content: flex-end;
}

/* Collapsible repair activity form */
.repair-toggle-container {
  margin: 6px 0 10px 0;
}

.toggle-link-btn {
  background: none;
  border: none;
  color: var(--accent-cyan);
  font-family: var(--font-main);
  font-size: 0.8rem;
  font-weight: 600;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 0;
  transition: var(--transition-fast);
}

.toggle-link-btn:hover {
  color: var(--accent-blue);
  text-decoration: underline;
}

.toggle-link-btn i {
  width: 15px;
  height: 15px;
  transition: transform 0.25s ease;
}

.toggle-link-btn.active {
  color: var(--accent-red);
}

.toggle-link-btn.active i {
  transform: rotate(45deg);
}

.collapsible-section {
  border-top: 1px dashed var(--border-color);
  padding-top: 12px;
  margin-bottom: 8px;
  animation: slideDown 0.25s ease-out;
}

@keyframes slideDown {
  from {
    opacity: 0;
    transform: translateY(-8px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

/* Text color helper classes */
.text-green { color: var(--accent-green) !important; }
.text-yellow { color: var(--accent-yellow) !important; }
.text-orange { color: #f97316 !important; }
.text-red { color: var(--accent-red) !important; }

/* ==========================================================================
   STAT CARD CLICKABLE POPOVER STYLES
   ========================================================================== */

/* Floating Panel */
.stat-details-panel {
  position: absolute;
  /* Centred on the canvas because it IS in the canvas wrapper, which is the
     positioned ancestor. No shell arithmetic: an earlier version computed this
     from --rail-w and the variable inherited correctly, but `left` did not
     re-resolve when the rail was collapsed or dragged, so the list drifted off
     the map. Position it where it belongs and the problem does not exist. */
  top: 24px;
  left: 50%;
  transform: translateX(-50%) translateY(-20px) scale(0.98);
  width: 90%;
  max-width: 900px;
  background: var(--bg-secondary);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-md);
  box-shadow: 0 20px 40px rgba(0,0,0,calc(0.6 * var(--shadow-k)));
  z-index: 1000;
  opacity: 0;
  pointer-events: none;
  transition: all 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
  display: flex;
  flex-direction: column;
  max-height: 50vh;
}

.stat-details-panel.open {
  opacity: 1;
  pointer-events: auto;
  transform: translateX(-50%) translateY(0) scale(1);
}

.stat-details-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 12px 20px;
  border-bottom: 1px solid var(--border-color);
  background: rgba(0,0,0,0.25);
  border-radius: var(--radius-md) var(--radius-md) 0 0;
}

.stat-details-header h3 {
  font-size: 0.95rem;
  color: var(--accent-cyan);
  display: flex;
  align-items: center;
  gap: 8px;
}

.stat-details-actions {
  display: flex;
  align-items: center;
  gap: 10px;
}

.report-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 12px;
  font-size: 0.8rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  color: #021018;
  background: linear-gradient(135deg, var(--accent-cyan), var(--accent-blue));
  border: none;
  border-radius: var(--radius-md, 8px);
  cursor: pointer;
  white-space: nowrap;
  box-shadow: 0 2px 10px rgba(6, 182, 212, 0.25);
  transition: transform 0.12s ease, box-shadow 0.12s ease, filter 0.12s ease;
}

.report-btn:hover {
  filter: brightness(1.08);
  box-shadow: 0 4px 16px rgba(6, 182, 212, 0.4);
  transform: translateY(-1px);
}

.report-btn:active {
  transform: translateY(0);
}

.report-btn i {
  width: 15px;
  height: 15px;
}

.stat-details-body {
  padding: 16px 20px;
  overflow-y: auto;
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-content: flex-start;
}

/* Badges for individual tubes */
.tube-badge {
  background: var(--bg-primary);
  border: 1px solid var(--border-color);
  padding: 6px 12px;
  border-radius: 20px;
  font-size: 0.75rem;
  font-weight: 500;
  color: var(--text-primary);
  cursor: pointer;
  transition: var(--transition-fast);
  display: flex;
  align-items: center;
  gap: 8px;
}

.tube-badge:hover {
  border-color: var(--accent-blue);
  background: rgba(59, 130, 246, 0.15);
  transform: scale(1.05);
}

.tube-badge .wall-tag {
  font-weight: 800;
  color: var(--text-muted);
  font-size: 0.65rem;
  text-transform: uppercase;
  background: rgba(var(--hi-rgb),0.05);
  padding: 2px 6px;
  border-radius: 10px;
}

/* Year Grouping in Stat Details */
.year-group {
  width: 100%;
  margin-bottom: 16px;
}
.year-group-title {
  font-size: 0.8rem;
  color: var(--accent-cyan);
  border-bottom: 1px solid var(--border-color);
  padding-bottom: 6px;
  margin-bottom: 10px;
  text-transform: uppercase;
  letter-spacing: 1px;
  display: flex;
  align-items: center;
}
.year-group-badges {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

/* Interactive UTG Legend Filter */
.interactive-utg-legend {
  cursor: pointer;
  padding: 4px 10px;
  border-radius: 6px;
  border: 1px solid transparent;
  transition: all 0.2s ease;
  user-select: none;
}
.interactive-utg-legend:hover {
  background: rgba(var(--hi-rgb),0.08);
}
.interactive-utg-legend.active-filter {
  background: rgba(6, 182, 212, 0.15);
  border-color: var(--accent-cyan);
}

/* Dropdown Option Styling Fix for Dark Mode */
select option {
  background-color: var(--bg-secondary);
  color: var(--text-primary);
}

/* New Thick/Over-nominal classes */
.color-thick { background-color: #f8fafc; }
.text-white { color: #f8fafc !important; }

/* ==========================================================================
   PROJEKT Entropy IPHONE & MOBILE VIEWPORT SYSTEM DESTRUCTION FIX
   ========================================================================== */

/* 1. Focus is on every screen size now (ADR 0023: the prototype's zoom
   cluster has it on the desktop too). */

/* 2. MOBILE & TABLET OVERRIDES: Triggers on screens 768px wide or smaller */
/* The shell stacks below 1000px, not 768px. Measured: the pinned toolbar
   actions plus a 370px rail need about 1000px, and below that "Find" ended
   up underneath its neighbours — a control you cannot reach, which is the
   whole thing the toolbar work set out to end. A window this narrow wants
   the map full width with the rail beneath it anyway. */
@media (max-width: 999px) {
  
  html, body {
    overflow-x: hidden !important;
    overflow-y: auto !important;
    height: auto !important;
    max-width: 100vw !important;
  }

  .dashboard-container {
    height: auto !important;
    width: 100% !important;
    max-width: 100vw !important;
    padding: 8px !important;
    gap: 12px !important;
    overflow-x: hidden !important;
  }

  
  

  

  

  

  

  

  
  
  

  /* The KPI ribbon that lived here is gone: the cards moved into the rail's
     Findings panel, which stacks on mobile like every other rail panel. */

  

  /* --- SECTION 3: CANVAS MAP VIEWPORT CONTAINER --- */
  .dashboard-main {
    /* The base layout is a grid now, so flex-direction would be ignored here —
       restate the stack as grid rows. */
    /* Every region named, or the unnamed ones are placed in columns of their
       own and the map's column is squeezed to nothing (measured: 0 px at an
       882 px window). The map first, so a tablet in portrait opens on it; the
       tools under it, then the open tool, then the left rail, which is the
       tallest and would push the map off the first screen. */
    grid-template-areas: "canvas" "strip" "rail" "nav" !important;
    grid-template-columns: minmax(0, 1fr) !important;
    grid-template-rows: auto auto auto auto !important;
    height: auto !important;
    gap: 12px !important;
    width: 100% !important;
  }

  .viewport-section {
    height: 480px !important;
    min-height: 480px !important;
    width: 100% !important;
    display: flex !important;
    flex-direction: column !important;
  }

  .canvas-wrapper {
    flex: 1 !important;
    height: 100% !important;
    width: 100% !important;
  }

  

  .wall-tabs {
    overflow-x: auto !important;
    display: flex !important;
    padding-bottom: 6px !important;
    width: 100% !important;
    -webkit-overflow-scrolling: touch;
  }

  .wall-tab {
    padding: 6px 12px !important;
    font-size: 0.75rem !important;
    white-space: nowrap !important;
    flex-shrink: 0 !important;
  }

  .viewport-actions {
    justify-content: space-between !important;
    flex-wrap: wrap !important;
    gap: 8px !important;
    width: 100% !important;
  }

  /* --- SECTION 4: SIDEBAR & ENTRY FORM ELEMENTS --- */
  .sidebar-section {
    width: 100% !important;
    height: auto !important;
  }

  .sidebar-panel {
    height: auto !important;
    min-height: 420px !important;
  }
  
  .form-row {
    grid-template-columns: 1fr !important; /* Stack form inputs into single columns on mobile mobile screens */
    gap: 0 !important;
  }
  
  .stat-details-panel {
    width: 96% !important;
    max-height: 75vh !important;
    top: 220px !important;
  }
  
  /* --- SECTION 5: mobile focus: the button shows everywhere now (ADR 0023). --- */

  

  

  
}

/* Add to styles.css */
.modal {
    position: fixed;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    z-index: 1000;
    background: var(--bg-secondary);
    padding: 20px;
    border-radius: var(--radius-md);
    box-shadow: 0 10px 25px rgba(0,0,0,calc(0.5 * var(--shadow-k)));
}
.hidden { display: none !important; }

.form-group {
    display: flex;
    flex-direction: column;
    gap: 8px;
    margin-bottom: 15px;
}

.form-group label {
    font-size: 0.85rem;
    color: var(--text-secondary);
    font-weight: 500;
}

.styled-input {
    background-color: var(--bg-secondary);
    border: 1px solid var(--border-color);
    border-radius: 6px;
    padding: 10px 12px;
    color: white;
    font-size: 0.9rem;
    width: 100%;
    transition: border-color 0.2s;
}

.styled-input:focus {
    outline: none;
    border-color: #3b82f6; /* Matches your active blue accent */
    box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.2);
}

/* Outage Badges */
.outage-badge {
  font-size: 0.65rem;
  padding: 2px 6px;
  border-radius: 4px;
  font-weight: 600;
  text-transform: uppercase;
  margin-left: 8px;
  display: inline-block;
  vertical-align: middle;
}

.outage-planned {
  background-color: rgba(59, 130, 246, 0.15);
  color: var(--tint-blue);
  border: 1px solid rgba(59, 130, 246, 0.3);
}

.outage-forced {
  background-color: rgba(239, 68, 68, 0.15);
  color: var(--tint-hot);
  border: 1px solid rgba(239, 68, 68, 0.3);
}

/* The erosion panel used to be re-floated over the canvas here. It is a docked
   rail panel now, and on mobile the rail stacks under the map with everything
   else, so it needs no special case at all. */
@media (max-width: 999px) {
  .dock-panel { max-height: none; }
}

/* ==========================================================================
   SSO login gate, signed-in chip, and view-only mode
   ========================================================================== */
/* ---- The gate ------------------------------------------------------------
   Open on the dot field, with no card and no glass (ADR 0029, superseding
   0015): boxed, the sign-in read as cheap. Centred like a page's opening.

   Its palette is a few names, SCOPED TO THE GATE rather than :root, pointed
   at the application's own tokens: one cyan, the app bar's. web/lab/gate.html
   tunes them by writing the same names onto the overlay. */
#entropy-login-overlay {
  /* Flat, darker than the application's field: a centre gradient hid the
     dots where the eye lands, so there is none (Hans). */
  --gl-bg:      #060a12;
  --gl-ink:     var(--text-primary);    /* #f8fafc */
  --gl-muted:   var(--text-secondary);  /* #94a3b8 */
  --gl-accent:  var(--accent-cyan);     /* #06b6d4 */
  --gl-danger:  var(--accent-red);      /* #ef4444 */

  position: fixed; inset: 0; z-index: 10000;
  /* A column: the sign-in centred in what is left above the maker's line, so
     on a short screen the two stack rather than overlap. */
  display: flex; flex-direction: column; align-items: center;
  padding: 16px;
  overflow-y: auto;
  background: var(--gl-bg);
  /* The gate is the ONE place this product is not Outfit. Chosen in
     web/lab/gate.html against the dot field rather than from a list.
     SCOPED HERE, and nowhere near --font-main; the application behind the gate
     stays on Outfit. Named here AND fetched in index.html - a face named and
     not loaded falls back silently and the change is invisible. */
  --gate-font: 'Red Hat Display', var(--font-main);
  /* The name. The React Bits page leaves its font at `inherit`, which is not a
     font - it means "whatever the surrounding CSS says", and there it was that
     site's own type. The stack is written out here so the gate decides it. */
  --gate-mark-font: system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
  font-family: var(--gate-font);
}

/* The field (entropy_login_backdrop.js): under everything, never in the way
   of a click. */
.entropy-login-field { position: absolute; inset: 0; z-index: 0; pointer-events: none; }

.entropy-login-hero {
  position: relative; z-index: 1;
  margin: auto 0;
  width: min(440px, 100%);
  display: flex; flex-direction: column; align-items: center;
  text-align: center;
}

/* The strapline above the name. A hairline under it, not a pill around it: the
   bordered capsule read as a component dropped onto the page rather than part of
   it, and it was the last box left after ADR 0029 took the card away. Chosen in
   web/lab/gate.html against the live field. */
.entropy-login-sub {
  margin: 0 0 18px;
  padding: 0 0 8px;
  border-bottom: 1px solid rgba(var(--hi-rgb),0.12);
  /* The rule is as wide as the words rather than the hero because .entropy-login-hero
     is a flex column that centres rather than stretches, so this is shrink-to-fit
     already. `display: inline-block` here would be inert - a flex container
     blockifies its children - so it is not written, and anything that changes the
     hero away from flex has to look at this line again. Measured: 280px of words
     in a 440px hero. */
  color: var(--gl-muted);
  font-size: 0.72rem; letter-spacing: 1px; text-transform: uppercase;
  white-space: nowrap;
}

/* The name, the page's headline: <entropy>, as Hans set it on the React Bits
   page (one colour, system font, 800, -0.06em), at 72 px rather than its 116,
   which drowned the form. A box of its own,
   wider than the form, because the warped drawing (entropy_login_backdrop.js)
   needs room to bend into; while it is drawn, the text under it is kept for
   screen readers and hidden from the eye. */
.entropy-login-title {
  position: relative;
  width: min(480px, calc(100vw - 32px)); height: clamp(64px, 16vw, 96px);
  display: flex; align-items: center; justify-content: center;
  margin: 0 0 14px; color: #f8f5ff;
  font-family: var(--gate-mark-font);
  font-size: clamp(2.5rem, 11vw, 72px); line-height: 0.9; font-weight: 800; letter-spacing: -0.06em;
}
.entropy-login-bracket { color: inherit; }
.entropy-login-title.is-warped > span { visibility: hidden; }
.entropy-login-warp { position: absolute; inset: 0; }

/* The way in: solid white, the one bright thing on the page. */
.entropy-signin-button {
  min-height: 48px;
  display: inline-flex; align-items: center; justify-content: center; gap: 10px;
  padding: 0 22px;
  border-radius: 12px;
  border: 1px solid #ffffff;
  background: #ffffff;
  color: #0b1220; font-family: inherit; font-size: 0.92rem; font-weight: 600;
  cursor: pointer; transition: var(--transition-fast);
}
.entropy-signin-button:hover { background: #e2e8f0; border-color: #e2e8f0; }
.entropy-signin-button:focus-visible { outline: 2px solid var(--gl-accent); outline-offset: 3px; }
.entropy-signin-button:disabled { opacity: 0.55; cursor: default; }
/* The ways in, one column: email and password, then Google (ADR 0030). */
.entropy-signin-ways { width: min(380px, 100%); display: flex; flex-direction: column; }
.entropy-password-form { display: flex; flex-direction: column; gap: 12px; margin: 0; }
.entropy-sr {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.entropy-field {
  width: 100%; height: 50px; padding: 0 16px;
  border-radius: 12px; border: 1px solid rgba(var(--hi-rgb),0.16);
  /* Solid, so the dots do not show through what is typed. */
  background: #0a0f1a; color: var(--gl-ink);
  font-family: inherit; font-size: 0.95rem;
  transition: var(--transition-fast);
}
.entropy-field::placeholder { color: var(--gl-muted); }
.entropy-field:focus { outline: none; border-color: var(--gl-accent); box-shadow: 0 0 0 3px rgba(6, 182, 212, 0.18); }
.entropy-password-row { position: relative; }
.entropy-password-row .entropy-field { padding-right: 52px; }
.entropy-password-toggle {
  position: absolute; right: 6px; top: 50%; transform: translateY(-50%);
  width: 40px; height: 40px; border-radius: 10px;
  display: inline-flex; align-items: center; justify-content: center;
  border: 0; background: transparent; color: var(--gl-muted); cursor: pointer;
}
.entropy-password-toggle:hover, .entropy-password-toggle[aria-pressed="true"] { color: var(--gl-ink); }
.entropy-password-toggle:focus-visible { outline: 2px solid var(--gl-accent); outline-offset: 1px; }
.entropy-signin-ways .entropy-signin-button { width: 100%; }
.entropy-forgot {
  align-self: center; margin-top: 2px;
  border: 0; background: none; padding: 4px 6px; cursor: pointer;
  color: var(--gl-muted); font-family: inherit; font-size: 0.82rem;
}
.entropy-forgot:hover { color: var(--gl-ink); text-decoration: underline; }
.entropy-forgot:focus-visible { outline: 2px solid var(--gl-accent); outline-offset: 2px; border-radius: 6px; }
.entropy-signin-or {
  display: flex; align-items: center; gap: 12px; margin: 18px 0;
  color: var(--gl-muted); font-size: 0.78rem; text-transform: uppercase; letter-spacing: 1px;
}
.entropy-signin-or::before, .entropy-signin-or::after {
  content: ''; flex: 1; height: 1px; background: rgba(var(--hi-rgb),0.12);
}
/* Google, the second way in: outlined, so Log in stays the one bright thing. */
.entropy-signin-button.google {
  background: transparent; border-color: rgba(var(--hi-rgb),0.28); color: var(--gl-ink);
}
.entropy-signin-button.google:hover { background: rgba(var(--hi-rgb),0.06); border-color: var(--gl-accent); }

/* Sign out, when shown, is the quieter of the two. */
.entropy-signin-button.secondary {
  background: rgba(var(--hi-rgb),0.06); border-color: rgba(var(--hi-rgb),0.14); color: var(--gl-ink);
}
.entropy-signin-button.secondary:hover { background: rgba(var(--hi-rgb),0.12); border-color: var(--gl-accent); }
/* The provider mark keeps its own colours on its own white (Google's terms). */
.entropy-signin-mark {
  display: inline-flex; align-items: center; justify-content: center;
  width: 22px; height: 22px; border-radius: 50%;
  background: #fff; flex: 0 0 auto;
}

.entropy-login-choice { margin-top: 14px; display: flex; gap: 8px; width: 100%; }
.entropy-login-choice select,
.entropy-login-select {
  flex: 1 1 auto;
  min-width: 0;
  padding: 10px 30px 10px 12px;
  border-radius: 10px;
  /* appearance:none, or Windows draws the native control white on dark; the
     chevron is drawn back on so it still reads as something that opens. */
  appearance: none;
  -webkit-appearance: none;
  background-color: rgba(8, 11, 16, 0.85);
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8' fill='none' stroke='%2394a3b8' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M1 1.5 6 6.5 11 1.5'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 10px center;
  color: var(--text-primary, #f8fafc);
  border: 1px solid rgba(var(--hi-rgb),0.14);
  font-family: inherit;
  font-size: 0.86rem;
  cursor: pointer;
}
.entropy-login-choice select:hover,
.entropy-login-choice select:focus { border-color: var(--gl-accent); outline: none; }
/* The open list. Its own rule, not the app's `select option`, so the gate's
   token check sees what it reads: on a light theme that rule painted the
   chosen unit white on white. */
.entropy-login-select option { background-color: var(--bg-secondary); color: var(--text-primary); }

/* One line's height reserved under the button, so a message appearing under
   the pointer mid-click does not move it. */
.entropy-login-status { color: var(--gl-muted); font-size: 0.84rem; line-height: 1.35; min-height: 1.35em; margin: 14px 0 0; }
.entropy-login-error { color: var(--gl-danger); font-size: 0.84rem; line-height: 1.35; min-height: 0; margin: 0; }
.entropy-login-error:not(:empty) { margin-top: 6px; }
.entropy-login-maker {
  position: relative; z-index: 1;
  flex: 0 0 auto;
  margin-top: 24px;
  display: flex; flex-direction: column; align-items: center; gap: 2px;
  color: var(--gl-muted); opacity: 0.75;
  font-size: 0.7rem; line-height: 1.5; letter-spacing: 0.4px; text-align: center;
}
.entropy-login-foot {
  color: var(--gl-muted); font-size: 0.76rem; line-height: 1.6;
  margin: 26px 0 0;
  max-width: 46ch;
}

/* View-only mode: best-effort hiding of common write controls.
   (The gateway + Apps Script are the real enforcement — this is UX only.) */
body.entropy-viewer #btn-add-material,
body.entropy-viewer #btn-add-year-color,
body.entropy-viewer #btn-exec-commit-spray,
body.entropy-viewer #btn-logo-upload { display: none !important; }
#logo-status.err { color: var(--accent-red); }

/* Not in the licence: the controls that would ask for UTG readings, and the
   figures computed only from them. UX only, like the role above - every gated
   route refuses on its own. Findings goes whole: every card in it, "Spray
   Only" included, is counted from readings. The ad-hoc buttons go with the
   form they open - a visible button that opens nothing looks broken. Hidden rather than disabled,
   because a greyed control invites a support call about a feature the client
   never bought. app.js adds `entropy-has-<entry>` for every entry the
   licence lists, so a new feature or boiler section is a rule here, not code. */
body.entropy-licensed:not(.entropy-has-utg) #btn-bulk-open,
body.entropy-licensed:not(.entropy-has-utg) #btn-toggle-erosion,
body.entropy-licensed:not(.entropy-has-utg) #btn-return-utg,
body.entropy-licensed:not(.entropy-has-utg) #panel-form,
body.entropy-licensed:not(.entropy-has-utg) #btn-trigger-form,
body.entropy-licensed:not(.entropy-has-utg) #btn-trigger-form-empty,
body.entropy-licensed:not(.entropy-has-utg) #legend-editor,
body.entropy-licensed:not(.entropy-has-utg) #activity-legend-group,
body.entropy-licensed:not(.entropy-has-utg) #stat-total-logs,
body.entropy-licensed:not(.entropy-has-utg) #panel-findings { display: none !important; }

/* Refresh button spin while syncing */
@keyframes entropy-spin { to { transform: rotate(360deg); } }
#btn-refresh-data.spinning i { display: inline-block; animation: entropy-spin 0.8s linear infinite; }

/* Editable map color-scale editor (Settings) */
#colorscale-editor { display: flex; flex-direction: column; gap: 16px; margin-top: 8px; }
.cs-scale { background: rgba(0,0,0,0.18); border: 1px solid var(--border-color); border-radius: var(--radius-sm); padding: 10px 12px; }
.cs-scale-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px; }
.cs-scale-title { font-size: 0.8rem; font-weight: 700; color: var(--text-primary); letter-spacing: 0.3px; }
.cs-scale-title .cs-unit { color: var(--text-muted); font-weight: 500; font-size: 0.72rem; }
.cs-add { font-size: 0.68rem !important; padding: 4px 10px !important; margin: 0 !important; width: auto !important; }
.cs-preview { width: 100%; height: 14px; border-radius: 6px; border: 1px solid rgba(var(--hi-rgb),0.12); }
.cs-prev-labels { display: flex; justify-content: space-between; font-size: 0.62rem; color: var(--text-muted); margin: 3px 2px 8px; }
.cs-rows { display: flex; flex-direction: column; gap: 6px; }
.cs-row { display: flex; align-items: center; gap: 8px; }
.cs-row .cs-color { width: 34px; height: 28px; padding: 0; border: 1px solid var(--border-color); border-radius: 5px; background: transparent; cursor: pointer; flex-shrink: 0; }
.cs-row .cs-val { flex: 1; max-width: 120px; padding: 5px 8px; background: var(--bg-input, #0f172a); border: 1px solid var(--border-color); color: var(--text-primary); border-radius: 5px; outline: none; font-family: var(--font-main); font-size: 0.78rem; }
.cs-row .cs-unit-lbl { font-size: 0.66rem; color: var(--text-muted); min-width: 34px; }
.cs-row .cs-del { margin-left: auto; width: 26px; height: 26px; line-height: 1; border: 1px solid var(--border-color); background: rgba(239,68,68,0.08); color: var(--accent-red, #ef4444); border-radius: 5px; cursor: pointer; font-size: 15px; font-weight: 700; }
.cs-row .cs-del:hover { background: rgba(239,68,68,0.2); }

/* The tab strip is the scroll container; the arrows are positioned against it. */
.wall-tabs-scroller {
  position: relative;
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  /* Separated from VIEW: and the toolbar actions, which the right-hand chevron
     used to sit hard against. A control needs a boundary to belong to. */
  align-items: stretch;
  padding-right: 4px;
  margin-right: 8px;
  border-right: 1px solid var(--border-color);
}
/* The tab-strip scroll arrows.

   IN FLOW, not floating over the strip. They were absolutely positioned, so they
   were lifted out of the row and drawn ON TOP of the tabs: at the left edge the
   first tab's label was legible straight through the arrow, which reads as a
   rendering fault rather than a control. As flex siblings of the strip they take
   their own space and cannot overlap anything, ever - and the fade that used to
   be their background is gone with the overlap that made it necessary.

   Hidden with display, not opacity, so a strip that fits reserves nothing. */
.hdr-scroll-btn {
  display: none;
  flex: 0 0 auto;
  align-self: center;
  width: 20px;
  height: 24px;
  /* Room on BOTH sides. Stretched full height and flush against its neighbours,
     the chevron touched the last tab on one side and the strip's border on the
     other, so it read as part of the boundary rather than as a button. */
  margin: 0 6px;
  border-radius: var(--radius-sm);
  align-items: center;
  justify-content: center;
  padding: 0;
  cursor: pointer;
  background: transparent;
  border: none;
  color: var(--text-muted);
  font-size: 17px; font-weight: 800; line-height: 1;
  transition: color 0.12s ease, background 0.12s ease;
}
.hdr-scroll-btn.show { display: flex; }
.hdr-scroll-btn:hover { color: var(--accent-cyan); background: rgba(6, 182, 212, 0.1); }
.hdr-scroll-btn:focus-visible { outline: 2px solid var(--accent-cyan); outline-offset: -2px; }

/* Interpolation method disclaimer note (map corner) */
#interp-note {
  position: absolute; left: 14px; bottom: 14px; z-index: 40;
  max-width: 340px; padding: 9px 12px;
  background: rgba(var(--note-rgb), 0.92); border: 1px solid var(--border-color);
  border-left: 3px solid var(--accent-cyan); border-radius: 6px;
  font-size: 0.68rem; line-height: 1.45; color: var(--text-secondary);
  box-shadow: var(--shadow-md); backdrop-filter: blur(3px); pointer-events: none;
}
#interp-note strong { color: var(--accent-cyan); font-weight: 700; }
#interp-note em { color: var(--text-muted); font-style: italic; }


/* Interpolation note escalates when the interpolated field is DERIVED
   (Erosion Rate / Forecast / Worst-Case). Wider, red-railed, and the warning
   line reads before the model description so it cannot be skimmed past. */
#interp-note.is-compounded {
  max-width: 430px;
  border-left-color: var(--accent-red, #ef4444);
  background: rgba(var(--note-bad-rgb), 0.94);
}
#interp-note.is-compounded strong { color: var(--tint-red); }
#interp-note.is-compounded em { color: var(--map-title); }

/* ENTROPY-PORT:CSS BEGIN */

/* The import tool's buttons, for a viewer (the tool is in shell.css, 5g). */
body.entropy-viewer #btn-bulk-open,
body.entropy-viewer #btn-coating-import { display: none; }

/* Thermal spray report - options step */
.spray-report-opts { position:fixed; inset:0; z-index:1200; display:flex;
  align-items:center; justify-content:center; background:rgba(2, 6, 23, 0.66); /* a scrim: dark in either theme */ }
.spray-report-opts .sro-card { width:min(460px, calc(100vw - 40px));
  background:var(--bg-secondary); border:1px solid var(--border-color);
  border-radius:10px; padding:20px 22px; box-shadow:0 18px 50px rgba(0,0,0,calc(0.5 * var(--shadow-k))); }
.spray-report-opts h3 { margin:0 0 16px; font-size:1rem; color:var(--text-primary); }
.spray-report-opts .sro-row { display:flex; align-items:center; gap:12px;
  margin-bottom:6px; font-size:0.8rem; color:var(--text-secondary); }
.spray-report-opts .sro-row span { flex:0 0 110px; }
.spray-report-opts select { flex:1; background:var(--bg-primary); color:var(--text-strong);
  border:1px solid var(--border-color); border-radius:4px; padding:5px 8px;
  font-family:var(--font-main); font-size:0.75rem; outline:none; cursor:pointer; }
.spray-report-opts select:disabled { opacity:0.5; cursor:not-allowed; }
.spray-report-opts .sro-hint { margin:0 0 16px 122px; font-size:0.68rem;
  color:var(--text-secondary); opacity:0.75; line-height:1.45; }
.spray-report-opts .sro-btns { display:flex; justify-content:flex-end; gap:8px;
  margin-top:4px; }
.spray-report-opts button { border-radius:5px; padding:7px 15px; cursor:pointer;
  font-family:var(--font-main); font-size:0.78rem; border:1px solid var(--border-color); }
.spray-report-opts .sro-ghost { background:transparent; color:var(--text-secondary); }
.spray-report-opts .sro-go { background:var(--accent-cyan, #06b6d4); color:#052e33;
  border-color:transparent; font-weight:600; }

/* View-only: say so where the action would have been, rather than hiding it. */
.viewonly-notice {
  display: flex; align-items: flex-start; gap: 9px;
  background: rgba(245,158,11,0.10); border: 1px solid rgba(245,158,11,0.35);
  border-left: 3px solid var(--accent-yellow, var(--tint-orange));
  border-radius: 6px; padding: 10px 12px;
  font-size: 0.74rem; line-height: 1.5; color: var(--text-secondary);
}
.viewonly-notice strong { color: var(--accent-yellow, var(--tint-orange)); }
.viewonly-notice i { flex: 0 0 auto; margin-top: 1px; width: 14px; height: 14px; }
.viewonly-notice.hidden { display: none !important; }

/* ENTROPY-PORT:CSS END */

/* Every vertically scrolling box keeps its content clear of its own
   scrollbar. None of these had right padding, so content - the reading
   history's outage badges, the erosion panel's inputs, the rail's panel
   edges - sat flush against or under the bar. Found by scanning every
   overflow-y box in a browser, not by reading this file. The gutter
   is reserved whether or not the box scrolls, so content does not jump
   sideways when a list grows long enough to need one. */
.timeline-container,
.dock-panel__body,
#commit-review-list,
#missing-color-list {
  scrollbar-gutter: stable;
  padding-right: 8px;
}

/* --- In-page dialogs (entropy_dialog.js) ------------------------------------
   Replace window.confirm / alert / prompt, which a sandboxed frame blocks.
   Above every modal, because a question can come from inside one (Settings). */
.entropy-dialog-overlay {
  position: fixed;
  inset: 0;
  z-index: 10000;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 16px;
  background: rgba(15, 23, 42, 0.7); /* a scrim: dark in either theme */
  backdrop-filter: blur(3px);
}
.entropy-dialog {
  width: min(440px, 100%);
  padding: 20px 22px 18px;
  border: 1px solid var(--border-color);
  border-radius: var(--radius-md);
  background: var(--bg-secondary);
  color: var(--text-primary);
  font-family: var(--font-main);
  box-shadow: 0 20px 45px rgba(0,0,0,calc(0.45 * var(--shadow-k)));
}
.entropy-dialog__title { margin: 0 0 8px; font-size: 1.05rem; font-weight: 600; }
.entropy-dialog__message { margin: 0; font-size: 0.9rem; line-height: 1.5; color: var(--text-secondary); white-space: pre-line; }
.entropy-dialog__input {
  width: 100%;
  margin-top: 12px;
  padding: 8px 10px;
  border: 1px solid var(--border-color);
  border-radius: var(--radius-sm);
  background: var(--bg-input);
  color: var(--text-primary);
  font: inherit;
}
.entropy-dialog__input:focus { outline: none; border-color: var(--accent-cyan); }
.entropy-dialog__actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 18px; }
.entropy-dialog__btn {
  padding: 7px 14px;
  border: 1px solid var(--border-color);
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--text-primary);
  font: inherit;
  font-size: 0.85rem;
  cursor: pointer;
}
.entropy-dialog__btn:hover { background: rgba(var(--hi-rgb),0.06); }
.entropy-dialog__btn:focus-visible { outline: 2px solid var(--accent-cyan); outline-offset: 2px; }
.entropy-dialog__btn.is-primary { border-color: var(--accent-cyan); background: var(--accent-cyan); color: #04121a; font-weight: 600; }
.entropy-dialog.is-danger .entropy-dialog__btn.is-primary { border-color: var(--accent-red); background: var(--accent-red); color: var(--text-strong); }

/* ===========================================================================
   The map-first shell (ADR 0019, docs/app-js-split.md step 3).

   Five regions: app bar | left rail · stage · dock · tool strip.
     - Left rail: what you LOOK at - Context, Boiler, View, Legend, Findings.
     - Stage: the map, with face and perspective above it.
     - Dock: the one open tool (entropy_dock.js). Zero wide when none is.
     - Tool strip: one button per tool; opens and closes the dock.
   Everything here was moved, not rebuilt: the same elements and ids, in new
   regions. Desktop only; below 1000px the stacked layout above still applies.
   =========================================================================== */
@media (min-width: 1000px) {
  .dashboard-main {
    --nav-width: 272px;
    --strip-w: 46px;
    grid-template-areas: "nav canvas rail strip";
    grid-template-columns: var(--nav-width) minmax(0, 1fr) var(--dock-w, 360px) var(--strip-w);
  }
  .dashboard-main.dock-empty {
    grid-template-columns: var(--nav-width) minmax(0, 1fr) 0px var(--strip-w);
  }
  .dashboard-main.dock-empty #rail-region { display: none; }
  .dashboard-main.nav-collapsed { --nav-width: 0px; }
  .dashboard-main.nav-collapsed .nav-rail { display: none; }
  .dashboard-main.strip-wide { --strip-w: 200px; }
}

.nav-rail .legend-title { flex: 1 0 100%; }
.nav-rail .legend-item { min-width: 0; }

/* One tool at a time, so the open one has the whole dock. The 46% cap was for
   a rail it shared with Findings and Details; that rail is gone. */
#rail-region > .dock-panel,
#rail-region > .sidebar-panel { flex: 1 1 auto; max-height: none; min-height: 0; }
/* Maximise belongs to a floating window; docked, the tool already has the column. */
#rail-region #btn-measure-maximise { display: none; }

/* Explanations for a control that is on but shows nothing (prototype QA). */
.panel-note { margin: 0 0 8px; font-size: 0.7rem; line-height: 1.45; color: var(--text-muted); }
.spray-filter-note { flex: 1 0 100%; color: var(--accent-yellow); font-size: 12px; padding: 2px 4px; }

/* ---- the world switch (coating step 4b) ---------------------------------- */

/* In the coating world the UTG-only controls say nothing true: its view modes,
   its legend and its findings. Hidden rather than removed; switching back
   brings each one back as it was. The coating legend and findings are step 4d. */
body.entropy-world-coating #view-mode-seg,
body.entropy-world-coating #legend-editor,
body.entropy-world-coating #activity-legend-group,
body.entropy-world-coating #panel-findings,
body.entropy-world-coating #details-utg,
body.entropy-world-coating #btn-trigger-form,
body.entropy-world-coating #btn-bulk-open,
body.entropy-world-coating #erosion-btn-label,
body.entropy-world-coating #erosion-forecast-panel,
body.entropy-world-coating #panel-form { display: none !important; }
/* And the coating world's own rail, details and Add form, only there (steps
   4d, 4e, 4f). The Add tool is one dock tool; the world picks its form. */
body:not(.entropy-world-coating) #coating-legend-group,
body:not(.entropy-world-coating) #panel-coating-findings,
body:not(.entropy-world-coating) #details-coating,
body:not(.entropy-world-coating) #btn-trigger-coating-form,
body:not(.entropy-world-coating) #btn-coating-import,
body:not(.entropy-world-coating) #coating-view-seg,
body:not(.entropy-world-coating) #coating-wear-btn-label,
body:not(.entropy-world-coating) #panel-coating-wear,
body:not(.entropy-world-coating) #panel-coating-form { display: none !important; }

/* Clean view last. In the prototype it was a one-shot link; here it is a
   setting that stays on (thicknessOnly), so it keeps its box to show it. */
.nav-rail #btn-toggle-clean { order: 99; margin-top: 4px; }

/* ---- the Boiler tree (step 3h) ------------------------------------------- */

.cs-note { margin: 4px 0 6px; font-size: 0.72rem; color: var(--text-muted); }

/* ---- the dock kit (step 3m) -------------------------------------------------
   ADR 0019's tool panel, from the prototype: a title bar with a close, a body
   of small uppercase sections and label | field rows, and the actions pinned
   in a footer. `dk-` so nothing collides with the older panel classes. */
.sidebar-panel.dk-tool { display: flex; flex-direction: column; padding: 0; overflow: hidden; }
.dk-head { padding: 10px 12px; }
.dk-head h2 { font-size: 14px; font-weight: 600; flex: 1; min-width: 0; }
.dk-x { width: 24px; height: 24px; border-radius: 5px; display: grid; place-items: center; color: var(--text-muted); background: none; border: 0; font-size: 18px; line-height: 1; cursor: pointer; }
.dk-x:hover { background: rgba(239, 68, 68, 0.15); color: var(--tint-red); }
.dk-state { flex: 1 1 0%; min-height: 0; display: flex; flex-direction: column; }
.dk-state.hidden { display: none !important; }
.dk-body { flex: 1 1 0%; min-height: 0; overflow: auto; padding: 12px; font-size: 13px; scrollbar-width: thin; scrollbar-color: var(--border-color) transparent; }
.dk-foot { padding: 10px 12px; border-top: 1px solid var(--border-color); display: grid; gap: 6px; }
.dk-foot-row { grid-template-columns: 1fr auto; }
.dk-h { font-size: 11px; font-weight: 600; letter-spacing: .6px; text-transform: uppercase; color: var(--text-secondary); margin: 16px 0 6px; display: flex; align-items: center; gap: 8px; }
.dk-body > .dk-h:first-child, .dk-body > :first-child .dk-h:first-child { margin-top: 0; }
.dk-note { font-size: 12px; color: var(--text-muted); line-height: 1.45; margin: 6px 0 0; }
.dk-btn { min-height: 30px; padding: 4px 12px; border-radius: 6px; border: 1px solid var(--border-color); background: var(--bg-primary); color: var(--text-primary); font: inherit; font-size: 13px;
  display: inline-flex; align-items: center; justify-content: center; gap: 6px; line-height: 1.2; cursor: pointer; }
.dk-btn:hover { border-color: var(--text-secondary); }
.dk-btn.primary { background: var(--accent-cyan); border-color: var(--accent-cyan); color: var(--on-accent); font-weight: 600; }
.dk-btn i, .dk-btn svg { width: 14px; height: 14px; }
.dk-readout { background: var(--bg-primary); border: 1px solid var(--border-color); border-radius: 8px; padding: 10px; margin-bottom: 8px; }

/* Cell details */
.cell-card-surface { font-size: 12px; color: var(--text-muted); }
.cell-card-title { font-size: 20px; font-weight: 700; margin: 2px 0; }
.cell-card-title small { font-size: 12px; font-weight: 500; color: var(--text-secondary); margin-left: 4px; }
.cell-card-spec { font-size: 12px; color: var(--text-muted); }
.cell-card-spec:empty { display: none; }

/* Add reading or repair (step 3m) */
.dk-h .dk-btn { margin-left: auto; }
.dk-btn-danger { color: var(--tint-red); border-color: rgba(239, 68, 68, 0.5); background: rgba(239, 68, 68, 0.08); }
.dk-foot-2 { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 2fr); gap: 6px; }
.dk-head .icon-button.compact { width: 24px; height: 24px; padding: 0; border: 0; background: none; color: var(--text-muted); }
.dk-head .icon-button.compact i { width: 16px; height: 16px; }

/* Kept from the old Area Tool for the panels still on the dk-* kit. */
.dk-swatch { width: 14px; height: 14px; border-radius: 3px; flex: none; border: 1px solid rgba(var(--hi-rgb),0.25); background: #64748b; }

.dk-btn-block { width: 100%; margin: 4px 0 8px; }

/* Findings list (step 3n) */
#findings-top-seg button.is-on { background: var(--accent-cyan); color: var(--on-accent); font-weight: 600; }

/* Spray record (step 3o) */
.dk-kv { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 4px 12px; margin: 0; font-size: 13px; }
.dk-kv dt { color: var(--text-muted); }
.dk-kv dd { margin: 0; text-align: right; font-variant-numeric: tabular-nums; display: flex; justify-content: flex-end; align-items: center; gap: 6px; }
.dk-area-readout b { font-size: 22px; font-weight: 700; margin-right: 6px; }
/* Its year, changed in place by an administrator; read as text by everyone else. */
.dk-year-edit { display: inline-flex; align-items: center; gap: 6px; }
.dk-year-edit .input { width: 84px; min-height: 30px; padding: 4px 8px; border-radius: 6px; border: 1px solid var(--border-color);
  background: var(--bg-primary); color: var(--text-primary); font: inherit; font-size: 13px; box-sizing: border-box; }
body:not(.entropy-viewer) #hist-spray-year,
body.entropy-viewer #hist-spray-year-edit { display: none; }
.dk-foot-even { grid-template-columns: 1fr 1fr; }

/* Help and the welcome guide (step 3r), from the prototype. */
.guide { position: fixed; inset: 0; z-index: 9800; font-family: var(--font-main); }
.guide.hidden { display: none; }
.guide-shade { position: absolute; border-radius: 10px; box-shadow: 0 0 0 9999px rgba(2, 6, 23, 0.72); /* a scrim: dark in either theme */ border: 2px solid var(--accent-cyan); transition: all .25s; pointer-events: none; }
.guide-card { position: absolute; width: 360px; max-width: calc(100vw - 24px); background: var(--bg-secondary); border: 1px solid var(--border-color); border-radius: 12px;
  box-shadow: 0 20px 50px rgba(0,0,0,calc(0.6 * var(--shadow-k))); padding: 16px; transition: all .25s; font-size: 13px; color: var(--text-primary); }
.guide-card h3 { font-size: 16px; margin: 0 0 6px; }
.guide-card p { color: var(--text-secondary); margin: 0 0 8px; line-height: 1.5; }
.guide-card ul { color: var(--text-secondary); margin: 0 0 8px 18px; padding: 0; line-height: 1.5; }
.guide-card li { margin-bottom: 3px; }
.guide-foot { display: flex; align-items: center; gap: 8px; margin-top: 12px; }
.guide-dots { display: flex; gap: 4px; margin-right: auto; }
.guide-dots i { width: 6px; height: 6px; border-radius: 50%; background: var(--dim); display: block; }
.guide-dots i.on { background: var(--accent-cyan); }
.guide-welcome { left: 50%; top: 50%; transform: translate(-50%, -50%); width: 520px; }


/* The coating Add form: one input per position across the tube (step 4f). */

