/* ============================================================================
   Dr. Ijezie's STEM Studio — Visualization Shell
   ----------------------------------------------------------------------------
   The shared lab chrome for every runtime-built visualization.

   SCOPING NOTE — read before editing.
   Every rule here is scoped under `.viz-shell`, which only the runtime applies.
   The six ESS module pages and twelve other module pages still define their own
   `.lab-shell` / `exp-*` rules in a per-page <style> block, and the six ESS viz
   files still inject `exp-*` CSS from JS string literals. Scoping under
   `.viz-shell` means this file can ship before those are migrated without
   changing a single pixel on a page that has not moved to the runtime yet.
   As each module migrates, its inline copy is deleted and this file takes over.

   Colour comes entirely from CSS custom properties resolved on the element, so
   a lab inherits the per-course accent (.course-alg1 / .course-ess / …) and the
   light-band / dark-well palette automatically. Do not hardcode a hex here.
   ============================================================================ */

/* ---- shell ------------------------------------------------------------- */

.viz-shell {
  --viz-gap: clamp(0.6rem, 1.4vw, 1rem);
  --viz-radius: var(--ah-r-3, 14px);
  --viz-stage-min: 320px;
  /* The stage grows with the viewport but is capped so a lab never pushes the
     rest of the module off the first screen on a large monitor. */
  --viz-stage-h: clamp(340px, 52vh, 620px);

  display: block;
  container-type: inline-size;
  /* Belt and braces against global horizontal overflow: nothing inside a lab
     may widen its column, whatever a scene tries to render. */
  max-width: 100%;
  min-width: 0;
  overflow: clip;
}

.viz-shell .exp-well {
  display: flex;
  flex-direction: column;
  gap: var(--viz-gap);
  padding: var(--viz-gap);
  border: 1px solid var(--line);
  border-radius: var(--viz-radius);
  background: var(--surface);
  box-shadow: var(--arch-shadow-sm, 0 1px 2px rgb(0 0 0 / 0.2));
  min-width: 0;
}

/* ---- scene rail (tabs) -------------------------------------------------- */

.viz-shell .viz-rail {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  min-width: 0;
}

.viz-shell .viz-tab {
  /* 44px is the minimum comfortable touch target; these labs are used on
     Chromebooks with touchscreens and on phones. */
  min-height: 44px;
  padding: 0.5rem 0.85rem;
  border: 1px solid var(--line);
  border-radius: var(--ah-r-pill, 999px);
  background: transparent;
  color: var(--ink-2);
  font-family: var(--font-heading, system-ui, sans-serif);
  font-size: clamp(0.78rem, 1.6cqi, 0.9rem);
  line-height: 1.2;
  cursor: pointer;
  transition: background var(--ah-dur-2, 160ms) var(--ah-ease-standard, ease),
              color var(--ah-dur-2, 160ms) var(--ah-ease-standard, ease),
              border-color var(--ah-dur-2, 160ms) var(--ah-ease-standard, ease);
}

.viz-shell .viz-tab:hover { color: var(--ink); border-color: var(--line-strong); }

.viz-shell .viz-tab[aria-selected="true"] {
  color: var(--ink);
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 16%, transparent);
  /* Selection is never signalled by colour alone — the weight change and the
     inset marker carry it too, for colour-vision-deficient and greyscale use. */
  font-weight: 600;
  box-shadow: inset 0 -2px 0 0 var(--accent);
}

/* ---- narration + console + readout -------------------------------------- */

.viz-shell .viz-narrate {
  color: var(--ink-2);
  font-family: var(--font-body, system-ui, sans-serif);
  font-size: clamp(0.85rem, 1.9cqi, 0.98rem);
  line-height: var(--ah-lh-body, 1.55);
  /* Prose stays readable even when the stage is allowed to run wide. */
  max-width: 68ch;
}

.viz-shell .viz-narrate:empty { display: none; }

.viz-shell .viz-console {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(220px, 100%), 1fr));
  gap: var(--viz-gap);
  align-items: start;
  min-width: 0;
}

.viz-shell .viz-console:empty { display: none; }

/* When a scene returns its controls as an HTML string the runtime caches them
   in a wrapper element. display:contents keeps the individual controls as
   direct grid items, so caching does not change the layout. */
.viz-shell .viz-controls-group { display: contents; }

