/* ============================================================
   portal.css — PORTAL MODE ONLY (map + pipeline).

   Every selector is scoped to [data-mode="portal"], so this file cannot
   affect the home page or any other page even if both are loaded.

   The old page-local portal/css/style.css and the portal/css/ folder
   with it no longer exist; everything that lived there is now in this
   file or in the shared sheets it sits alongside.

   Requires, in this order: tokens.css → base.css → topbar.css →
   controls.css → portal.css → analytic.css.
   ============================================================ */

/* RULE: .app AND .stage LIVE IN styles/controls.css, unscoped, NOT here.
   #viewer-root is a child of .stage, so if these rules were scoped to
   [data-mode="portal"] instead, the moment data-mode flipped to
   "analytic" .app would stop being a flex column and .stage would stop
   being position:relative — #viewer-root's inset:0 would then resolve
   against the viewport instead of the stage (a stretched scene, wrong
   aspect ratio) and the status bar, following a now-zero-height .stage,
   would jump to y=0 under the scene. The page shell is not mode-specific;
   only what fills it is. */

/* .map, not #map. main.js still binds MapLibre with container: 'map', so
   the id stays on the element for that one caller, but this selector no
   longer needs to read it: index.html's <div id="map" class="map"> carries
   both. */
[data-mode="portal"] .stage > .map { position: absolute; inset: 0; }

/* MapLibre's own chrome — our status bar carries the attribution instead. */
[data-mode="portal"] .maplibregl-ctrl-bottom-right,
[data-mode="portal"] .maplibregl-ctrl-bottom-left { display: none; }

/* Monochrome mode. ui/view-tools.js SECTION 6B puts .bw-mode on the map
   CONTAINER; the filter applies to the CANVAS only. The container holds the
   tool rail as a DOM child, so filtering it greyed out the toolbar along
   with the map — which killed the one thing the B&W button needs to show,
   its own active highlight, and drained the compass needle's orange north
   half. Everything the map actually draws (basemap plus the Three.js
   layers) shares this single canvas, so nothing map-side is lost by
   narrowing the selector; only true DOM overlays keep their colour. */
[data-mode="portal"] .map.bw-mode .maplibregl-canvas { filter: grayscale(1); }

/* Legend dock. Anchored to the viewport corner, not to the panel: a result
   the user already produced should stay checkable with the panel collapsed
   or switched to Pipeline. This rule owns only WHERE the dock sits — its
   look is styles/controls.css's `.legend, #legend-dock`, and its width is
   fixed there at 300px rather than matching the panel — legend is legend,
   panel is panel.
   The id selector is deliberate and is one of only two left in this file:
   this is a singleton dock that controls.css already addresses by the same
   id, and inventing a class for it would be a third name for one element.
   (#mode-dock, its bottom-left twin, is gone — that button is now folded
   into .col-left, which positions its own children.) */
[data-mode="portal"] #legend-dock {
  position: absolute; z-index: 12;
  right: var(--inset-overlay); bottom: var(--inset-overlay);
}

/* The basemap controls' wrapper. It carries the .ctrl-group-body class from
   when it was an accordion body; the class survives only for this padding
   rule, and the wrapper is relocated into the icon column's basemap popover
   by main.js right after init. */
[data-mode="portal"] .ctrl-group-body { padding-top: 2px; }

/* ============================================================
   THE LAYER POPOVER — the step 4 "Layers" and "Verification layers"
   controls (#wf-layer-controls), relocated by main.js into the icon
   column's Layer popover.
   ============================================================ */

/* RULE: `width`, not `max-width`. controls.css caps .vt-popover at
   --vt-popover-width (220px) at BOTH ends; a raised max-width stretches
   nothing, because a flex column of checkboxes and sliders has no intrinsic
   width to grow into. 300px so a 0-100 opacity slider has ~260px of travel
   and can actually be aimed — at 220 it had under 190.

   RULE: opens UPWARD. The Layer button is the last item in the column, so
   its top edge sits near the bottom of the viewport; a top-anchored popover
   this tall would run straight off the bottom. bottom-anchored, it grows up
   from the item instead. max-height + overflow-y keeps it inside the viewport
   whatever the window height. */
[data-mode="portal"] .vt-popover-layers {
  width: 300px;
  max-width: 300px;
  top: auto;
  bottom: 0;
  max-height: 78vh;
  overflow-y: auto;
}

