/* Slide labs — report-scoped styles, shared by every imported lab.
 *
 * Every rule is rooted at .slab or at the body class these reports set, so
 * nothing here can reach the application shell, another report, or the generic
 * presentation. Colour is not written here: each study's reviewed palette
 * arrives as --slab-* custom properties on the container, so a new film needs a
 * registry entry and no new stylesheet. */

.slab {
  --slab-serif: Iowan Old Style, Palatino Linotype, Book Antiqua, Palatino, Georgia, serif;
  --slab-sans: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  --slab-mono: "IBM Plex Mono", "SFMono-Regular", Consolas, "Liberation Mono", monospace;

  display: flex;
  min-height: 100vh;
  min-height: 100dvh;
  flex-direction: column;
  color: var(--slab-cream);
  background:
    radial-gradient(circle at 50% 42%, var(--slab-glow), transparent 38%),
    var(--slab-black);
  font-family: var(--slab-sans);
}

/* The lab owns the whole viewport on its own page. */
body.slab-shell {
  overflow-x: hidden;
  background: #000;
}

body.slab-scope-open {
  overflow: hidden;
}

.slab *,
.slab *::before,
.slab *::after { box-sizing: border-box; }

.slab button { font: inherit; }
.slab img { display: block; }

.slab-topbar {
  display: grid;
  min-height: 78px;
  padding: 12px 22px;
  grid-template-columns: minmax(250px, 1fr) auto minmax(110px, 1fr);
  gap: 24px;
  align-items: center;
  background: color-mix(in srgb, var(--slab-black) 93%, transparent);
  border-bottom: 1px solid var(--slab-line);
}

.slab-identity { display: flex; gap: 12px; align-items: center; }
.slab-mark,
.slab-key {
  display: inline-grid;
  place-items: center;
  color: #fff7e8;
  background: var(--slab-orange);
  font-family: var(--slab-mono);
  font-weight: 800;
}
.slab-mark { width: 36px; height: 36px; font-size: 10px; }
.slab-identity div { display: grid; line-height: 1.1; }
.slab-identity strong { font-family: var(--slab-serif); font-size: 17px; font-weight: 600; }
.slab-identity span { margin-top: 5px; color: var(--slab-muted); font-family: var(--slab-mono); font-size: 8px; letter-spacing: .08em; text-transform: uppercase; }

.slab-instructions { display: flex; gap: 22px; color: var(--slab-muted); font-family: var(--slab-mono); font-size: 9px; letter-spacing: .08em; text-transform: uppercase; }
.slab-instructions b { color: var(--slab-cream); font-weight: 700; }
.slab-counter { justify-self: end; color: var(--slab-muted); font-family: var(--slab-mono); font-size: 13px; }
.slab-counter span:first-child { color: var(--slab-cream); font-size: 22px; }
.slab-counter i { margin: 0 7px; font-style: normal; }

.slab-workspace {
  display: grid;
  flex: 1;
  min-height: 0;
  padding: 14px;
  grid-template-columns: minmax(235px, .72fr) minmax(470px, 2.25fr) minmax(235px, .72fr);
  gap: 14px;
  align-items: stretch;
}

