/* ---------- depth scroll ----------
   The track is the page's real height and the only thing that scrolls. Every
   slide is pinned to the viewport on top of it, and js/depth-scroll.js writes
   scale and opacity onto them per frame. Height is set from JS, not here, so
   adding a section to the markup needs no CSS change.

   perspective on the track rather than transform: translateZ on the slides:
   the slides are position:fixed, and a fixed element is laid out against the
   viewport, not against an ancestor's 3D context, so real Z translation on
   them is silently flattened. Scaling is the reliable way to spend depth on
   fixed children. */
.depth-track{position:relative}

.depth-slide{
  position:fixed;inset:0;
  display:flex;flex-direction:column;
  align-items:center;justify-content:center;
  padding:var(--nav-h) var(--pad) clamp(1.5rem,5vh,3.5rem);
  opacity:0;
  pointer-events:none;
  /* Written every frame by JS, so no CSS transition: a transition here would
     fight the scroll position and lag behind it. */
  transition:none;
  will-change:transform,opacity;
  /* Scale from the centre of the frame, so the copy opens out around the
     reader rather than growing away from one corner. */
  transform-origin:50% 50%;
  backface-visibility:hidden;   /* keeps type off the CPU path in Safari */
}
.depth-slide.is-active{pointer-events:auto}

/* Reduced motion, and any case where the script bails: ordinary stacked
   sections in normal flow, scrolling the way a page does. */
.depth-track.is-static{height:auto!important}
.depth-track.is-static .depth-slide{
  position:relative;inset:auto;
  opacity:1!important;transform:none!important;filter:none!important;
  min-height:auto;padding-block:clamp(4rem,12vh,7rem);
  pointer-events:auto;
}
/* The first slide keeps the nav offset; the rest would double it. */
.depth-track.is-static .depth-slide + .depth-slide{padding-top:0}

/* .intro sizes itself against the viewport for the ordinary flow case. Inside
   a pinned slide the slide already IS the viewport, so the min-height would
   stack a second full screen inside one that is already full. */
.has-depth-scroll .intro{min-height:0;padding-block:0}

/* PROVI About — opening section. Sized to hold one screen with no scroll. */

/* svh, not vh: on mobile Safari and Chrome, 100vh is the height with the URL
   bar RETRACTED, so a section sized to 100vh is taller than the visible frame
   on load and its last line sits under the browser chrome. 100svh is the
   smallest state, which is the only one guaranteed to be on screen.
   min-height rather than height so the stacked mobile layout can still grow
   past the frame and scroll instead of clipping. */
.intro{
  min-height:calc(100svh - var(--nav-h));
  display:flex;flex-direction:column;
  align-items:center;justify-content:center;
  text-align:center;
  padding-block:clamp(1.5rem,5vh,3.5rem);
}

/* ---------- title ----------
   Every vertical measure in this section is clamped against vh as well as vw,
   because the constraint here is the height of the screen, not its width. A
   vw-only clamp fills a short wide laptop frame and pushes the columns off
   the bottom. */
.intro-title{
  font-family:var(--font-display);font-weight:400;
  text-transform:uppercase;
  font-size:clamp(1.8rem,min(5.4vw,7.2vh),4rem);
  letter-spacing:.02em;line-height:1.0;
  color:#B4B2AB;
}

/* The title used to be filled with the same fractal noise as the page
   overlay, clipped to the letterforms. It went with the rest of the CSS
   grain: at this size the speckle read as a dirty print rather than as
   film, and against the point-grain scene behind it the two textures
   fought. The glyphs are a flat paper tone now; the grain in the scene
   is the only grain on the page. */

/* Subtitle. Set in the display face and tracked open so it reads as part of
   the title block rather than as the first line of body copy, and kept dim
   enough that it never competes with the word above it. */