/* The .wf-substep dashed rail + indent is for a numbered sub-step inside a
   panel step; in the popover it reads as clutter. The substep TITLE stays
   (it separates "Layers" from "Verification layers"). */
[data-mode="portal"] .vt-popover-layers .wf-substep {
  border-left: none;
  padding: 0;
  margin: 0 0 var(--sp-3);
}
[data-mode="portal"] .vt-popover-layers .wf-substep:last-child { margin-bottom: 0; }

/* A control whose capability the current basemap style simply does not have
   (positron has no fill-extrusion layers, so there are no native 3D
   buildings to toggle). Tone and cursor, not brightness — and STILL
   OCCUPYING ITS ROW: display:none'd here would collapse the row and make
   every control below jump on each theme switch. Sitting directly under
   "Dark Basemap", that shift would read as the dark toggle flickering and
   breaking its neighbour.
   RULE: these two rules are NOT prefixed with `#controls` — main.js moves
   #group-basemap-body into the tool rail's basemap popover right after
   init, at which point #controls stops being an ancestor, so a prefixed
   selector would silently lose its unavailable styling in the one place
   these rows are actually seen. */
[data-mode="portal"] .ctrl-row-unavailable label {
  color: var(--text-off); cursor: not-allowed;
}
[data-mode="portal"] .ctrl-note { display: none; font-style: italic; font-size: 0.9em; }
[data-mode="portal"] .ctrl-row-unavailable .ctrl-note { display: inline; }

/* an <hr> separates whole groups, so it takes the more generous step */
[data-mode="portal"] .fpanel-body hr {
  margin: var(--sp-2) 0; border: none; border-top: var(--border) solid var(--line);
}

/* ============================================================
   MAP OVERLAYS — corner-anchored utility classes.
   No caller yet: #legend-dock / .vt-root already carry their own
   hand-written position rules (above and in controls.css), predating this
   utility and matching it exactly (same --inset-overlay corner math), so
   converting them to use these classes would mean touching their markup for
   zero visual change. Defined here for spec completeness and for whatever
   the next overlay turns out to be (a coordinate readout, a north-locked
   indicator).
   ============================================================ */
[data-mode="portal"] .overlay {
  position: absolute; z-index: 4;
  background: var(--overlay-bg-thin);
  -webkit-backdrop-filter: var(--overlay-blur);
          backdrop-filter: var(--overlay-blur);
  border: var(--border) solid var(--line-strong); border-radius: var(--radius);
}
[data-mode="portal"] .overlay--tl { left: var(--inset-overlay);  top: var(--inset-overlay); }
[data-mode="portal"] .overlay--tr { right: var(--inset-overlay); top: var(--inset-overlay); }
[data-mode="portal"] .overlay--bl { left: var(--inset-overlay);  bottom: var(--inset-overlay); }
[data-mode="portal"] .overlay--br { right: var(--inset-overlay); bottom: var(--inset-overlay); }

/* ============================================================
   THE PANEL SHELL LIVES IN styles/controls.css, NOT HERE.

   `.col-left` / `.fpanel` / `.fpanel--grow|--thin` / `.fpanel-head|-body|
   -foot` and the panel-body checkbox rows live there UNSCOPED, because
   analytic mode builds its left column out of the same three pieces and
   the two must agree to the pixel. Only the portal-specific bit stays
   here: the step counter in the panel head.

   #controls keeps its id — main.js/controls.js bind by it — and .col-left
   is the name for what it now IS: a transparent flex column stacking the
   Simulation card over the mode switch, not a visual card itself.
   ============================================================ */

/* RULE: THE WRAPPER HAS TO PASS THE COLUMN'S HEIGHT THROUGH.
   .col-left is a flex column and .fpanel--grow asks for flex: 1, but the
   two are not parent and child: #group-simulation sits between them (a
   plain layout wrapper — see index.html), and a plain display:block
   wrapper both ignores flex-grow and defaults to min-height:auto. Without
   this rule the card sizes itself to its CONTENT and overflows — the card
   grows taller than the column that holds it, pushing the Analytic Mode
   button below the fold and stopping .fpanel-body from ever scrolling, the
   exact failure .fpanel's own min-height:0 comment describes, one level up. */
[data-mode="portal"] .col-left > .ctrl-group {
  display: flex; flex-direction: column; gap: var(--sp-3); min-height: 0;
}
[data-mode="portal"] .col-left > .ctrl-group:has(> .fpanel--grow) { flex: 1; }

/* the step counter is the panel head's single accent, and it exists only
   in portal mode — analytic has no steps to count. */