.slab-rail { display: grid; min-width: 0; grid-template-rows: repeat(3, minmax(176px, 1fr)); gap: 14px; }
.slab-panel {
  min-width: 0;
  min-height: 0;
  margin: 0;
  overflow: hidden;
  background: linear-gradient(160deg, rgba(255, 255, 255, .02), transparent 48%), var(--slab-panel);
  border: 1px solid var(--slab-line);
  transform-origin: top left;
  transition: border-color 180ms ease, box-shadow 180ms ease, transform 180ms ease;
  will-change: transform;
}
.slab-panel:nth-child(1) { grid-row: 1; }
.slab-panel:nth-child(2) { grid-row: 2; }
.slab-panel:nth-child(3) { grid-row: 3; }
.slab-panel:focus-visible { outline: 2px solid var(--slab-gold); outline-offset: 2px; }
.slab-panel:hover { border-color: color-mix(in srgb, var(--slab-gold) 45%, transparent); }
.slab-panel figcaption {
  display: grid;
  min-height: 55px;
  margin: 0;
  padding: 9px 10px;
  grid-template-columns: 31px minmax(0, 1fr) auto;
  gap: 10px;
  align-items: center;
  border-bottom: 1px solid var(--slab-line);
}
.slab-key { width: 31px; height: 31px; font-size: 10px; }
.slab-panel figcaption div { display: grid; min-width: 0; line-height: 1.05; }
.slab-panel figcaption strong { overflow: hidden; font-family: var(--slab-serif); font-size: 15px; font-weight: 600; text-overflow: ellipsis; white-space: nowrap; }
.slab-panel figcaption small,
.slab-baseline { color: var(--slab-muted); font-family: var(--slab-mono); font-size: 7px; letter-spacing: .08em; text-transform: uppercase; }
.slab-panel figcaption small { margin-top: 5px; }
.slab-baseline { text-align: right; }
.slab-chart { width: 100%; height: calc(100% - 55px); min-height: 190px; }
.slab-chart svg { display: block; width: 100%; height: 100%; }

.slab-stage {
  display: grid;
  min-width: 0;
  background: var(--slab-stage);
  border: 1px solid var(--slab-line);
  grid-template-rows: minmax(380px, 1fr) auto;
}

/* The media row is a size container so the well can hold the frame's exact
   aperture — the largest box of that ratio that fits — instead of letterboxing
   a fixed rectangle. Both constraints are needed: one alone lets the other axis
   overflow. */
.slab-media {
  display: grid;
  min-height: 0;
  padding: 10px;
  place-items: center;
  container-type: size;
  transform-origin: top left;
  will-change: transform;
}
.slab-well {
  --slab-ar: 16 / 9;
  position: relative;
  width: min(100cqw, calc(100cqh * (var(--slab-ar))));
  height: min(100cqh, calc(100cqw / (var(--slab-ar))));
  overflow: hidden;
  background: #000;
}
.slab-well::after {
  position: absolute;
  z-index: 3;
  inset: 0;
  pointer-events: none;
  border: 1px solid rgba(255, 255, 255, .06);
  content: "";
}

/* Two stacked layers so a frame change is a real crossfade rather than a
   src swap. The imported bytes are the evidence: no filter, no blend mode. */
.slab-frame {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
  opacity: 0;
  filter: none;
  mix-blend-mode: normal;
  transition: filter 180ms ease, opacity 180ms ease;
  will-change: opacity, transform, filter;
}
.slab-frame.is-current { opacity: 1; }

/* This is explicitly a loading affordance, never a treatment of the incoming
   evidence. It is removed after decode and before promotion, so every settled
   frame remains the imported original with no filter or blend mode. */
.slab-well.is-loading .slab-frame.is-current {
  opacity: .48;
  filter: grayscale(1) brightness(.58);
}
.slab-frame-loading {
  position: absolute;
  z-index: 5;
  inset: 0;
  display: flex;
  gap: 9px;
  align-items: center;
  justify-content: center;
  pointer-events: none;
  color: var(--slab-cream);
  background: rgba(0, 0, 0, .28);
  font-family: var(--slab-mono);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: .08em;
  text-transform: uppercase;
}
.slab-frame-loading[hidden] { display: none; }
.slab-loading-mark {
  width: 8px;
  height: 8px;
  background: var(--slab-orange);
  box-shadow: 0 0 0 0 color-mix(in srgb, var(--slab-orange) 50%, transparent);
  animation: slab-loading-pulse 1.15s ease-out infinite;
}

@keyframes slab-loading-pulse {
  70%, 100% { box-shadow: 0 0 0 9px transparent; }
}

.slab.slab-switching .slab-caption > * { animation: slab-copy-arrive 320ms cubic-bezier(.22, .72, .2, 1) both; }
.slab.slab-switching .slab-chart svg { animation: slab-scope-arrive 320ms ease-out 40ms both; }
.slab.slab-switching .slab-counter span:first-child { animation: slab-count-arrive 320ms ease-out both; }