.intro-sub{
  margin-top:clamp(.9rem,2.2vh,1.5rem);
  font-family:var(--font-display);font-weight:400;
  text-transform:uppercase;
  font-size:clamp(.72rem,min(1.7vw,2.1vh),1.05rem);
  letter-spacing:.2em;line-height:1.4;
  /* Sits directly under the title and was the same grey as the body. It is
     the line that says what the studio is, so it takes the highlight. */
  color:var(--accent-text);
  text-shadow:0 0 18px rgba(10,10,12,.9);
}

/* ---------- the two columns ----------
   One column until there is room for two. The gap is wide because the two
   paragraphs speak in different voices (the studio, then the person), and a
   narrow gutter would read as one broken column of text. */
.intro-cols{
  margin-top:clamp(2rem,5.5vh,4rem);
  display:grid;grid-template-columns:1fr;
  gap:clamp(2rem,4.5vw,4rem);
  max-width:56rem;width:100%;
  text-align:left;
}
@media(min-width:800px){
  .intro-cols{grid-template-columns:1fr 1fr}
}

.intro-label{
  font-family:var(--font-display);font-weight:400;
  font-size:.64rem;letter-spacing:.2em;text-transform:uppercase;
  /* The section labels were the same grey as everything else on a page with
     no colour in it at all. They are the only fixed landmarks in a stack of
     slides, so they are the right place to spend the highlight. */
  color:var(--accent-text);
  padding-bottom:.8rem;margin-bottom:1.2rem;
  border-bottom:1px solid var(--line);
}
/* The role sits under the name on its own line rather than beside it: at
   this tracking the two run together into one long string. */
.intro-label span{display:block;margin-top:.45rem;color:var(--paper-faint)}

.intro-col p{
  color:#8A8882;
  font-size:clamp(.9rem,min(1.05vw,1.75vh),1rem);
  line-height:1.68;
}
/* The emphasised line each column ends on, matched to .scene-p--close on the
   homepage so the two pages agree about what a highlight means. An inline
   <em> keeps the old brighter grey: providentia and provisions sit inside a
   sentence, and a coloured word mid-prose reads as a link. */
