/* KRITIKAL — the rig as the page.
 *
 * One canvas, fixed behind the whole document. The sections sit over it and go
 * translucent so the stack is genuinely behind them, moving as you scroll,
 * rather than being a loop playing in a box at the top of the page.
 *
 * Every translucency rule here is gated on `html.rig-film`, which JavaScript
 * only adds once a WebGL context has actually been created. Without that class
 * every section keeps its original opaque background, so a browser that cannot
 * run the scene gets the site exactly as it was before any of this existed —
 * not a page of half-transparent panels over nothing.
 */

.rigfilm {
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;      /* drag is captured on window — see drag-spin.js */
}

.rigfilm__canvas {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
}

/* No grab cursor at rest. The rig is draggable from anywhere, but `cursor:
   grab` inherits, so setting it on the document would put a hand over every
   paragraph on the site and make the text look unreadable rather than
   grabbable. It only appears once a drag is genuinely under way. */
.rig-dragging,
.rig-dragging body,
.rig-dragging * { cursor: grabbing !important; }

/* While turning the rig, stop drags selecting the copy they pass over. */
.rig-dragging body { user-select: none; }

/* No scroll-linked camera when motion is reduced. Scroll-driven movement is
   precisely what that setting asks us not to do, so the canvas stops being
   fixed and scrolls away with the first screen, like a photograph of the rig.
 *
 * Keyed off the class JavaScript sets, NOT off the media query directly. The
 * two can disagree: ?motion=full deliberately overrides the system setting, and
 * a media query cannot see that. When it was written as a media query the
 * canvas stayed absolutely positioned while the scene ran, so the rig sat
 * frozen at the top of the page and every section below it looked empty. */
.rig-still .rigfilm {
  position: absolute;
  top: 0;
  height: 100svh;
}

.rigfilm__canvas:focus-visible {
  outline: 2px solid var(--hivis);
  outline-offset: -2px;
}

/* Everything the page draws sits above the scene.
 *
 * NOT the skip link. It is `position: absolute` and hidden off the top of the
 * screen by a transform, so forcing it to `relative` puts it back in flow and
 * it appears on every page — which is exactly what happened. It already has
 * z-index 100 and is positioned, so it paints above the canvas untouched. */
body > header,
body > main,
body > footer {
  position: relative;
  z-index: 1;
}

/* ---------- Letting the rig through ----------
 *
 * Dark sections go translucent; the pale cabinet sections stay solid. That
 * alternation is deliberate rather than a compromise: the site is built as a
 * stack of cabinets, and the rig passing behind the dark ones and disappearing
 * behind the pale ones reads as depth. A rig ghosting faintly through every
 * section without exception would just be noise.
 */

.rig-film .hero { background: transparent; }
.rig-film .hero__poster { opacity: 0.4; }

/* The hero scrim exists to keep the headline legible over the model. With the
   model now the subject rather than the backdrop, it pulls back to the left
   third, where the type actually is. */
.rig-film .hero__scrim {
  background:
    linear-gradient(to right,
      rgb(var(--pit-rgb) / 0.93) 0%,
      rgb(var(--pit-rgb) / 0.7) 30%,
      rgb(var(--pit-rgb) / 0.06) 58%,
      rgb(var(--pit-rgb) / 0) 100%),
    linear-gradient(to top,
      rgb(var(--pit-rgb) / 0.8) 0%,
      rgb(var(--pit-rgb) / 0.12) 38%,
      rgb(var(--pit-rgb) / 0) 70%);
}

/* Narrow screens put the copy over the middle of the model, so the wash runs
   the other way or the type lands on a lit cabinet face.
 *
 * This breakpoint is mirrored in js/rig3d.js, which re-centres the rig at the
 * same width and reads the same media query rather than a number near it. They
 * were 860 here and 768 there, so between those two widths the rig kept its
 * desktop placement — offset right, beside a column of type that had already
 * reflowed to full width — underneath a scrim built for a centred one. */
@media (max-width: 860px) {
  .rig-film .hero__scrim {
    background: linear-gradient(to top,
      rgb(var(--pit-rgb) / 0.92) 0%,
      rgb(var(--pit-rgb) / 0.8) 38%,
      /* Opened up through the middle, where the re-centred rig now sits */
      rgb(var(--pit-rgb) / 0.46) 70%,
      rgb(var(--pit-rgb) / 0.62) 100%);
  }
}