@keyframes slab-copy-arrive {
  from { opacity: .25; transform: translateX(var(--slab-drift, 5px)); }
  to { opacity: 1; transform: translateX(0); }
}

@keyframes slab-scope-arrive {
  from { opacity: .32; }
  to { opacity: 1; }
}

@keyframes slab-count-arrive {
  from { opacity: .3; transform: translateY(-4px); }
  to { opacity: 1; transform: translateY(0); }
}

.slab-arrow {
  position: absolute;
  z-index: 4;
  top: 50%;
  display: grid;
  width: 44px;
  height: 66px;
  padding: 0;
  place-items: center;
  cursor: pointer;
  color: var(--slab-cream);
  background: color-mix(in srgb, var(--slab-black) 55%, transparent);
  border: 1px solid rgba(255, 255, 255, .19);
  opacity: 0;
  transform: translateY(-50%);
  transition: opacity 140ms ease, background 140ms ease;
}
.slab-well:hover .slab-arrow,
.slab-arrow:focus-visible { opacity: 1; }
.slab-arrow:hover { background: color-mix(in srgb, var(--slab-orange) 88%, transparent); }
.slab-arrow-left { left: 12px; }
.slab-arrow-right { right: 12px; }

.slab-caption { padding: 16px 20px 17px; border-top: 1px solid var(--slab-line); }
.slab-heading { display: flex; gap: 12px; color: var(--slab-orange); font-family: var(--slab-mono); font-size: 8px; font-weight: 700; letter-spacing: .09em; text-transform: uppercase; }
.slab-heading span + span { color: var(--slab-muted); }
/* The strand chip sits at the far end of the heading rule because it labels the
   frame's photography rather than its position; only a study that measures a
   strand carries one. */