.viz-shell .viz-readout {
  color: var(--ink-2);
  font-family: var(--font-body, system-ui, sans-serif);
  font-size: clamp(0.82rem, 1.7cqi, 0.94rem);
  line-height: var(--ah-lh-body, 1.55);
  min-width: 0;
}

.viz-shell .viz-readout:empty { display: none; }

/* Numeric readouts are tabular so digits do not jitter as values change —
   a shifting number is genuinely harder to read while dragging a slider. */
.viz-shell .viz-num {
  font-family: var(--font-mono, ui-monospace, monospace);
  font-variant-numeric: tabular-nums;
  color: var(--ink);
}

/* ---- controls ----------------------------------------------------------- */

.viz-shell .viz-control {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  min-width: 0;
}

.viz-shell .viz-control > label {
  color: var(--muted);
  font-family: var(--font-mono, ui-monospace, monospace);
  font-size: 0.72rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.viz-shell input[type="range"] {
  width: 100%;
  min-height: 44px;   /* touch target, not visual height */
  accent-color: var(--accent);
  background: transparent;
}

.viz-shell .viz-btn {
  min-height: 44px;
  padding: 0.5rem 0.9rem;
  border: 1px solid var(--line-strong);
  border-radius: var(--ah-r-2, 10px);
  background: var(--surface-2);
  color: var(--ink);
  font-family: var(--font-heading, system-ui, sans-serif);
  font-size: 0.85rem;
  cursor: pointer;
}

.viz-shell .viz-btn:hover { border-color: var(--accent); }

.viz-shell .viz-btn[aria-pressed="true"] {
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 20%, var(--surface-2));
  font-weight: 600;
}

/* ---- stage -------------------------------------------------------------- */

.viz-shell .viz-stage {
  position: relative;
  width: 100%;
  min-width: 0;
  height: var(--viz-stage-h);
  min-height: var(--viz-stage-min);
  border: 1px solid var(--line);
  border-radius: var(--ah-r-2, 10px);
  background: var(--recessed);
  overflow: hidden;
  /* Touch: let the browser scroll the page vertically, but give horizontal
     drag to the scene so orbiting a 3D model does not fight the page. */
  touch-action: pan-y;
}

/* One layer per scene; only the active one is not [hidden]. */
.viz-shell .viz-layer {
  position: absolute;
  inset: 0;
  display: flex;
  gap: 1px;
  background: var(--line);   /* the 1px gap reads as a divider between panes */
  min-width: 0;
}

.viz-shell .viz-layer[hidden] { display: none; }

/* A pane is one drawing surface: one canvas, one context type.
   See the surface-model note in viz-runtime.js — a canvas cannot be both
   2D and WebGL, so a linked view needs two panes, not one canvas. */
.viz-shell .viz-pane {
  position: relative;
  flex: 1 1 0;
  min-width: 0;
  min-height: 0;
  background: var(--recessed);
}

/* Side-by-side: 3-D explanation beside the exact 2-D evidence. */
.viz-shell .viz-layer[data-layout="split"] { flex-direction: row; }
.viz-shell .viz-layer[data-layout="stack"] { flex-direction: column; }

/* On a narrow container the 2-D evidence layer must NOT be squeezed into a
   sliver — it is the authoritative artifact. Stack instead. */
@container (max-width: 720px) {
  .viz-shell .viz-layer[data-layout="split"] { flex-direction: column; }
}

.viz-shell .viz-canvas {
  display: block;
  width: 100%;
  height: 100%;
  /* A canvas is focusable in these labs (keyboard camera + object selection),
     so it needs a real focus ring. */
  outline-offset: -3px;
}

.viz-shell .viz-overlay {
  position: absolute;
  inset: 0;
  pointer-events: none;
}

.viz-shell .viz-labels { position: absolute; inset: 0; }

/* CSS2D labels: real DOM, so they stay crisp at any DPR and are selectable. */
.viz-shell .viz-label {
  padding: 0.15rem 0.4rem;
  border: 1px solid var(--line-strong);
  border-radius: var(--ah-r-1, 6px);
  background: color-mix(in srgb, var(--surface) 86%, transparent);
  color: var(--ink);
  font-family: var(--font-mono, ui-monospace, monospace);
  font-size: 0.7rem;
  white-space: nowrap;
  transform: translate(-50%, -50%);
}

/* ---- loading + fallback ------------------------------------------------- */