/* The head of an inner page is that page's hero, so it opens up in both
   modes. Everything below it only opens up when the scene is actually running
   — see the reduced-motion note at the foot of this file. */
.rig-film .page-head { background: rgb(var(--pit-rgb) / 0.4); }

/* A window, not a wash.
 *
 * Two attempts failed before this one, and both failed for the same reason. A
 * flat alpha at 0.82 swallowed the rig; dropping it far enough to see through
 * put body copy on top of lit cabinets. Then a long gradient fade left the
 * stack sitting at around 70% alpha, and a grey object behind a dark panel at
 * 70% is not "subtle", it is gone.
 *
 * There is no alpha that both hides and reveals. So the sections stay fully
 * opaque under the column of type and become fully transparent to the right of
 * it — a hole in the page that the scene shows through, with a short blend so
 * the edge is not a visible seam. The scene path puts the stack inside that
 * hole. Contrast where the words are, and the actual rig where they are not.
 */
.rig-film:not(.rig-still) .cab--pit {
  background: linear-gradient(to right,
    rgb(var(--pit-rgb)) 0%,
    rgb(var(--pit-rgb)) 54%,
    rgb(var(--pit-rgb) / 0.72) 63%,
    rgb(var(--pit-rgb) / 0) 76%,
    rgb(var(--pit-rgb) / 0) 100%);
}

.rig-film:not(.rig-still) .cab--dark {
  background: linear-gradient(to right,
    rgb(var(--warnex-rgb)) 0%,
    rgb(var(--warnex-rgb)) 54%,
    rgb(var(--warnex-rgb) / 0.72) 63%,
    rgb(var(--warnex-rgb) / 0) 76%,
    rgb(var(--warnex-rgb) / 0) 100%);
}

.rig-film:not(.rig-still) .creds   { background: rgb(var(--warnex-rgb) / 0.6); }
.rig-film:not(.rig-still) .anatomy { background: rgb(var(--pit-rgb) / 0.22); }
.rig-film:not(.rig-still) .footer  { background: rgb(var(--pit-rgb) / 0.92); }

/* Content that genuinely spans the full width — a photo strip, a card grid —
   would otherwise be cut in half by the window. It gets its own opaque plate
   behind it, so the window only opens where there is nothing to read. */
.rig-film:not(.rig-still) .cab--dark .build-strip,
.rig-film:not(.rig-still) .cab--pit .work-grid,
.rig-film:not(.rig-still) .cab--dark .rail {
  position: relative;
}

@media (max-width: 900px) {
  /* The copy runs the full width here, so there is no room for a window beside
     it. The rig sits behind a nearly solid panel and the page reads normally;
     the hero and the page head are where it gets to be seen on a phone. */
  .rig-film:not(.rig-still) .cab--pit  { background: rgb(var(--pit-rgb) / 0.94); }
  .rig-film:not(.rig-still) .cab--dark { background: rgb(var(--warnex-rgb) / 0.94); }
}

/* ---------- Page heads ----------
   Taller than they were, because the rig needs somewhere to be. */

.page-head[data-rig-head] {
  min-height: 62vh;
  display: flex;
  align-items: flex-end;
}

@media (max-width: 700px) {
  .page-head[data-rig-head] { min-height: 44vh; }
}

/* The copy layer is a full-width block with nothing in most of it. Left alone
   it would swallow every drag aimed at the model beside it: the guard in
   drag-spin.js only skips genuinely interactive elements, so plain text blocks
   have to be transparent to the pointer here, and anything you click or select
   gets it back. */
.page-head[data-rig-head] > .wrap,
.hero__content {
  pointer-events: none;
}

.page-head[data-rig-head] > .wrap :is(a, button, input, label, h1, h2, h3, p, li),
.hero__content :is(a, button, input, label, h1, h2, h3, p, li) {
  pointer-events: auto;
}

/* ---------- Anatomy: the hire page's exploded view ----------
 *
 * There is no canvas in this section. The fixed scene holds the stack on the
 * right of the viewport for the length of it, and this is the column of names
 * beside it. The section's height IS the interaction — that is the scroll
 * budget the explosion is spread across, not padding.
 */

.anatomy {
  position: relative;
  border-top: 1px solid var(--edge-dark);
  background: var(--pit);
  padding-block: var(--s-6) 0;
}

.anatomy__intro { max-width: 46ch; }

.anatomy__track {
  position: relative;
  min-height: 300vh;
}