.slab-heading #slab-strand-label { margin-left: auto; color: var(--slab-gold); }
.slab-caption h1 { margin: 7px 0 0; color: var(--slab-cream); font-family: var(--slab-serif); font-size: clamp(23px, 2vw, 33px); font-weight: 500; letter-spacing: -.025em; line-height: 1.02; }
.slab-caption > p { max-width: 850px; margin: 8px 0 0; color: var(--slab-note); font-size: 11px; line-height: 1.45; }
.slab-caption-footer { display: flex; margin-top: 13px; gap: 14px; align-items: center; justify-content: space-between; }
.slab-palette { display: flex; width: min(360px, 48%); height: 8px; overflow: hidden; background: #000; }
.slab-palette span { height: 100%; min-width: 2px; }
.slab-metrics { display: flex; gap: 6px; flex-wrap: wrap; justify-content: flex-end; }
.slab-metrics span { padding: 3px 6px; color: var(--slab-muted); border: 1px solid var(--slab-line); font-family: var(--slab-mono); font-size: 7px; letter-spacing: .04em; text-transform: uppercase; }

.slab-transport {
  display: grid;
  min-height: 58px;
  padding: 9px 22px;
  grid-template-columns: minmax(180px, .7fr) minmax(320px, 1.8fr) minmax(260px, .7fr);
  gap: 22px;
  align-items: center;
  background: var(--slab-stage);
  border-top: 1px solid var(--slab-line);
}
.slab-transport-copy { display: grid; min-width: 0; line-height: 1.1; }
.slab-transport-copy strong { overflow: hidden; font-family: var(--slab-serif); font-size: 13px; font-weight: 500; text-overflow: ellipsis; white-space: nowrap; }
.slab-transport-copy span,
.slab-transport p { margin: 3px 0 0; color: var(--slab-muted); font-family: var(--slab-mono); font-size: 7px; letter-spacing: .06em; text-transform: uppercase; }
.slab-transport p { text-align: right; }
.slab-dots { display: flex; gap: 4px; align-items: center; justify-content: center; }
.slab-dots button { width: 100%; max-width: 18px; height: 6px; padding: 0; cursor: pointer; background: var(--slab-dot); border: 0; transition: height 120ms ease, background 120ms ease; }
.slab-dots button:hover { background: var(--slab-green); }
.slab-dots button[aria-current="true"] { height: 12px; background: var(--slab-orange); }

.slab-backdrop {
  position: fixed;
  z-index: 80;
  inset: 0;
  pointer-events: none;
  background: rgba(0, 0, 0, .82);
  opacity: 0;
  backdrop-filter: blur(6px);
  transition: opacity 160ms ease;
}
/* A latched scope — one opened by a tap, with no held key or finger keeping it
   there — is the only state where the backdrop is a control rather than a
   veil. */
.slab.slab-latched .slab-backdrop { pointer-events: auto; cursor: pointer; }

.slab-scope-close {
  position: fixed;
  z-index: 103;
  right: max(12px, env(safe-area-inset-right));
  /* Bottom rather than top: it is reached with the thumb that opened the scope,
     and the panel's own heading keeps its right end. */
  bottom: max(12px, env(safe-area-inset-bottom));
  padding: 12px 16px;
  color: var(--slab-cream);
  background: var(--slab-panel-raised);
  border: 1px solid var(--slab-line);
  cursor: pointer;
  font-family: var(--slab-mono);
  font-size: 10px;
  letter-spacing: .08em;
  text-transform: uppercase;
}
.slab-scope-close:focus-visible { outline: 2px solid var(--slab-gold); outline-offset: 2px; }

.slab-focus-hint {
  position: fixed;
  z-index: 102;
  right: 7vw;
  bottom: 3vh;
  pointer-events: none;
  opacity: 0;
  transition: opacity 120ms ease;
}
.slab-focus-hint span { padding: 6px 9px; color: var(--slab-muted); background: var(--slab-hint); border: 1px solid var(--slab-line); font-family: var(--slab-mono); font-size: 8px; letter-spacing: .08em; text-transform: uppercase; }
.slab.slab-focus-active:not(.slab-focus-releasing) .slab-backdrop,
.slab.slab-focus-active:not(.slab-focus-releasing) .slab-focus-hint { opacity: 1; }

/* The frame expands by the same geometry as a scope. It needs no rule of its
   own beyond this: .slab-media is already a size container, so the well
   recomputes the largest box of the frame's own aperture that fits the bigger
   room — the aspect is never stretched to make it fill. */
.slab-panel.is-focused,
.slab-media.is-focused {
  position: fixed;
  z-index: 100;
  top: 5vh;
  left: 7vw;
  width: 86vw;
  height: 90vh;
  /* Mobile browsers hand out a 100vh that the URL bar covers, so the dynamic
     unit is what the reader can actually see. */
  height: 90dvh;
  min-height: 0;
  background: var(--slab-focused);
  border-color: color-mix(in srgb, var(--slab-gold) 58%, transparent);
  box-shadow: 0 24px 100px rgba(0, 0, 0, .75);
  transform-origin: top left;
  transform: translate(var(--slab-dx, 0), var(--slab-dy, 0)) scale(var(--slab-sx, 1), var(--slab-sy, 1));
  transition: transform 460ms cubic-bezier(.2, .78, .18, 1), border-color 260ms ease, box-shadow 360ms ease;
}
.slab-panel.is-preparing,
.slab-media.is-preparing,
.slab-panel.is-resetting,
.slab-media.is-resetting { transition: none !important; }
.slab-panel.is-focused.is-expanded,
.slab-media.is-focused.is-expanded { transform: translate(0, 0) scale(1, 1); }
/* The frame at full size is a picture, not a panel: black behind it, a border
   to separate it from the backdrop, and its own transport still reachable. */
.slab-media.is-focused { padding: 18px; background: #000; border: 1px solid var(--slab-line); }
.slab-media.is-focused .slab-arrow { opacity: 1; }
.slab-media:focus-visible { outline: 2px solid var(--slab-gold); outline-offset: -2px; }
.slab-panel.is-focused figcaption { min-height: 67px; padding: 12px 15px; grid-template-columns: 38px minmax(0, 1fr) auto; }
.slab-panel.is-focused .slab-key { width: 38px; height: 38px; }
.slab-panel.is-focused figcaption strong { font-size: 23px; }
.slab-panel.is-focused .slab-chart { height: calc(100% - 67px); }

.slab .slab-axis text { fill: var(--slab-muted); font-family: var(--slab-mono); font-size: 7px; }
.slab .slab-axis line { stroke: var(--slab-line-mark); }
.slab .slab-axis path { display: none; }
.slab .slab-scope-label { fill: var(--slab-muted); font-family: var(--slab-mono); font-size: 7px; letter-spacing: .04em; }

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

@media (max-width: 1120px) {
  .slab-topbar { grid-template-columns: 1fr auto; }
  .slab-instructions { display: none; }
  .slab-workspace { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .slab-stage { min-height: 72vh; grid-column: 1 / -1; grid-row: 1; }
  .slab-rail { grid-template-columns: repeat(3, minmax(0, 1fr)); grid-template-rows: minmax(260px, auto); }
  .slab-panel:nth-child(1) { grid-column: 1; grid-row: 1; }
  .slab-panel:nth-child(2) { grid-column: 2; grid-row: 1; }
  .slab-panel:nth-child(3) { grid-column: 3; grid-row: 1; }
  .slab-rail-left { grid-column: 1 / -1; grid-row: 2; }
  .slab-rail-right { grid-column: 1 / -1; grid-row: 3; }
  .slab-transport { grid-template-columns: 1fr 2fr; }
  .slab-transport p { display: none; }
}

@media (max-width: 680px) {
  .slab-topbar { min-height: 64px; padding: 9px 12px; }
  .slab-identity span { display: none; }
  .slab-identity strong { font-size: 14px; }
  .slab-counter span:first-child { font-size: 17px; }
  .slab-workspace { padding: 7px; gap: 7px; grid-template-columns: 1fr; }
  .slab-stage { min-height: 68vh; }
  .slab-media { padding: 6px; }
  .slab-rail { grid-template-columns: 1fr; grid-template-rows: repeat(3, minmax(265px, auto)); gap: 7px; }
  .slab-panel:nth-child(1) { grid-column: 1; grid-row: 1; }
  .slab-panel:nth-child(2) { grid-column: 1; grid-row: 2; }
  .slab-panel:nth-child(3) { grid-column: 1; grid-row: 3; }
  .slab-caption { padding: 14px; }
  .slab-caption h1 { font-size: 25px; }
  .slab-caption-footer { align-items: flex-start; flex-direction: column; }
  .slab-palette { width: 100%; max-width: none; }
  .slab-metrics { justify-content: flex-start; }
  .slab-transport { padding: 9px 12px; grid-template-columns: 1fr; }
  .slab-transport-copy { display: none; }
  .slab-dots { gap: 2px; }
  .slab-dots button { height: 5px; }
  /* The close control keeps the bottom strip to itself, so a latched scope can
     always be folded back without scrolling for the button. */
  .slab-panel.is-focused,
  .slab-media.is-focused { top: 2vh; left: 3vw; width: 94vw; height: 96vh; }
  .slab-panel.is-focused,
  .slab-media.is-focused { top: 2dvh; height: calc(98dvh - 66px); }
  /* A landscape frame is width-bound on a phone. Giving its overlay the
     chart panel's 3vw gutters plus desktop padding made the image finish
     smaller than the compact frame. Let the black picture stage take the
     viewport width and keep only the compact six-pixel breathing room. */
  .slab-media.is-focused {
    left: 0;
    width: 100vw;
    padding: 6px;
    border-right: 0;
    border-left: 0;
  }
  .slab-focus-hint { right: 3vw; bottom: 1vh; }
}

/* A reader who asked for less motion still gets the frame; the renderer skips
   the crossfade entirely rather than shortening it. */
@media (prefers-reduced-motion: reduce) {
  .slab *,
  .slab *::before,
  .slab *::after { transition: none !important; }
  .slab.slab-switching .slab-caption > *,
  .slab.slab-switching .slab-chart svg,
  .slab.slab-switching .slab-counter span:first-child { animation: none !important; }
}
