/* ============================================================
   css/viewer.css -- the 3D Field Volume viewer's own styles.

   ONE stylesheet for a viewer that four surfaces open: the operator app, the
   3D examples demo, the public showcase and the customer portal.

   These rules used to live in css/shell.css, which the public pages do not
   load, so api/depth-embed-core.php carried a HAND-PORT of them with every
   custom property resolved to a literal. Two copies of the same stylesheet,
   and they drifted: `.depth-viewer-overlay` was added to shell.css on
   2026-08-21 and never reached the port, so the public full-screen viewer kept
   the default modal padding. Nothing compared them. (Consolidation stage 4;
   AUDIT.md S3-5.)

   WHY EVERY COLOUR CARRIES A CHAIN. The admin shell defines --fg, --border and
   friends; the public pages define the --gl-* palette that shell.css maps those
   onto. A chain resolves under both:

       var(--fg-muted, var(--gl-text-muted, #5a5a6e))

   The literal at the end is the value the hand-port used, so a page supplying
   neither renders what the public surfaces rendered before this file existed.
   Two tokens have no public equivalent and their middle link is the nearest
   value the SHELL resolves them to: --accent falls to --gl-gold, because
   shell.css defines --gl-accent as #C9992E and --gl-gold is the same colour;
   --bg-elev falls to --gl-parchment, the nearest public surface tone. The
   --space-* scale is admin-only and falls to its literal.

   WHAT DELIBERATELY STAYED IN shell.css: the `.modal-*` base, which is generic
   furniture that share-ui, the markdown editor, the wizard, the password modal,
   four library panels and settings all use. It is not the viewer's to take.
   The public pages still port those few rules.

   `@keyframes dv-spin` DID move here, and `.gl-spinner` in shell.css still
   uses it -- the operator app loads both files, so it resolves. That is the one
   cross-file reference in this split and shell.css carries a note at the site.

   Load AFTER shell.css so these rules keep the "later wins" relationship they
   had with everything that followed them in the original file.
   ============================================================ */

/* `margin-right: auto` because .modal-head is space-between: with the logo
   added there are three children, and without it the title would be
   stranded in the middle of the bar instead of sitting beside the mark. */
.depth-viewer-head-titles { display: flex; align-items: baseline; gap: var(--space-3, 12px); min-width: 0; margin-right: auto; }

/* Previous / next through a set of tests, when the host supplies neighbours
   (2026-08-16). Sits between the title and the close button; each button names
   the test it moves TO, with the name truncating rather than pushing the head
   row wider. Disabled at the ends of the list rather than hidden, so the
   control keeps its position. */
.depth-viewer-nav-group { display: flex; align-items: center; gap: var(--space-2, 8px); flex: 0 0 auto; margin-right: var(--space-3, 12px); }

