/* ==========================================================================
   work-gl.css — the GL stage for #work.

   Loaded AFTER gallery.css so it wins where the two overlap, and it only ever
   applies under .work-gl-on on <html>. Without that class the section is the
   original CSS-3D runway, untouched. The GL layer is an enhancement; there is
   no code path in which it can leave the section unusable.

   The split of labour, once the class is on:

     the canvas   draws the room — floor grid, depth, fog, the pointer wake,
                   the lens — and the three site plates standing in it.
     the DOM      keeps every word and every control. It is projected onto the
                   plate it belongs to, so it stays real text: selectable,
                   translatable, focusable, and legible to a screen reader.

   That last part is the deliberate divergence from the reference, which paints
   its titles into WebGL with -webkit-text-fill-color: transparent. Doing that
   here would buy nothing — the room is ours, the words are ours — and it would
   cost us focus rings and text rendering. See work-gl.js for the projection.
   ========================================================================== */

.work-gl-canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  z-index: 0;
  pointer-events: none;
  opacity: 0;
  transition: opacity 600ms var(--ease);
}

/* --------------------------------------------------------------------------
   The heading is out of the stage entirely (gallery.css), so it never shares a
   stacking context with the composited canvas any more — the
   background-clip:text rasterisation bug the will-change workaround existed
   for cannot reach it. The workaround and its comment are gone with the old
   absolute rule.

   Nothing else in the stage is given a z-index here, and that is deliberate.
   gallery.css already puts .gallery-head at z-index 3 and .gallery-foot at 2.
   The canvas at 0 simply sits under them, and nothing in the existing stacking
   order is disturbed.
    -------------------------------------------------------------------------- */

/* The room only exists where the stage is. Until the first frame has actually
   been drawn we keep the canvas transparent, because a black rectangle
   flashing over a section that still shows the CSS runway is worse than the
   delay. .work-gl-ready is set by JS after the first successful draw. */
.work-gl-on .work-gl-canvas.is-live {
  opacity: 1;
}

.work-gl-on .gallery-viewport {
  cursor: grab;
}

.work-gl-on .gallery-viewport.is-dragging {
  cursor: grabbing;
}

/* --------------------------------------------------------------------------
   The plate itself stops being painted by the DOM in GL mode. It stays laid
   out — that is how we still get a real hit target and a real focus order per
   project, and how JS measures honest card widths — but it paints nothing,
   because the canvas is drawing it now.

   The cards leave the track and every plate takes the SAME origin, so one
   translate per card is the whole of the positioning. Left in the flow, each
   card would carry its own static offset into the runway and the projection
   would be offset by a different amount every time; that is the bug this
   arrangement exists to prevent.
   -------------------------------------------------------------------------- */
.work-gl-on .gallery-card {
  /* The plate is absolutely positioned, so the card has to be its containing
     block — stated here rather than inherited from gallery.css, because losing
     it silently moves every plate by that card's own offset in the runway. */
  position: relative;
  /* The old per-frame --gx/--gy/--gr transforms are meaningless against a
     camera. Cleared rather than left to fight the projection. */
  transform: none !important;
  filter: none !important;
}

.work-gl-on .gallery-shot {
  position: absolute;
  left: 0;
  top: 0;
  /* width comes from JS: measured once from the real flow layout before this
     class lands, because an absolute box with no width shrink-wraps. */
  will-change: transform;
  transition: none;
}

/* Only the picture goes. The plate box itself must stay fully opaque and
   hit-testable, because .gallery-ui lives inside it — hiding the box would hide
   the captions and every button on them. */
.work-gl-on .gallery-plane,
.work-gl-on .gallery-shot::before,
.work-gl-on .gallery-shot::after,
.work-gl-on .gallery-floor,
.work-gl-on .gallery-spec {
  opacity: 0;
}

/* --------------------------------------------------------------------------
    Captions. Each plate's own .gallery-ui block sits on its bottom-left corner,
    as real HTML, at a fixed type size, with real focus rings — and the dark
    band under the words is drawn by the PLATE SHADER now (uScrim), because the
    DOM's own ::after scrim is a flat rectangle and the plate is neither flat
    nor still: it bends, fogs and hovers, and a fixed gradient would sit on it
    like a sticker. The band lands in the plate's own uv, so it curves with the
    plate and the label is legible over the recordings' bright footers.

    right: 26% bounds the text column so a long name ("Bob's Plumbing Co.")
    wraps before it runs under the round live button in the opposite corner.
    -------------------------------------------------------------------------- */