.anatomy__pin {
  position: sticky;
  top: 0;
  height: 100vh;
  height: 100svh;
  display: grid;
  grid-template-columns: minmax(0, 0.9fr) minmax(0, 1.1fr);
  align-items: center;
}

.anatomy__rows {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--s-3);
  align-content: center;
}

/* Rows are legible before the scene reaches them — dimmed, not hidden. A list
   that is blank until you scroll far enough is a list nobody can search. */
.anatomy__row {
  border-left: 2px solid var(--edge-dark);
  padding: var(--s-2) 0 var(--s-2) var(--s-3);
  opacity: 0.36;
  transition:
    opacity var(--dur) var(--ease-out),
    border-color var(--dur) var(--ease-out),
    transform var(--dur) var(--ease-out);
}

.anatomy__row.is-active {
  opacity: 1;
  border-left-color: var(--hivis);
  transform: translateX(5px);
}

.anatomy__row h3 {
  font-size: var(--t-md);
  font-stretch: 110%;
  margin: 0 0 0.35rem;
}

.anatomy__row p {
  margin: 0;
  color: var(--ink-invert-2);
  font-size: var(--t-sm);
  max-width: 34ch;
}

.anatomy__count {
  display: block;
  font-family: var(--mono);
  font-size: var(--t-xs);
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--accent-text);
  margin-bottom: 0.35rem;
}

.anatomy__hint,
.rig-hint {
  font-family: var(--mono);
  font-size: var(--t-xs);
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ink-invert-2);
  opacity: 0;
  margin: var(--s-2) 0 0;
  transition: opacity var(--dur-slow) var(--ease-out);
}

/* Shown only once the scene is genuinely live. Telling someone to drag a model
   that failed to load is worse than saying nothing. */
.rig-film .anatomy__hint,
.rig-film .rig-hint { opacity: 0.7; }
.rig-still .anatomy__hint,
.rig-still .rig-hint { display: none; }

@media (max-width: 900px) {
  .anatomy__track { min-height: 240vh; }

  .anatomy__pin {
    grid-template-columns: minmax(0, 1fr);
    align-content: end;
    padding-bottom: var(--s-5);
  }

  .anatomy__rows { align-content: end; gap: var(--s-1); }
  .anatomy__row p { display: none; }   /* the model is the explanation here */
}

/* ---------- Reduced motion ----------
 *
 * The scene renders one composed frame and stops. With a canvas fixed over the
 * whole document that is not enough on its own: the rig would hang frozen in
 * its opening pose behind every section for the length of the site. So the
 * canvas is un-fixed to the height of the first screen and scrolls away, the
 * sections below keep their opaque backgrounds, and the result is a still
 * photograph of the rig at the top of an otherwise ordinary page.
 */

/* ...and put it back if the scene is genuinely running.
 *
 * The collapse above is keyed to the media query so it still applies when
 * JavaScript never runs at all and there is no scene to scroll through. But
 * ?motion=full deliberately overrides the system setting, and a media query
 * cannot see that — without this the explosion had no scroll budget to happen
 * in and the section flashed past in a third of a screen. These rules are more
 * specific, so they win wherever the scene is live and not still. */
.rig-film:not(.rig-still) .anatomy__track { min-height: 300vh; }

.rig-film:not(.rig-still) .anatomy__pin {
  position: sticky;
  height: 100svh;
  padding-block: 0;
  grid-template-columns: minmax(0, 0.9fr) minmax(0, 1.1fr);
}

@media (max-width: 900px) {
  .rig-film:not(.rig-still) .anatomy__track { min-height: 240vh; }
  .rig-film:not(.rig-still) .anatomy__pin { grid-template-columns: minmax(0, 1fr); }
}

@media (prefers-reduced-motion: reduce) {
  .anatomy__track { min-height: 0; }
  .anatomy__pin {
    position: static;
    height: auto;
    grid-template-columns: minmax(0, 1fr);
    padding-block: var(--s-4);
  }
  .anatomy__row { opacity: 1; transition: none; }
}

/* ---------- Light mode: take the paper off the rig ----------
 *
 * Every overlay above is a wash of --pit. In the dark that is a shadow laid
 * over a lit object, which reads as mood. In the light --pit is the paper, so
 * the same washes are a sheet of tracing paper over the stack: the hero poster
 * alone was a 40% veil across all of it, and the rig came out as a ghost.
 *
 * So in light mode the washes stay only where there is type to protect, and
 * the rig itself is lit darker than the page (see --rig-* in tokens.css) so it
 * stands as a solid grey object against it.
 */