[data-mode="portal"] .fpanel-head .counter {
  margin-left: auto; color: var(--accent);
  font-size: var(--fs-eyebrow); letter-spacing: 0.14em; font-variant-numeric: tabular-nums;
}

/* ============================================================
   STEPS — a segmented rail carries progress. No chips, no rules.

   A 2px rail runs down a 16px gutter left of every step, one segment per
   step, coloured by that step's state: --done behind you, --accent where
   you are, --line ahead. It replaces TWO things at once — the
   DONE / ACTIVE / LOCKED chip that used to sit in each .step-head, and the
   1px rule that used to separate one step from the next. One mark now says
   where you are, instead of a word per step plus a line per boundary.

   RULE: THE 8px BREAK BETWEEN SEGMENTS IS THE DESIGN, not a rounding error.
   The bar counts the steps out rather than running them together; closing
   it turns four segments into one continuous line that says nothing. It is
   never typed twice — the two calc()s below derive it, so changing the
   gap or the panel padding cannot silently put it out of register:

     between steps   .fpanel-body's gap (--sp-5, controls.css)
                     minus the rail's own overshoot  =  --rail-break
     after the last  the rail runs on for exactly .fpanel-body's padding
                     (--sp-4), ending flush with the panel's padding edge,
                     so the bar is never SEEN to terminate on a step — and
                     because it stops flush rather than past it, it adds no
                     scrollable overflow to a body that otherwise fits.

   WARNING: the rail belongs to each .step, NOT to the scroll container: drawn by
   .fpanel-body it would scroll out of register with the steps it counts.
   .fpanel-body's overflow-y must stay auto (never hidden) in the block
   direction, or the negative `bottom` clips.

   Colour comes from data-state, position from the step's own box, so a
   reordered or virtualised list needs no change here. markStep()
   (workflow-controls.js) is the only writer of that attribute.

   `.step`/`.wf-step` and `.step-head`/`.wf-step-title` stay combined
   selectors: `.wf-step*` is the live name — refreshUI()'s markStep()
   toggles wf-step-active/-done/-locked and two :has() CTA rules read
   them — `.step`/`.step-head` are the design vocabulary's names, aliased
   here rather than requiring a markup rename.
   ============================================================ */
[data-mode="portal"] .step,
[data-mode="portal"] .wf-step {
  /* containing block for the rail below — without it the pseudo-element
     resolves against the panel and every step draws one full-height bar. */
  position: relative;
  padding-left: var(--rail-gutter);
  display: flex; flex-direction: column; gap: var(--sp-4);
}

/* The rail. `bottom` is negative so the segment reaches down INTO the gap
   below its step, stopping --rail-break short of the next one. */
[data-mode="portal"] .step::before,
[data-mode="portal"] .wf-step::before {
  content: "";
  position: absolute; left: 0; top: 0;
  bottom: calc(var(--rail-break) - var(--sp-5));
  width: var(--rail-w);
  background: var(--line);              /* ahead of you — not yet reached */
}
[data-mode="portal"] .step[data-state="done"]::before,
[data-mode="portal"] .wf-step.wf-step-done::before   { background: var(--done); }
[data-mode="portal"] .step[data-state="active"]::before,
[data-mode="portal"] .wf-step.wf-step-active::before { background: var(--accent); }

/* The last segment runs on to the panel's padding edge instead of stopping
   on its step — a bar that ends on the final step reads as "this is the
   end of the process", which is the one thing it must not say while you
   are still in it. --sp-4 is .fpanel-body's padding (controls.css). */
[data-mode="portal"] .step:last-child::before,
[data-mode="portal"] .wf-step:last-child::before { bottom: calc(-1 * var(--sp-4)); }

[data-mode="portal"] .step-head,
[data-mode="portal"] .wf-step-title { display: flex; align-items: center; gap: var(--sp-3); }

/* square index, aligned to the same grid as everything else — replaces the
   old 16px round .wf-step-num fill dot. */
[data-mode="portal"] .step-index {
  flex: none; width: 18px; height: 18px;
  display: flex; align-items: center; justify-content: center;
  font-size: var(--fs-eyebrow); color: var(--text-2);
  border: var(--border) solid var(--line-strong); border-radius: var(--radius);
}
[data-mode="portal"] .step[data-state="active"] .step-index,
[data-mode="portal"] .wf-step.wf-step-active .step-index { border-color: var(--accent); color: var(--accent); }
[data-mode="portal"] .step[data-state="done"] .step-index,
[data-mode="portal"] .wf-step.wf-step-done .step-index {
  border-color: var(--done); background: var(--done); color: var(--bg-ground);
}