.work-gl-on .gallery-ui {
  position: absolute;
  left: 5.5%;
  right: 26%;
  bottom: 5.5%;
  top: auto;
  width: auto;
  max-width: none;
  transform-origin: 0 100%;
  pointer-events: none;
  will-change: transform, opacity;
  transition: none;
}

.work-gl-on .gallery-ui > * {
  pointer-events: auto;
}

.work-gl-on .gallery-peek {
  margin-top: 10px;
}

.work-gl-on .gallery-name {
  /* On the plate the name is the headline, not one line in a stack. */
  font-size: clamp(1.4rem, 2.6vw, 2.4rem);
  line-height: 1.02;
  letter-spacing: -0.02em;
  text-wrap: balance;
}

.work-gl-on .gallery-kind {
  color: var(--detail-accent, #ff8064);
  /* Small coral caps are the first thing to disappear over a light video
     frame; the name already carries a shadow from gallery.css. */
  text-shadow: 0 1px 14px rgba(0, 0, 0, .9);
}

.work-gl-on .gallery-peek,
.work-gl-on .gallery-open svg {
  filter: drop-shadow(0 1px 10px rgba(0, 0, 0, .85));
}

/* The round open-the-demo button, in the plate's bottom-right corner — the
   one place it can be a circle without reading as decoration, and the pair
   (words left, button right) is the reference's own layout. The base rule in
   gallery.css pins it at right:4px of whatever box it is in; here that box is
   the projected plate, so it needs a real inset and a size that survives the
   plate's scale. Backdrop blur keeps it legible over a bright recording; JS
   gates its opacity with the caption, so a neighbour never carries a button. */
.work-gl-on .gallery-open {
  position: absolute;
  right: 4.5%;
  bottom: 7%;
  width: clamp(44px, 3.2vw, 58px);
  height: clamp(44px, 3.2vw, 58px);
  margin: 0;
  background: rgba(11, 7, 16, .62);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
}

/* Off-plate captions are faded to nothing by inline opacity, and hidden from
   hit-testing and from the a11y tree by inline visibility, so a card the
   visitor cannot see never offers a button to a keyboard either. */

/* --------------------------------------------------------------------------
   Spec list: gone from the room.

   The room used to mirror the front plate's "BUILT WITH / WHAT IT DOES /
   OWNERSHIP" into the stage foot. It went because it is a second place saying
   the same three things the caption already says, and because it cross-faded as
   the front plate changed — which put a line of text on screen for a frame on
   every single transition. The per-card .gallery-spec stays in the DOM for the
   CSS fallback and for anything reading the page as text; the room just does not
   show it.
   -------------------------------------------------------------------------- */

/* The drag hint goes with them: it describes a strip you push along. */
.work-gl-on .gallery-help {
  display: none;
}

/* --------------------------------------------------------------------------
   The three rail pointers are gone from the room too. In a room you stand in
   rather than a strip you push along, three ticks along the bottom edge are a
   control for a different interaction, and they read as debris next to the floor
   grid. The rail is still built and still a real tablist — it is only hidden
   while the room is up, so keyboard and the CSS fallback are untouched.
   -------------------------------------------------------------------------- */
.work-gl-on .gallery-rail {
  display: none;
}

/* --------------------------------------------------------------------------
   Focus must be obvious on a caption floating over a dark plate. The outline
   is drawn on the caption block, not on the (invisible) card, so it lands
   where the eye is.
   -------------------------------------------------------------------------- */
.work-gl-on .gallery-ui :focus-visible,
.work-gl-on .gallery-foot :focus-visible {
  outline: 2px solid var(--detail-accent, #ff8064);
  outline-offset: 3px;
  border-radius: 4px;
}

/* --------------------------------------------------------------------------
   Reduced motion. Not "the animation is shorter" — the room does not move.
   The plates are laid out by the same projection but the camera is fixed, the
   wake and the fog are off, and the depth push on scroll is gone. Everything
   stays reachable and readable; it just stops performing.
   -------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  .work-gl-on .work-gl-canvas {
    transition: none;
  }

  .work-gl-on .gallery-ui {
    will-change: transform;
  }

}

/* --------------------------------------------------------------------------
   Phones. Below the gallery's own 900px breakpoint the section stacks and the
   CSS runway is the better layout — a room with 80mm of depth is unreadable on
   a 390px screen. GL stays off. Same reason the incumbent parks its videos.
   -------------------------------------------------------------------------- */
@media (max-width: 900px) {
  .work-gl-canvas {
    display: none;
  }

}