[data-mode="light"].rig-film .hero__poster { opacity: 0; }

[data-mode="light"].rig-film .hero__scrim {
  background:
    linear-gradient(to right,
      rgb(var(--pit-rgb) / 0.92) 0%,
      rgb(var(--pit-rgb) / 0.86) 40%,
      rgb(var(--pit-rgb) / 0) 60%),
    linear-gradient(to top,
      rgb(var(--pit-rgb) / 0.55) 0%,
      rgb(var(--pit-rgb) / 0) 16%);
}

[data-mode="light"].rig-film .page-head { background: rgb(var(--pit-rgb) / 0.34); }

/* ---------- Phones: the rig gets its own room ----------
 *
 * On a narrow screen the copy runs the full width, so there is nowhere beside
 * it for the rig to stand. It used to sit centred BEHIND the type instead, and
 * that never worked: either the scrim was heavy enough to read the words, and
 * the rig was a smudge, or it was not, and the words sat on lit cabinets.
 *
 * So on a phone the two stop sharing space. The rig is lifted into the top part
 * of the screen (narrowify() in data/scene-path.js does the lifting), it fills
 * that, and each page's opening type starts underneath.
 * Sections below are solid and slide over it like a card. Nothing is ever read
 * through the model.
 *
 * The space is reserved from first paint, not when the scene arrives, so the
 * headline does not jump down a second after load. `no-rig` is set by
 * js/rig3d.js when there is no WebGL to wait for, and gives the space back.
 */
@media (max-width: 860px) {
  html:not(.no-rig) .hero { align-items: start; }
  html:not(.no-rig) .hero__content { padding-top: 57svh; }

  html:not(.no-rig) .page-head,
  html:not(.no-rig) .page-head[data-rig-head] {
    min-height: 0;
    padding-top: 57svh;
  }

  /* Clear over the rig, solid under the type. Both modes: with the type moved
     out from in front of the model there is nothing left for a wash to do. */
  .rig-film .hero__poster { opacity: 0; }

  .rig-film .hero__scrim,
  [data-mode="light"].rig-film .hero__scrim,
  .rig-film .page-head,
  [data-mode="light"].rig-film .page-head {
    background: linear-gradient(to bottom,
      rgb(var(--pit-rgb) / 0) 0,
      rgb(var(--pit-rgb) / 0) 53svh,
      rgb(var(--pit-rgb) / 0.94) 61svh,
      rgb(var(--pit-rgb) / 0.94) 100%);
  }

  .rig-film:not(.rig-still) .cab--pit  { background: var(--pit); }
  .rig-film:not(.rig-still) .cab--dark { background: var(--warnex); }
  .rig-film:not(.rig-still) .creds     { background: var(--warnex); }

  /* The hire page's taken-apart section, laid out as it is on a desktop: the
     three rows down the left, the exploded stack standing beside them on the
     right. A stack pulled apart is tall and narrow, which is exactly the shape
     of the space beside a column of type on a phone. The heading block above
     is solid, so it slides over the rig on the way in. */
  .rig-film:not(.rig-still) .anatomy { padding-top: 0; }
  .rig-film:not(.rig-still) .anatomy > .wrap {
    background: var(--pit);
    padding-block: var(--s-6) var(--s-4);
  }
  .anatomy__hint { display: none; }

  .rig-film:not(.rig-still) .anatomy__pin {
    grid-template-columns: minmax(0, 52%) minmax(0, 1fr);
    align-content: center;
    padding-bottom: 0;
  }

  .rig-film:not(.rig-still) .anatomy__rows { align-content: center; gap: var(--s-2); }

  .rig-film:not(.rig-still) .anatomy__row {
    padding: var(--s-1) 0 var(--s-1) var(--s-2);
  }
  .rig-film:not(.rig-still) .anatomy__row.is-active { transform: none; }
  .rig-film:not(.rig-still) .anatomy__row h3 { font-size: var(--t-base); }
  .rig-film:not(.rig-still) .anatomy__count { letter-spacing: 0.2em; font-size: 0.625rem; }

  /* Only the row the model is showing gets its description. All three open at
     once is a wall of small type taller than the screen. */
  .rig-film:not(.rig-still) .anatomy__row.is-active p {
    display: block;
    font-size: 0.8125rem;
    line-height: 1.5;
    max-width: none;
  }
}