/* a step that is not active dims its TITLE only, never its controls */
[data-mode="portal"] .step[data-state="done"] .t-step,
[data-mode="portal"] .step[data-state="locked"] .t-step,
[data-mode="portal"] .wf-step.wf-step-done .t-step,
[data-mode="portal"] .wf-step.wf-step-locked .t-step { color: var(--text-2); }

/* a completed step collapses to its result + one way back. The EDIT
   affordance re-expands it IN PLACE — it does not jump back a step and does
   not clear the result (Q5=A). Re-running is still one click away, which is
   what the three-state CTA (styles/controls.css) was built around.
   #wf-step-4 is EXCLUDED on purpose: it has no primary action to re-run and
   no .step-edit button in its markup, so collapsing it the instant a
   prediction exists would hide the very Time/Air controls it exists to
   show — see the comment on that step in index.html. */
[data-mode="portal"] .step[data-state="done"]:not(.step--open):not(#wf-step-4) > *:not(.step-head):not(.meta),
[data-mode="portal"] .wf-step.wf-step-done:not(.step--open):not(#wf-step-4) > *:not(.wf-step-title):not(.meta) {
  display: none;
}

[data-mode="portal"] .step-edit {
  display: none;
  margin-left: auto; flex: none;
  background: none; border: 0; cursor: pointer;
  font-size: var(--fs-eyebrow); letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--text-2);
}
[data-mode="portal"] .step-edit:hover { color: var(--text-1); }
[data-mode="portal"] .step[data-state="done"] .step-edit,
[data-mode="portal"] .wf-step.wf-step-done .step-edit { display: inline-block; }

/* PROGRESSIVE DISCLOSURE. A locked step shows its title line
   and nothing else. The panel was reported as "too many settings"; most of
   them belonged to steps that could not be run yet, so they were noise with
   a delayed payoff.

   Driven entirely by the class refreshUI() already writes — "should this be
   visible" is a function of the step's state, and that state has exactly
   one owner. Hiding controls from JS instead would mean a second list to
   keep in sync, and the failure mode there is a control leaking into a
   locked step with no error anywhere.

   `> *` is direct children only: the title stays, everything else goes.
   That is also why this needs no markup change — no wrapper div, no
   re-indenting four steps' worth of HTML.

   ESCAPE HATCH: add class "wf-always" to anything that must stay visible
   while its step is locked. `:not(.meta)` keeps the one-line secondary-facts
   summary (empty, so it costs no height) for the same reason the
   collapse-when-done rule above excludes it. */
[data-mode="portal"] .wf-step.wf-step-locked > *:not(.wf-step-title):not(.wf-always):not(.meta) {
  display: none;
}

/* The step 4 "Settings" fold (.wf-advanced-mode / .wf-advanced-only) was
   removed: the layer and verification-layer controls it folded away now
   live in the icon column's Layer popover (js/ui/view-tools.js), relocated
   there by js/main.js. */

/* Two status elements that read as ONE sentence. Used by step 3.1, where
   the SVF scan and the material sampling report separately but describe
   the same press of the same button — stacked, they read as two
   unrelated events and cost a line each.
   RULE: the [hidden] re-assertion is required, not belt-and-braces.
   `hidden` is a UA display:none at (0,1,0) and the `display: inline` rule
   above is an author rule of the same specificity that beats it. #wf-status-3b
   is hidden on the photo base (applyBasePresentation), and without this line
   it would show as an empty inline box that still eats the separator. */
[data-mode="portal"] .wf-status-runon { display: inline; }
[data-mode="portal"] .wf-status-runon[hidden] { display: none; }

/* A named block inside a step. Step 3 uses two for its 3.1/3.2 gate, step 4
   uses three to separate "what the model is fed" from "what is drawn" from
   the beta. Indented off a faint rule rather than boxed, so a step with
   substeps still reads as one step. */