.depth-viewer-nav {
  display: inline-flex; align-items: center; gap: 4px; max-width: 190px;
  padding: 4px 10px; font-size: 12px; cursor: pointer;
  background: var(--bg-elev, var(--gl-parchment, #f7f5f0)); color: var(--fg, var(--gl-text, #1a1a2e));
  border: 1px solid var(--border, var(--gl-border, #e2ddd3)); border-radius: 999px;
}

.depth-viewer-nav:hover:not(:disabled) { border-color: var(--accent, var(--gl-gold, #c9992e)); color: var(--accent, var(--gl-gold, #c9992e)); }

.depth-viewer-nav:disabled { opacity: 0.4; cursor: default; }

.depth-viewer-nav-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.depth-viewer-nav-arrow { flex: 0 0 auto; font-size: 14px; line-height: 1; }

@media (max-width: 720px) {
  /* On a phone the names would crowd the title out; the arrows still work. */
  .depth-viewer-nav-name { display: none; }
  .depth-viewer-nav { max-width: none; padding: 4px 8px; }
}

.depth-viewer-head-logo {
  flex: 0 0 auto; height: 26px; width: auto; display: block;
  margin-right: var(--space-2, 8px);
}

.depth-viewer-id { font-size: 13px; color: var(--fg-muted, var(--gl-text-muted, #5a5a6e)); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* Full-screen and using dvh so the modal never exceeds the visible viewport on
   iPad / iOS Safari, where the dynamic browser chrome makes vh taller than the
   visible area and pushes the bottom controls off-screen (user direction
   2026-06-26). The overlay drops its padding so the card goes edge to edge. */
.depth-viewer-overlay { padding: 0; align-items: stretch; }

.depth-viewer-modal {
  max-width: 100vw; width: 100vw;
  height: 100vh; max-height: 100vh;
  height: 100dvh; max-height: 100dvh;
  border: none; border-radius: 0;
}

.depth-viewer-toolbar {
  display: flex; align-items: center; gap: var(--space-3, 12px);
  flex-wrap: wrap; padding: var(--space-2, 8px) var(--space-4, 16px);
  border-bottom: 1px solid var(--border, var(--gl-border, #e2ddd3));
}

.depth-viewer-picker { display: flex; align-items: center; gap: var(--space-2, 8px); }

.depth-viewer-picker span { font-size: 12px; color: var(--fg-muted, var(--gl-text-muted, #5a5a6e)); }

.depth-viewer-speed { display: flex; align-items: center; gap: var(--space-2, 8px); font-size: 12px; color: var(--fg-muted, var(--gl-text-muted, #5a5a6e)); }

.depth-viewer-speed input[type="range"] { width: 96px; }

.depth-viewer-speed-val { min-width: 4.2em; font-variant-numeric: tabular-nums; color: var(--fg, var(--gl-text, #1a1a2e)); }

.depth-viewer-fmt { display: flex; align-items: center; gap: var(--space-1, 4px); font-size: 12px; color: var(--fg-muted, var(--gl-text-muted, #5a5a6e)); }

.depth-viewer-fmt select { font-size: 12px; }

.depth-viewer-hint { font-size: 12px; color: var(--fg-muted, var(--gl-text-muted, #5a5a6e)); margin-left: auto; }

.depth-viewer-smooth { display: flex; align-items: center; gap: var(--space-1, 4px); font-size: 12px; color: var(--fg-muted, var(--gl-text-muted, #5a5a6e)); white-space: nowrap; cursor: pointer; }

.depth-viewer-smooth input[type="checkbox"] { cursor: pointer; }

.depth-viewer-ring-note {
  margin: 8px 0 0; padding: 0;
  font-size: 11px; line-height: 1.5; color: var(--fg-muted, var(--gl-text-muted, #5a5a6e));
  background: none; border: none;
}

.depth-viewer-ring-note strong { color: var(--gl-navy); }

.depth-viewer-ring-note[hidden] { display: none; }

/* The Pulse inset is docked under the Layers panel (dockPulse moves it into
   .depth-viewer-pulsebox at the bottom of the legend), so it fills the column
   width as a static block instead of floating over the 3D canvas. */
.depth-viewer-pulsebox {
  margin-top: var(--space-3, 12px); padding-top: var(--space-3, 12px);
  border-top: 1px solid var(--border, var(--gl-border, #e2ddd3));
}

.depth-viewer-pulsebox[hidden] { display: none; }

/* The scrub bar docked under the pulse waveform (user direction 2026-08-10,
   "a scrub bar under the waveform ... synchronized with the waveform" +
   "the line on the pulser to line up with the scroll bar"): the track spans
   the full inset width, the same width as the waveform canvas (user
   direction 2026-08-11, "make the scrub control the same width as the
   waveform"; supersedes the earlier plot-area margins 14.7%/2.9%, which
   also mis-sat on insets without a Gauss label column). The word "Scrub"
   is dropped in the docked form (font-size 0; the input keeps its
   aria-label). */
.depth-viewer-scrub-docked { display: block; margin: 2px 0 0; font-size: 0; }

.depth-viewer-scrub-docked input[type="range"] { width: 100%; margin: 0; min-width: 0; cursor: pointer; }

/* Collapse the whole block (title + separator) for a setting with no pulse,
   where drawWave hides the canvas. */
.depth-viewer-pulsebox:has(.depth-viewer-wave[hidden]) { display: none; }

.depth-viewer-wave {
  display: block; width: 100%; height: auto;
  border: 1px solid var(--border, var(--gl-border, #e2ddd3)); border-radius: 4px;
  background: rgba(255, 255, 255, 0.92);
}

.depth-viewer-wave[hidden] { display: none; }

.depth-viewer-stage {
  flex: 1 1 auto; min-height: 0; position: relative;
  display: flex; align-items: stretch;
}

.depth-viewer-views {
  position: absolute; left: 10px; bottom: 10px; z-index: 3;
  display: flex; flex-wrap: wrap; gap: 4px; max-width: 60%;
  background: color-mix(in srgb, var(--bg-elev, var(--gl-parchment, #f7f5f0)) 78%, transparent);
  padding: 4px; border: 1px solid var(--border, var(--gl-border, #e2ddd3)); border-radius: 4px;
}

.depth-viewer-views .btn { padding: 2px 8px; font-size: 11px; }

/* 3D Analysis control colors (user direction 2026-08-11, "add some color to
   the buttons to make them standout more"): transport in brand primary,
   saves + sweep in the deep-teal field accent, white text on both. */
.depth-viewer-btn-play { background: #1f4e79; border-color: #1f4e79; color: #fff; }

.depth-viewer-btn-play:hover { background: #2e75b6; border-color: #2e75b6; color: #fff; }

.depth-viewer-btn-save, .depth-viewer-slice-sweep { background: #0c6e96; border-color: #0c6e96; color: #fff; }

.depth-viewer-btn-save:hover, .depth-viewer-slice-sweep:hover { background: #0d81b0; border-color: #0d81b0; color: #fff; }

.depth-viewer-btn-glyph { font-size: 14px; line-height: 1; min-width: 34px; }

/* Animation controls dock under the pulse waveform as two compact rows. */
.depth-viewer-animctl { display: inline-flex; gap: 8px; align-items: center; flex-wrap: wrap; }

.depth-viewer-animctl-row { display: inline-flex; gap: 8px; align-items: center; }

.depth-viewer-animctl-docked { display: flex; flex-direction: column; align-items: stretch; gap: 6px; margin-top: 6px; }

.depth-viewer-animctl-docked .depth-viewer-animctl-row { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }

.depth-viewer-animctl-docked .depth-viewer-speed input[type=range] { width: 90px; }

.depth-viewer-animctl-docked .depth-viewer-fmt select { max-width: 130px; }

/* Collapsible legend folds (Layers / Explore level, shut by default). */
.depth-viewer-fold { margin: 0 0 8px; }

.depth-viewer-fold > summary { cursor: pointer; list-style: none; }

.depth-viewer-fold > summary::before { content: '\25B8'; display: inline-block; margin-right: 5px; transition: transform 0.15s; }

.depth-viewer-fold[open] > summary::before { transform: rotate(90deg); }

.depth-viewer-fold > summary::-webkit-details-marker { display: none; }

/* Fold headers carry the same gold bar as the explainer accordion, so the
   collapsed Layers / Explore level controls are obvious (user direction
   2026-08-12, "highlight those accordions too, so that they are more
   obvious"). Scoped to fold SUMMARIES: plain legend titles (Cross-section,
   the Pulse block) keep the muted caption look. */
.depth-viewer-fold > summary.depth-viewer-legend-title {
  background: #d0a840; color: #14181f;
  font-weight: 700; padding: 6px 10px;
  border-radius: 6px; margin-bottom: 6px;
}

/* The active view's explainer: an accordion at the top of the right
   sidebar (user direction 2026-08-12, "at the top right of the right
   sidebar as an accordion ... a more noticeable color"; supersedes the
   2026-08-11 always-visible stage overlay, which covered working
   controls). Closed by default; the gold summary bar keeps it noticed. */
.depth-viewer-helptip {
  flex: 0 0 auto;
  margin: 8px 8px 0;
  border: 1px solid #b8922f;
  border-radius: 6px;
  overflow: hidden;
  background: var(--bg-elev, var(--gl-parchment, #f7f5f0));
}

.depth-viewer-helptip[hidden] { display: none; }

.depth-viewer-helptip-head {
  background: #d0a840; color: #14181f;
  font-size: 12px; font-weight: 700;
  padding: 7px 10px; cursor: pointer;
}

.depth-viewer-helptip-pop {
  max-height: 32vh; overflow-y: auto;
  padding: 8px 10px; font-size: 12px; line-height: 1.5;
  color: var(--fg-muted, var(--gl-text-muted, #5a5a6e));
}

.depth-viewer-cube {
  position: absolute; top: 42px; left: 10px; z-index: 3;
  width: 100px; height: 100px; cursor: pointer; opacity: 0.85;
}

.depth-viewer-cube:hover { opacity: 1; }

/* Home ABOVE the cube, left-aligned with it (user report 2026-08-16, "The home
   button and cube overlap"), keeping the top-left placement asked for on
   2026-08-16 ("I want the home button in the top left of the cube, not bottom
   left") - which is where Fusion 360 and the other CAD packages put it, so it
   needs no label.

   It USED to overlap the cube's canvas, on the claim that the drawn cube is
   inset well inside it and leaves the corner empty. That claim was wrong.
   Projecting the cube's eight corners through the camera the viewer actually
   builds - PerspectiveCamera(28, 1) at z=6 over a Box(1.6) - the silhouette
   reaches canvas x=19.1 face-on, x=9.0 at a 3/4 corner, and x=2.4 at the worst
   angle in a full sweep. Against a house covering 6..34 px that is a 5 px
   overlap in the person's own opening view and 15 px at any orbited corner, so
   there is no safe spot INSIDE the cube's box and the two have to be separated.
   Stacked rather than side-by-side because the cube keeps its left margin that
   way and only vertical space is spent.

   Above the cube in z as well, so the house always takes the click. Camera
   only: the accessory does not move. */
.depth-viewer-home {
  position: absolute; top: 8px; left: 10px; z-index: 4;
  display: flex; align-items: center; justify-content: center;
  width: 28px; height: 28px; padding: 0;
  border: 1px solid var(--border, var(--gl-border, #e2ddd3)); border-radius: 5px;
  background: color-mix(in srgb, var(--bg-elev, var(--gl-parchment, #f7f5f0)) 88%, transparent);
  color: var(--fg-muted, var(--gl-text-muted, #5a5a6e)); cursor: pointer; opacity: 0.85;
}

.depth-viewer-home:hover { color: var(--accent, var(--gl-gold, #c9992e)); border-color: var(--accent, var(--gl-gold, #c9992e)); opacity: 1; }

.depth-viewer-home:active { background: var(--bg-elev, var(--gl-parchment, #f7f5f0)); }

.depth-viewer-home svg { width: 17px; height: 17px; display: block; }

/* Navigation pad: pan arrows + zoom, directly under the ViewCube. Between
   them every camera move has an on-screen control, which is what frees the
   canvas drag to move the accessory instead (user direction 2026-08-15). */
.depth-viewer-navpad {
  position: absolute; top: 150px; left: 10px; z-index: 3;
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  opacity: 0.8;
}

.depth-viewer-navpad:hover { opacity: 1; }

.depth-viewer-navpad .dv-nav-grid {
  display: grid; grid-template-columns: repeat(3, 30px); grid-auto-rows: 30px; gap: 2px;
}

.depth-viewer-navpad .dv-nav-zoom { display: flex; gap: 2px; }

.depth-viewer-navpad .dv-nav-btn {
  display: flex; align-items: center; justify-content: center;
  width: 30px; height: 30px; padding: 0;
  border: 1px solid var(--border, var(--gl-border, #e2ddd3)); border-radius: 5px;
  background: color-mix(in srgb, var(--bg-elev, var(--gl-parchment, #f7f5f0)) 88%, transparent);
  color: var(--fg-muted, var(--gl-text-muted, #5a5a6e)); cursor: pointer;
}

.depth-viewer-navpad .dv-nav-btn:hover { color: var(--accent, var(--gl-gold, #c9992e)); border-color: var(--accent, var(--gl-gold, #c9992e)); }

.depth-viewer-navpad .dv-nav-btn:active { background: var(--bg-elev, var(--gl-parchment, #f7f5f0)); }

.depth-viewer-navpad .dv-nav-btn svg { width: 15px; height: 15px; display: block; }

/* Save spinner: the Gauss Labs spirograph, spinning, over a dimmed stage. */
.depth-viewer-saving {
  position: absolute; inset: 0; z-index: 5;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 12px;
  background: color-mix(in srgb, var(--bg-elev, var(--gl-parchment, #f7f5f0)) 70%, transparent);
}

.depth-viewer-saving[hidden] { display: none; }

.depth-viewer-saving .dv-spiro-svg {
  /* Gauss Labs logo gold (user direction 2026-06-26). */
  width: 96px; height: 96px; color: #d0a840;
  animation: dv-spin 2.4s linear infinite;
}

.depth-viewer-saving-text { font-size: 13px; color: var(--fg, var(--gl-text, #1a1a2e)); font-weight: 600; }

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

.depth-viewer-body {
  flex: 1 1 auto; min-width: 0; min-height: 0; position: relative;
  display: flex; align-items: stretch; justify-content: center;
  background: #ffffff; overflow: hidden;
}

.depth-viewer-body canvas { display: block; width: 100%; height: 100%; }

/* Right sidebar: the explainer accordion on top, the legend scrolling
   beneath it. The wrapper carries the column layout the legend used to
   own. */
.depth-viewer-side {
  flex: 0 0 auto; width: 230px; min-height: 0;
  display: flex; flex-direction: column;
  border-left: 1px solid var(--border, var(--gl-border, #e2ddd3));
  background: var(--bg-elev, var(--gl-parchment, #f7f5f0));
}

.depth-viewer-legend {
  flex: 1 1 auto; min-height: 0; overflow-y: auto;
  padding: var(--space-3, 12px) var(--space-4, 16px);
}

.depth-viewer-legend-title {
  font-size: 11px; text-transform: uppercase; letter-spacing: 0.04em;
  color: var(--fg-muted, var(--gl-text-muted, #5a5a6e)); margin-bottom: var(--space-2, 8px);
}

.depth-viewer-legend-row {
  display: flex; align-items: center; gap: var(--space-2, 8px);
  padding: 4px 0; cursor: pointer; font-size: 12px; color: var(--fg, var(--gl-text, #1a1a2e));
}

.depth-viewer-legend-row input { margin: 0; flex: 0 0 auto; cursor: pointer; }

/* A field-shell row stacks its on/off toggle over its own opacity slider. */
.depth-viewer-legend-row--slider {
  flex-direction: column; align-items: stretch; gap: 4px; cursor: default;
}

.depth-viewer-legend-toggle {
  display: flex; align-items: center; gap: var(--space-2, 8px); cursor: pointer;
}

.depth-viewer-shell-op { width: 100%; cursor: pointer; }

/* The Full field mirror row: label + a three-state picker (On / Off / Auto)
   rather than a checkbox, so it needs the select sized like a control in a
   12px row rather than inheriting the browser default. The resolved-state
   note sits with the label and is muted, because it reports rather than
   labels. Mirrored in the embed stylesheet (api/depth-embed-core.php). */
.depth-viewer-legend-row--mode { cursor: default; }

.depth-viewer-mirror-mode {
  flex: 0 0 auto; font: inherit; font-size: 12px; padding: 1px 4px;
  background: var(--bg-elev, var(--gl-parchment, #f7f5f0)); color: var(--fg, var(--gl-text, #1a1a2e));
  border: 1px solid var(--border, var(--gl-border, #e2ddd3)); border-radius: 4px; cursor: pointer;
}

.depth-viewer-mirror-state { color: var(--fg-muted, var(--gl-text-muted, #5a5a6e)); }

.depth-viewer-swatch {
  flex: 0 0 auto; width: 14px; height: 14px; border-radius: 3px;
  border: 1px solid rgba(0,0,0,0.25);
}

.depth-viewer-legend-label { flex: 1 1 auto; }

/* Movable "explore" shell control: a percent slider + live Gauss readout the
   operator sweeps from 1% to 100% of the displayed setting's peak. Set off with
   a top border so it reads as a distinct control below the fixed-shell layers. */
.depth-viewer-explore {
  display: flex; flex-direction: column; align-items: stretch; gap: 4px;
  margin-top: var(--space-2, 8px); padding-top: var(--space-2, 8px);
  border-top: 1px solid var(--border, var(--gl-border, #e2ddd3));
}

/* Explore level's heading now that it lives INSIDE the Layers fold rather
   than in an accordion of its own (user direction 2026-08-16, "Explore level
   can just be added to layers for simplification"). It still needs a name -
   the controls below it are a level sweep, not another shell toggle - but it
   is a sub-heading within a fold, so it is quieter than the fold's own gold
   summary bar. The rule that follows comes from .depth-viewer-explore's own
   border-top, so the heading sits directly above the divider. */
.depth-viewer-explore-head {
  font-size: 12px; font-weight: 700; color: var(--fg-muted, var(--gl-text-muted, #5a5a6e));
  margin-top: var(--space-2, 8px);
}

.depth-viewer-explore-slider { width: 100%; cursor: pointer; }

.depth-viewer-explore-readout {
  font-size: 12px; font-weight: 600; color: var(--fg, var(--gl-text, #1a1a2e));
}

.depth-viewer-legend-note {
  margin-top: var(--space-3, 12px); font-size: 11px; line-height: 1.5;
  color: var(--fg-muted, var(--gl-text-muted, #5a5a6e));
}

/* Cross-section analysis view: while a cut is active the legend drops the
   layer toggles, opacity sliders, Explore control, and the dome note (the
   scene hides those layers too) and shows the cut's own note instead, so the
   cross-section reads as its own analysis view (user direction 2026-08-10). */
.depth-viewer-slice-note {
  display: none; margin: 2px 0 0; font-size: 11px; line-height: 1.5;
  color: var(--fg-muted, var(--gl-text-muted, #5a5a6e));
}

.depth-viewer-slice-view .depth-viewer-legend-row,
.depth-viewer-slice-view .depth-viewer-explore:not(.depth-viewer-slice),
.depth-viewer-slice-view .depth-viewer-fold,
.depth-viewer-slice-view .depth-viewer-legend-note { display: none; }

.depth-viewer-slice-view .depth-viewer-slice-note { display: block; }

.depth-viewer-slice-view .depth-viewer-slice { border-top: none; margin-top: 0; padding-top: 0; }

/* Angle row for the vertical cut (rotates the cut plane). */
.depth-viewer-slice-angle { display: flex; align-items: center; gap: 8px; font-size: 12px; color: var(--fg-muted, var(--gl-text-muted, #5a5a6e)); }

.depth-viewer-slice-angle[hidden] { display: none; }

.depth-viewer-slice-angle input[type="range"] { flex: 1 1 auto; width: auto; min-width: 0; }

.depth-viewer-slice-angleval { min-width: 2.8em; text-align: right; font-variant-numeric: tabular-nums; color: var(--fg, var(--gl-text, #1a1a2e)); }

/* Person shade slider in the viewer toolbar. */
.depth-viewer-person-shade input[type="range"] { width: 90px; cursor: pointer; }

.depth-viewer-msg { padding: var(--space-4, 16px); color: var(--fg-muted, var(--gl-text-muted, #5a5a6e)); }

@media (max-width: 600px) {
  .depth-viewer-hint { margin-left: 0; }
  .depth-viewer-stage { flex-direction: column; }
  .depth-viewer-side {
    width: auto; border-left: none; border-top: 1px solid var(--border, var(--gl-border, #e2ddd3));
    max-height: 38%;
  }
}