.intro-col em{color:#C6C4BD;font-style:normal}
.intro-col .p-close em{color:var(--accent-text)}

/* A slide with one block instead of two. Same measure as a single column of
   the pair, so the reading width does not change between slides. */
.intro-cols--one{max-width:30rem}
@media(min-width:800px){
  .intro-cols--one{grid-template-columns:1fr}
}
.intro-col p + p{margin-top:1.05rem}

/* ---------- work slide ----------
   Cards left, copy right, waterfall filling the right of the frame behind
   both. The grid stops at 2fr 1.15fr rather than something even because the
   cards carry screenshots and need the width; the copy column is a single
   short paragraph and reads better narrow. */
/* This slide does NOT centre its content. The falls fill the right of the
   frame, so the cards and the copy are held to the left of it: a centred
   block would sit straight on top of the water. .depth-slide centres by
   default, so both axes are overridden here. */
.depth-slide--work{align-items:flex-start;justify-content:center}
.work-shell{
  width:100%;
  padding-inline:var(--pad);
}
.work-grid{
  display:grid;
  grid-template-columns:1fr;
  gap:clamp(1.5rem,2.6vw,2.6rem);
  align-items:center;
  /* Left column of the frame only. The vw cap is what reserves the right of
     the screen for the water; the rem cap stops the block from sprawling on
     an ultrawide, where 60vw is most of a metre. 60/58 rather than the old
     50/46: at the depth this slide sits at, the cards were reading as
     thumbnails. The falls still hold the right 40% of the frame. */
  width:100%;
  max-width:min(60vw,58rem);
  margin-right:auto;
}
@media(min-width:900px){
  .work-grid{grid-template-columns:1.32fr 1fr}
}
@media(max-width:1100px){
  .work-grid{max-width:min(76vw,52rem)}
}

/* ---------- the mirror (Dovito slide) ----------
   Everything the work slide does, handed the other way: the block is held to
   the RIGHT of the frame so the storefront can fill the left, the copy comes
   before the cards in source order and in the grid, and the card group turns
   away toward the right edge instead of the left. Only handedness is
   overridden here — sizing, type, and card treatment all stay shared, so the
   two sections read as a pair. */
.depth-slide--dovito{align-items:flex-end;justify-content:center}
/* Narrower than the work slide's block, and pinned to the right edge. The
   storefront's right edge lands at 34% of the frame (RIGHT_EDGE_AT in
   js/building.js), so a block starting at 44% clears it with room rather
   than butting up against it. */
.work-grid--mirror{
  margin-right:0;margin-left:auto;
  max-width:min(56vw,54rem);
}
@media(min-width:900px){
  /* copy first, cards second — the reverse weighting of the work slide */
  .work-grid--mirror{grid-template-columns:1fr 1.32fr}
}
/* The viewer sits to the RIGHT of the group here, matching where the cards
   now are on screen. An origin left of them would throw the vanishing point
   past the far edge and turn the recession inside out. */
.work-grid--mirror .work-persp{perspective-origin:82% 50%}
/* NEGATIVE rotateY, and the tilt subtracted rather than added. In CSS a
   positive angle pushes the right edge away; the mirror needs the LEFT edge
   pushed away and the right edge held forward, which is the opposite sign on
   both the resting angle and the scroll-driven one. */
.work-grid--mirror .work-stack{
  transform:rotateY(calc(-9deg - var(--tilt)));
  /* anchored on the near edge, which on this side is the right one */
  transform-origin:100% 50%;
}

/* The card stack from the sketch: one large frame, two smaller under it.
   perspective lives on the stack, not on the cards, so all three recede
   toward ONE vanishing point. Per-card perspective gives each its own, and
   the set reads as three unrelated tilts instead of a receding group. */
/* Perspective on the WRAPPER, rotation on the group inside it. Putting both
   on the same element makes an element's perspective apply to its children
   while its own rotation happens in the parent's (flat) space, which is why
   per-card rotation read as three unrelated turns: each card was spinning
   about its own right edge with its own vanishing point. Now one plane
   turns and the cards ride it. */
.work-persp{
  perspective:1500px;
  /* Viewer sits left of the group, matching where the cards are on screen.
     An origin off to the right threw the vanishing point past the far edge
     and turned the recession inside out. */
  perspective-origin:18% 50%;
}
.work-stack{
  display:grid;
  grid-template-columns:1fr 1fr;
  gap:clamp(.7rem,1.4vw,1.1rem);
  /* --tilt is written per frame by js/depth-scroll.js; the resting angle
     lives here so the group is already turned before any scrolling. */
  --tilt:0deg;
  /* POSITIVE rotateY. In CSS a point at +x maps to z' = -x·sin(theta), so a
     positive angle pushes the RIGHT edge away from the viewer and holds the
     left edge forward. That is the sketch: left edge tall and near, top edge
     sloping down to the right. The negative angle did the exact opposite. */
  transform:rotateY(calc(9deg + var(--tilt)));
  /* Anchored on the near edge, so the group swings away to the right instead
     of sliding sideways as it turns. */
  transform-origin:0% 50%;
  transform-style:preserve-3d;
  will-change:transform;
}
.work-card:first-child{grid-column:1 / -1}

.work-card{
  display:block;
  position:relative;
  overflow:hidden;
  border:1px solid var(--line);
  background:var(--ink-2);
  transition:border-color .3s;
}
.work-card:hover{border-color:var(--line-strong)}
.work-card img{
  width:100%;height:100%;object-fit:cover;object-position:top center;
  aspect-ratio:16/10;
  /* Screenshots are full-colour and this page is achromatic. Desaturated and
     held down so they sit in the same tonal world as the grain, then brought
     most of the way back on hover: the work should be legible when someone
     goes looking at it, and quiet when they are reading past it. */
  filter:grayscale(1) contrast(1.08) brightness(.62);
  transition:filter .45s ease,transform .6s ease;
}
.work-card:hover img{filter:grayscale(.15) contrast(1.02) brightness(.92)}

.work-cap{
  position:absolute;left:0;right:0;bottom:0;
  padding:.85rem .9rem .75rem;
  background:linear-gradient(180deg,rgba(10,10,12,0),rgba(10,10,12,.92));
  font-family:var(--font-display);font-weight:400;
  font-size:.64rem;letter-spacing:.18em;text-transform:uppercase;
  color:#9C9A94;
}
.work-cap span{display:block;margin-top:.35rem;color:var(--paper-faint);font-size:.58rem}
.work-card:hover{border-color:var(--accent-dim)}
.work-card:hover .work-cap{color:var(--accent-text)}

.work-copy .intro-label{margin-bottom:1rem;font-size:.7rem}
.work-copy p{
  color:#8A8882;
  font-size:clamp(.9rem,min(1.1vw,1.75vh),1.05rem);
  line-height:1.68;
}
.work-copy p + p{margin-top:.95rem}
.work-copy em{color:#C6C4BD;font-style:normal}
.work-copy .p-close em{color:var(--accent-text)}

/* The waterfall canvas. Fixed and behind the content like every other scene
   canvas on the site; js/waterfall.js drives its opacity off the slide's own
   depth progress, so it is only ever painted while this section is on. */
#waterfallCanvas,
#buildingCanvas{
  position:fixed;inset:0;z-index:0;
  display:block;
  opacity:0;
  pointer-events:none;
}
.webgl-unavailable #waterfallCanvas,
.webgl-unavailable #buildingCanvas{display:none}

@media(max-width:899px){
  /* A DEPTH SLIDE CANNOT SCROLL. It is position:fixed and inset:0, so
     anything past the viewport is not below the fold, it is gone — and on a
     390x844 phone the Dovito slide measured about 738px of content into
     728px of usable height, which quietly ate the bottom row of cards. There
     is no overflow to chase; the content has to fit, so the type comes down
     and the gaps close up until it does. */
  .work-grid{gap:clamp(.9rem,3vw,1.4rem)}
  .work-copy .intro-label{font-size:.62rem;padding-bottom:.55rem;margin-bottom:.7rem}
  .work-copy p{font-size:.82rem;line-height:1.5}
  .work-copy p + p{margin-top:.6rem}
  /* The captions were set for a card half the frame wide. On a card 160px
     across they were taking three lines and covering the screenshot. */
  /* The gradient was tuned for a card half the frame wide. On one 100px tall
     it starts fading in over the middle of the screenshot, so the caption's
     first line lands on bare photograph. Shorter ramp, darker foot. */
  .work-cap{
    padding:.5rem .55rem .45rem;
    background:linear-gradient(180deg,rgba(10,10,12,0),rgba(10,10,12,.86) 42%,rgba(10,10,12,.96));
  }
  .work-cap{font-size:.5rem;letter-spacing:.12em}
  .work-cap span{font-size:.46rem;margin-top:.25rem}
  .work-stack{gap:.5rem}

  /* The falls occupy the right of the frame, which on a phone is where the
     copy has to go. Pulled back to a backdrop rather than a subject. */
  .work-grid{max-width:none;margin-right:0}
  .depth-slide--work{align-items:center}
  .work-persp{perspective:1100px;perspective-origin:50% 50%}
  .work-stack{transform:rotateY(calc(4deg + var(--tilt) * .5))}
  /* The storefront occupies the left of the frame, which on a phone is where
     the copy has to go. Pulled back to a backdrop, same as the falls. */
  .work-grid--mirror{margin-left:0}
  .depth-slide--dovito{align-items:center}
  .work-grid--mirror .work-stack{transform:rotateY(calc(-4deg - var(--tilt) * .5))}
}