[data-mode="portal"] .wf-substep {
  border-left: var(--border) dashed var(--line-strong);
  /* left inset var(--sp-3), not --sp-2: a checkbox is a solid bordered
     square, and a square sitting close to another line (the dashed
     border) reads as touching it far sooner than plain text does at the
     same distance — most visible in Exploded view, which is three
     checkboxes stacked right after the substep title. --sp-2 (this
     card's tight "inside one control" step) was right for text but not
     for a shape; --sp-3 ("chip and icon gaps") is the nearest step up. */
  padding: 0 0 2px var(--sp-3);
  margin: var(--sp-2) 0;
}
[data-mode="portal"] .wf-substep:last-child { margin-bottom: var(--sp-1); }
[data-mode="portal"] .wf-substep-title {
  margin: var(--sp-4) 0 2px;
  font-size: var(--fs-hint); font-weight: bold;
  letter-spacing: 0.03em; text-transform: uppercase; color: var(--text-2);
}

/* STEP 4 SPACING.
   The model-inputs block lost its .wf-substep-title, so its first child (the
   time-of-day .field) sits under the step header with only .wf-step's flex
   gap; that reads fine. What does not is the air-temperature readout — a bare
   .field-head immediately after that .field, which without this sits flush
   against the play button in the row above. */
[data-mode="portal"] #wf-step-4 .field + .field-head { margin-top: var(--sp-3); }
/* And the rule before the exploded-view block: .wf-step's own flex gap plus
   the substep's margin already separate the two, so the <hr>'s default
   .fpanel-body margin on top of that opened a gap wider than the content
   around it. Zeroed — the gap does the spacing. */
[data-mode="portal"] #wf-step-4 hr { margin: 0; }
/* Beta = implemented and usable, but its inputs or geometry are still being
   judged. Marked rather than hidden, so it is obvious in a demo which part
   is provisional. --text-3 rather than a hue of its own: spec §2 allows
   exactly three interface state colours, and this is information about the
   feature, not a state of it. */
[data-mode="portal"] .wf-beta { border-left-color: var(--text-3); }

/* .wf-substep.wf-substep-idle deliberately carries no rule: every control
   it wraps is ALSO individually :disabled at the same time and already
   has its own dashed-border signal, so a container-level fade on top
   would dim controls that are already visibly inert. The class itself
   stays — workflow-controls.js still toggles it, and it still selects the
   substep for the .wf-always escape hatch. */

/* ============================================================
   MAP-SIDE POPUP — the facade-photo hover inspector, drawn over the canvas.
   Portal-only: analytic mode does not have it.
   ============================================================ */

/* Hover inspector for the facade photos (workflow/facade-photos.js).
   Positioned in JS against the map canvas, so only appearance is here.
   pointer-events:none is required, not cosmetic: the panel follows the
   cursor, and without it the panel would swallow the mousemove that
   positions it and the whole thing would flicker. */
[data-mode="portal"] .facade-photo-tooltip {
  position: absolute; z-index: 10; pointer-events: none;
  min-width: 210px; max-width: 280px;
  padding: var(--sp-2) var(--sp-3);
  border: var(--border) solid var(--line-strong); border-radius: var(--radius);
  background: var(--overlay-bg-thin);
  -webkit-backdrop-filter: var(--overlay-blur);
          backdrop-filter: var(--overlay-blur);
  color: var(--text-1); font-size: var(--fs-hint); line-height: 1.45;
}
[data-mode="portal"] .facade-photo-tooltip .fp-title {
  margin-bottom: var(--sp-1); font-weight: 600; word-break: break-all;
}
[data-mode="portal"] .facade-photo-tooltip .fp-group {
  margin: var(--sp-2) 0 2px;
  font-size: var(--fs-eyebrow); letter-spacing: var(--ls-eyebrow);
  text-transform: uppercase; color: var(--text-3);
}
[data-mode="portal"] .facade-photo-tooltip .fp-row {
  display: flex; justify-content: space-between; gap: 10px;
}
[data-mode="portal"] .facade-photo-tooltip .fp-key { color: var(--text-3); }
/* tabular figures stop the numbers jittering sideways as the cursor moves */
[data-mode="portal"] .facade-photo-tooltip .fp-val {
  font-variant-numeric: tabular-nums; text-align: right;
}

/* ============================================================
   SITE BOUNDARY — the design vocabulary describes a DOM box with amber
   corner brackets (.site-box). NOT implemented that way here:
   workflow/site-select.js draws the boundary as MapLibre fill/line layers
   directly on the map, not as a DOM element — so a .site-box rule would
   have zero callers and sit as dead code nothing runs. If the boundary is
   ever rebuilt as DOM instead of a map layer, the intended markup is a
   positioned box with four `<i>` corner brackets (nth-child 1-4) plus a
   `.site-box-label`. */