.viz-shell[data-loading] .viz-stage::after {
  content: "Loading 3-D view…";
  position: absolute;
  inset: auto 0 0.6rem 0;
  text-align: center;
  color: var(--muted);
  font-family: var(--font-mono, ui-monospace, monospace);
  font-size: 0.72rem;
}

.viz-shell .viz-fallback {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  justify-content: center;
  align-items: center;
  height: 100%;
  padding: 1.5rem 1rem;
  text-align: center;
}

.viz-shell .viz-fallback-title {
  color: var(--ink);
  font-family: var(--font-heading, system-ui, sans-serif);
  font-size: 0.95rem;
  margin: 0;
}

.viz-shell .viz-fallback-body {
  color: var(--muted);
  font-family: var(--font-body, system-ui, sans-serif);
  font-size: 0.85rem;
  line-height: var(--ah-lh-body, 1.55);
  max-width: 52ch;
  margin: 0;
}

/* ---- text alternative --------------------------------------------------- */

.viz-shell .viz-alt {
  border-top: 1px solid var(--line);
  padding-top: 0.6rem;
}

.viz-shell .viz-alt > summary {
  min-height: 44px;
  display: flex;
  align-items: center;
  color: var(--muted);
  font-family: var(--font-mono, ui-monospace, monospace);
  font-size: 0.74rem;
  letter-spacing: 0.05em;
  cursor: pointer;
}

.viz-shell .viz-alt-text {
  color: var(--ink-2);
  font-family: var(--font-body, system-ui, sans-serif);
  font-size: 0.88rem;
  line-height: var(--ah-lh-body, 1.55);
  max-width: 72ch;
}

/* Wide data tables scroll inside their own box. The page body must never
   gain a horizontal scrollbar because of a lab. */
.viz-shell .viz-alt-scroll {
  max-width: 100%;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}

.viz-shell .viz-alt-table {
  border-collapse: collapse;
  font-family: var(--font-mono, ui-monospace, monospace);
  font-size: 0.76rem;
  color: var(--ink-2);
}

.viz-shell .viz-alt-table caption {
  text-align: left;
  padding-bottom: 0.35rem;
  color: var(--muted);
  font-size: 0.72rem;
}

.viz-shell .viz-alt-table th,
.viz-shell .viz-alt-table td {
  border: 1px solid var(--line);
  padding: 0.28rem 0.5rem;
  text-align: right;
  white-space: nowrap;
}

.viz-shell .viz-alt-table th { color: var(--ink); text-align: left; }

/* ---- focus + screen-reader utility -------------------------------------- */

.viz-shell :focus-visible {
  outline: 2px solid var(--accent-bright, var(--accent));
  outline-offset: 2px;
  border-radius: var(--ah-r-1, 6px);
}

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

/* ---- responsive --------------------------------------------------------- */

/* Container queries, so a lab reflows to the space it actually has rather
   than to the viewport — correct inside a narrow column on a wide screen. */
@container (max-width: 560px) {
  .viz-shell { --viz-stage-h: clamp(280px, 62vh, 420px); --viz-stage-min: 260px; }
  .viz-shell .viz-console { grid-template-columns: 1fr; }
  .viz-shell .viz-tab { flex: 1 1 auto; justify-content: center; }
}

/* 320px is the narrowest target. Keep the stage usable rather than square. */
@media (max-width: 360px) {
  .viz-shell { --viz-gap: 0.5rem; --viz-stage-h: 260px; --viz-stage-min: 240px; }
  .viz-shell .viz-tab { padding: 0.5rem 0.6rem; font-size: 0.75rem; }
}

/* Give the stage real desktop width — the whole point of an immersive lab —
   while prose above keeps its readable measure. */
@media (min-width: 1280px) {
  .viz-shell { --viz-stage-h: clamp(420px, 56vh, 680px); }
}

/* Short landscape phones: a 52vh stage leaves nothing for the controls. */
@media (max-height: 520px) and (orientation: landscape) {
  .viz-shell { --viz-stage-h: 240px; --viz-stage-min: 200px; }
}

/* ---- reduced motion ----------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  .viz-shell *,
  .viz-shell *::before,
  .viz-shell *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
}

/* ---- forced colours ----------------------------------------------------- */

@media (forced-colors: active) {
  .viz-shell .exp-well,
  .viz-shell .viz-stage,
  .viz-shell .viz-tab { border-color: CanvasText; }
  .viz-shell .viz-tab[aria-selected="true"] { box-shadow: inset 0 -2px 0 0 Highlight; }
}
