/* ==========================================================================
   bto-scroll-stack.css
   SHIPPED COPY — lifted 2026-09-30 from _lab/scroll-stack/stack.css

   The LAB folder is the DESIGN source and the founder review page (_lab/index.html).
   THIS file is what the homepage serves. They are two copies on purpose: the lab
   page must stay editable without changing a live page.

   If you change the design, change it in the lab, review it there, then re-lift
   with tools/lift-lab-section.sh. Do NOT diverge this file by hand.
   ==========================================================================*/
/* ==========================================================================
   stack.css — scroll-driven "recent work" stack.
   Pairs with stack.js and data.js. No build step, no framework, no CDN.

   PINNING IS NATIVE `position: sticky`. That is load-bearing:
   never introduce Lenis, ScrollSmoother or any smooth-scroll library that
   scrolls by transforming a wrapper. A transformed ancestor becomes the
   containing block and kills every descendant sticky — documented in
   assets/js/bto-motion.js, where it already cost this site a re-authored pin.

   THEMING: everything you would want to change is a custom property in the
   :root block below. Changing the palette is a 5-line edit.
   ========================================================================== */

:root {
  /* ---- palette — BY THE OWL, MONOCHROME ------------------------------
     Founder ruling 2026-09-23: the accent is black and the site is monochrome.
     Every token below is `var(--brand-token, <literal>)` so this component
     INHERITS the real palette when it is dropped into a page that loads
     bto-theme.css, and still renders correctly standing alone in the lab.
     Values + roles: SKIN.md §0 and §4.                                     */
  --stack-bg:          var(--primary,  #030303);  /* ground — was #101010   */
  --stack-ink:         var(--on-dark,  #FFFFFF);  /* headline + card title  */
  --stack-ink-dim:     rgba(255, 255, 255, 0.78); /* eyebrow — 12.27:1      */
  --stack-hairline:    rgba(255, 255, 255, 0.14); /* SKIN.md §4             */
  --stack-cta-bg:      var(--on-dark,  #FFFFFF);  /* pill fill — was #ff4fa3 */
  --stack-cta-ink:     var(--primary,  #030303);  /* pill label  20.59:1    */

  /* ---- THE DEPTH LADDER — the whole monochrome answer -----------------
     The five saturated frame colours are GONE and are not replaced by five
     greys. Colour is now a function of DEPTH, not of card: `--stack-max-depth`
     is 3, so only ever FOUR surfaces render, however many cards there are.
     Five per-card greys would have to spread the same range five ways; eight
     would be ~1.25:1 apart and invisible. Positional never degrades. §2.

     The engine writes ONE NUMBER per card per frame — the opacity of the
     shade overlay — and never a colour. Both ends live here, so re-skinning
     the ladder stays a token edit and the hot path stays transform+opacity.

       depth 0 = --stack-frame-near          #F2F4F7   (the site's card fill)
       depth 3 = --stack-frame-far           #444649
       between = linear, so d=1 -> #B8BABD, d=2 -> #7E8083
                 (exactly SKIN.md's "subtract 58 per channel per step")     */
  --stack-frame-near:  var(--accent-color-3, #F2F4F7);
  --stack-frame-far:   #444649;

  /* ---- type — TWO FACES EXIST ON DISK, NAME NO THIRD (SKIN.md §3) -----
     The serif is dead: By The Owl has no serif, so the headline's second word
     is a CASE change in the same face, not a typeface change. Anton is pinned
     by founder instruction to the accordion and the wordmark — this section is
     neither, so everything here is Inter.                                   */
  --stack-font:        var(--font-family-2, "Inter", system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif);
  --stack-font-display: var(--font-family-1, "Inter", system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif);
  --stack-font-accent: var(--font-family-2, "Inter", system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif);
  --stack-h-size:      clamp(37px, 6vw, 49px);   /* was 38.4->96px — the site
                                                    display tier is 49px      */
  --stack-h-leading:   var(--type-lh-display, 1);/* was 0.84 — negative
                                                    leading exists nowhere    */
  --stack-h-tracking:  var(--type-track-display, -0.028em);
  --stack-h-weight:    var(--type-weight-display, 400);
  --stack-eyebrow-size: var(--type-small, 14px);       /* was 15.2->22.4px   */
  --stack-eyebrow-lh:  var(--type-lh-small, 1.43);
  --stack-title-size:  clamp(25px, 3.4vw, 49px); /* card title — was 22->67px */
  --stack-ui-size:     var(--type-small, 14px);  /* chip + CTA — was 9.6-13.6 */
  --stack-ui-weight:   600;                      /* Runwai control            */

  /* ---- card ---------------------------------------------------------- */
  --card-aspect-w:     16;
  --card-aspect-h:     9;
  /* "A LITTLE CURVED." 16px is the system's own top step, not a new number.
     On a 720px card that is 2.2% of its width; the capability cards already
     shipping at 8px are ~400px wide, i.e. 2.0%. The same curve. 32px would
     have been double anything else By The Owl owns. SKIN.md §1.             */
  --card-radius:       var(--bto-radius-l, 16px);  /* was 0px                */
  /* Tightened from clamp(10px, 1.1vw, 16px): at wide viewports that drove the
     concentric inner radius to ZERO, so the media corner went square exactly
     where the card corner is most visible. 0.8vw keeps the inner value in the
     4-8px band, which is --bto-radius-s territory.                          */
  --card-frame:        clamp(8px, 0.8vw, 12px);
  --card-max-w:        720px;
  /* Scrim raised from rgba(0,0,0,.34): a white title over a blown-out frame
     computed 2.37:1 there, which FAILS 3:1 for large text. 0.46 is 3.46:1 —
     the least scrim that clears the bar. SKIN.md §2.                        */
  --card-scrim:        rgba(3, 3, 3, 0.46);

  /* ---- motion — "smoothness and slowness", as three numbers -----------
     SKIN.md §5. Three independent levers; they do different jobs and Ash will
     feel all three. The BEFORE value is on every line.                      */
  --stack-perspective:      1400px;  /* the ONLY source of the depth shrink   */
  --stack-peek-y:           40px;    /* upward offset per depth step          */
  --stack-depth-z:          120px;   /* z push-back per depth step            */
  --stack-max-depth:        3;       /* visible layers behind the front — the
                                        ladder above is built on this number  */
  --stack-exit-vh:          0.9;     /* exit travel, x viewport height        */
  /* SLOWNESS, lever 2 of 3. Was 3 (cubic). t^3 leaves the card at 12.5% of
     its travel at the halfway point and then dumps 87.5% in the back half —
     that is snap, the opposite of the ask. t^2 puts it at 25%: still an
     ease-in, so the card still accelerates away, but the first half is twice
     as present. The token's own comment already said "2 softer".            */
  --stack-exit-power:       2;
  --stack-fade-end:         0.95;    /* exit progress at which opacity hits 0 */
  --stack-fade-window:      0.25;    /* length of the fade, in exit progress  */
  /* SLOWNESS, lever 3 of 3 — the literal one. Was 1.2. A 1.33x multiplier:
     every card holds the screen a third longer, and a 5-card stack goes from
     5.8 to 7.4 viewport-heights. NOT 2.0 — that is nine screens of scroll for
     an uninterested visitor, which reads as a trap rather than as weight.    */
  --stack-scroll-per-card:  1.6;

  /* ---- THE ARRIVAL — added 2026-10-01 ---------------------------------
     "They should also have this continuous transition."

     HOW MUCH OF THE APPROACH the deck's fan-out occupies, as a fraction of
     one pin height — 1 = the whole viewport of scroll that already passes
     between this section's top edge entering the frame and it reaching the
     top and pinning. That scroll EXISTS TODAY and is spent sliding a
     finished, fully-fanned deck up the screen. Spending it on an arrival
     costs ZERO extra length: the section does not get one pixel longer.
     0 disables it and the deck behaves exactly as it did before.

     WHAT IT DOES: scales --stack-peek-y and --stack-depth-z together, so
     at the start of the approach every card sits in exactly the same place
     and the deck reads as ONE CARD, and by the time the pin engages it is
     the full pile. The section above it leaves as one white card
     contracting at the centre of the frame (assets/css/bto-seams.css), so
     the join is a card becoming a pile of cards — agreed by shape and
     place, with neither section reading the other's state. */
  --stack-entry:            1;
  /* SMOOTHNESS, lever 1 of 3. Was 0.085. The loop runs
     cur += (raw-cur) * (1 - e^(-dt/tau)); at 60fps tau=0.085 catches 17.8% of
     the gap per frame and lands in ~0.25s, tau=0.16 catches 9.9% and lands in
     ~0.48s. THAT is the weight — the deck arrives after you rather than with
     you. NOT 0.25: past ~0.20 the card is still moving a visible beat after
     the scroll has stopped, which reads as broken rather than as smooth.     */
  --stack-smoothing:        0.16;

  /* SECOND-ORDER, DELIBERATELY NOT APPLIED YET (SKIN.md §5, closing note).
     The Creative Director's instruction is to ship the three numbers above
     and let Ash FEEL them before anyone touches these. They are written out
     so the next pass is four edits and no re-derivation:
         --stack-exit-vh      0.9  -> 1.05   card fully clears frame
         --stack-fade-window  0.25 -> 0.34   a slower card is visible longer
         --stack-peek-y       40px -> 30px   tighten the deck as you slow it
         --stack-depth-z      120px -> 96px                                  */

  /* ---- layout -------------------------------------------------------- */
  --stack-gutter:      var(--bto-gutter, clamp(20px, 4vw, 44px));
  --stack-row-gap:     clamp(14px, 2.4vh, 34px);
  --stack-ease:        var(--bto-ease, cubic-bezier(.65, 0, .34, 1));
  --stack-dur:         var(--bto-dur-ui, 300ms);  /* UI transitions only,
                                                     never the scroll path    */

  /* Vertical room the eyebrow + headline + CTA + peek need. The card width is
     derived back from whatever height is left, so the whole composition always
     fits one screen. Raise it if you add a line of copy; lower it for a bigger
     card on short laptops. */
  --stack-chrome:      340px;
}

/* ==========================================================================
   SECTION
   ========================================================================== */

.wstack {
  position: relative;
  background: var(--stack-bg);
  color: var(--stack-ink);
  font-family: var(--stack-font);
  /* Not stacked (small screen, reduced motion, no JS): a plain tall section. */
  padding-block: clamp(56px, 9vw, 120px);
}

/* The scroll budget. Only ever applied when the stack is actually running, so
   a phone or a reduced-motion visitor never scrolls past empty space.
   --n is written by stack.js. */
.wstack.is-stacked {
  padding-block: 0;
  height: calc(100vh + (var(--n) - 1) * var(--stack-scroll-per-card) * 100vh);
}

.wstack__pin {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--stack-row-gap);
  padding-inline: var(--stack-gutter);
}

.wstack.is-stacked .wstack__pin {
  position: sticky;   /* <- the pin. Do not replace with JS. */
  top: 0;
  height: 100vh;
  overflow: clip;     /* an exiting card must not extend the page */
}

/* ==========================================================================
   HEADER + CTA
   ========================================================================== */

.wstack__eyebrow {
  margin: 0;
  max-width: 34ch;
  text-align: center;
  font-family: var(--stack-font-accent);
  font-size: var(--stack-eyebrow-size);
  line-height: var(--stack-eyebrow-lh);
  color: var(--stack-ink-dim);
  text-wrap: balance;
}

.wstack__h {
  margin: 0;
  text-align: center;
  font-family: var(--stack-font-display);
  font-size: var(--stack-h-size);
  font-weight: var(--stack-h-weight);
  line-height: var(--stack-h-leading);
  letter-spacing: var(--stack-h-tracking);
  text-transform: uppercase;
  text-wrap: balance;
}

/* The second word. In the reference this was a TYPEFACE change (sans -> serif);
   By The Owl has no serif, so it is a CASE change in the same face instead —
   the parent is uppercase, this is not. Still one colour, still one family.
   SKIN.md §3: "two faces exist on disk, name no third". */
.wstack__h em {
  font-family: var(--stack-font-accent);
  font-style: normal;
  font-weight: var(--stack-h-weight);
  text-transform: none;
  letter-spacing: var(--stack-h-tracking);
}

.wstack__cta {
  display: inline-flex;
  align-items: center;
  gap: 0.6em;
  padding: 0.72em 1.35em;
  border-radius: 999px;
  background: var(--stack-cta-bg);
  color: var(--stack-cta-ink);
  font-size: var(--stack-ui-size);
  font-weight: var(--stack-ui-weight);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  text-decoration: none;
  transition: transform var(--stack-dur, 300ms) var(--stack-ease),
              filter   var(--stack-dur, 300ms) var(--stack-ease);
}

.wstack__cta:hover  { transform: translateY(-2px); filter: brightness(1.06); }
.wstack__cta:active { transform: translateY(0); }
.wstack__cta svg    { width: 1em; height: 1em; flex: none; }

/* ==========================================================================
   DECK — the perspective root
   ========================================================================== */

.wstack__deck {
  position: relative;
  width: min(
    var(--card-max-w),
    100% ,
    /* never taller than the room we have: derive width back from height */
    calc((100vh - var(--stack-chrome, 340px)) * var(--card-aspect-w) / var(--card-aspect-h))
  );
  aspect-ratio: var(--card-aspect-w) / var(--card-aspect-h);
  margin-inline: auto;
  /* reserve the room the peeking cards need above the front one */
  margin-top: calc(var(--stack-peek-y) * min(var(--n) - 1, var(--stack-max-depth)));
  perspective: var(--stack-perspective);
  transform-style: preserve-3d;
}

/* ==========================================================================
   CARD
   ========================================================================== */

.wstack__card {
  position: absolute;
  inset: 0;
  display: block;
  /* The frame you see is this background showing through the padding — there
     is no border. It is ALWAYS the near end of the ladder; the darkening for
     depth is done by .wstack__shade over the top, so nothing here repaints. */
  background: var(--stack-frame-near);
  padding: var(--card-frame);                       /* THIS is the "border" */
  border-radius: var(--card-radius);
  color: var(--stack-ink);
  text-decoration: none;
  transform-style: preserve-3d;
  /* backface + contain keep the compositor honest with many layers */
  backface-visibility: hidden;
}

/* will-change is added by stack.js only while the loop runs, and removed
   the moment the section leaves the viewport. */
.wstack.is-running .wstack__card { will-change: transform, opacity; }

/* THE DEPTH SHADE — how five cards stay separable without a single hue.
   A solid `--stack-frame-far` sheet over the whole card (inset:0 resolves
   against the PADDING box, so it covers the coloured frame as well as the
   media) sitting UNDER .wstack__media in paint order. stack.js writes only
   its opacity: 0 at the front, 1 at --stack-max-depth, linear between.

   WHY AN OVERLAY AND NOT `el.style.backgroundColor` PER FRAME, which is what
   SKIN.md §2 suggests: a background-color write invalidates the card's
   compositor layer and re-rasterises ~300k px, five times a frame. Opacity on
   its own layer is a compositor property and paints nothing. The arithmetic
   is identical — it is a linear blend between the same two endpoints — so the
   ladder lands on §2's exact hexes (#F2F4F7 / #B8BABD / #7E8083 / #444649)
   while the hot path stays transform + opacity, which §6.4 requires. */
.wstack__shade {
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: var(--stack-frame-far);
  opacity: 0;
  pointer-events: none;
}

/* Promote the sheet so its opacity is composited, not painted. Toggled with
   the loop by .is-running, exactly like the cards — never permanent. */
.wstack.is-running .wstack__shade { will-change: opacity; }

.wstack__media {
  position: relative;   /* paints ABOVE .wstack__shade — DOM order, both z:auto */
  width: 100%;
  height: 100%;
  overflow: hidden;
  border-radius: calc(var(--card-radius) - var(--card-frame));
  background: #000;
}

.wstack__media > img,
.wstack__media > video {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.wstack__scrim {
  position: absolute;
  inset: 0;
  background: linear-gradient(to top, var(--card-scrim) 0%, rgba(0,0,0,0) 52%);
  pointer-events: none;
}

.wstack__tag {
  position: absolute;
  top: clamp(10px, 1.4vw, 18px);
  left: clamp(10px, 1.4vw, 18px);
  padding: 0.5em 0.9em;
  border-radius: 999px;
  background: var(--stack-ink);
  color: var(--stack-bg);
  font-size: var(--stack-ui-size);
  font-weight: var(--stack-ui-weight);
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.wstack__title {
  position: absolute;
  left: 0;
  right: 0;
  bottom: clamp(12px, 2.4vw, 30px);
  margin: 0;
  padding-inline: 0.5em;
  text-align: center;
  font-family: var(--stack-font-display);
  font-size: var(--stack-title-size);
  font-weight: var(--stack-h-weight);
  line-height: var(--type-lh-display, 1);
  letter-spacing: var(--stack-h-tracking);
  text-transform: uppercase;
  text-wrap: balance;
}

/* Focus must stay visible even though cards overlap in 3D. */
.wstack__card:focus-visible {
  outline: 3px solid var(--stack-cta-bg);
  outline-offset: 4px;
}

/* ==========================================================================
   FALLBACK — small screens, short screens, reduced motion, no JS.
   The reference does the same thing below 991px: a plain vertical list.
   Every project stays readable; nothing is stranded behind another card.
   ========================================================================== */

.wstack:not(.is-stacked) .wstack__deck {
  width: min(var(--card-max-w), 100%);
  aspect-ratio: auto;
  margin-top: 0;
  perspective: none;
  display: grid;
  gap: clamp(16px, 4vw, 30px);
}

.wstack:not(.is-stacked) .wstack__card {
  position: relative;
  inset: auto;
  aspect-ratio: var(--card-aspect-w) / var(--card-aspect-h);
  /* clear anything the engine may have written before the breakpoint flipped */
  transform: none !important;
  opacity: 1 !important;
  visibility: visible !important;
}

/* Every card in the flat list is a FRONT card — there is no depth to read, so
   the shade must be off. It is `!important` for the same reason the three
   above are: the engine may have written an inline opacity onto it before the
   breakpoint flipped, and nothing would ever clear it. */
.wstack:not(.is-stacked) .wstack__shade { opacity: 0 !important; }

@media (prefers-reduced-motion: reduce) {
  .wstack__cta { transition: none; }
  .wstack__cta:hover { transform: none; }
}

/* --------------------------------------------------------------------------
   THE CASE CARD · 2026-10-12 (BTO-0004) · diverged from the lab on purpose

   Founder, on Brand films & ads and on Events & artist films: Selected work
   is this stack, each card "more of a case study": the words on one side, two
   photos on the other, the sides swapping from card to card. Then, on the
   first cut: "it still misses the polishing side … it looks very AI and
   rough."

   The cards are printed by tools/build-capabilities.mjs and adopted by the
   engine (see the header of bto-scroll-stack.js). This block is the whole
   card: its box, its words (.wcase) and its pictures (.wcase__media). Nothing
   here is borrowed from another sheet, so another page's work cannot move it.

   A homepage card has none of these classes and none of this matches it.

   WHAT THE POLISH PASS CHANGED, AND WHY EACH RULE IS THE WAY IT IS:

   · THE CLIENT IS THE HEADLINE. It is the one specific thing a card says.
   · THE WORDS ARE ANCHORED, NOT CENTRED. The index sits on the top edge, the
     name and the facts on the bottom edge, and the space between them is the
     card breathing rather than a gap nobody chose.
   · THE CARD IS A THIN FRAME ROUND ITS PICTURES. The pictures get the room;
     the words get their own margin inside their half.
   · EDGES ARE HAIRLINES. A dark still on a black card had no edge at all.
     One inset line on the card and on each frame; this site has no shadows.

   THE STACK ITSELF IS THE HOMEPAGE'S, UNTUNED. The founder, over a screenshot
   of the homepage deck: "Use this particular asset … just utilize the space
   or make it a bit bigger, but I want this. This is smoother." So this block
   sets no motion token, no pile colour and no spacing above the deck: the
   pace, the grey ladder of cards behind, the room for them and the button
   under the deck are all the homepage's. A first polish pass darkened the
   pile, closed the gap above the deck and put a dot grid behind it; all three
   were taken back out for that reason.

   ONLY THE CARD IS BIGGER, because it holds a case study and not one picture:
   up to 1080 wide at 16 : 7.6, against the homepage's 720 at 16 : 9. It still
   shrinks to fit the window through the deck's own formula.

   TWO PHOTO ARRANGEMENTS, CHOSEN FROM THE PICTURES by the generator: upright
   stills stand side by side; wide stills sit one above the other.
   -------------------------------------------------------------------------- */
.wstack--case {
  --card-max-w:     1080px;
  --card-aspect-h:  7.6;
  --stack-chrome:   300px;                      /* heading, pile, button: 242 measured */
  --case-pad:       clamp(10px, 1.1vw, 16px);   /* the frame round the pictures */
  --case-gap:       clamp(10px, 1.4vw, 20px);
  --case-text-pad:  clamp(14px, 1.7vw, 24px);   /* the words' own margin */
  --case-dim:       rgba(255, 255, 255, 0.62);
  --case-soft:      rgba(255, 255, 255, 0.80);
  --case-line:      rgba(255, 255, 255, 0.16);
  --case-rim:       rgba(255, 255, 255, 0.14);
}
/* ---- the box ------------------------------------------------------------ */
.wstack__card--case {
  display: grid;
  grid-template-columns: minmax(0, 1.5fr) minmax(0, 1fr);
  gap: var(--case-gap);
  align-items: stretch;
  padding: var(--case-pad);
  background: var(--primary, #030303);
  color: var(--on-dark, #FFFFFF);
}
.wstack__card--case.wstack__card--media-first { grid-template-columns: minmax(0, 1fr) minmax(0, 1.5fr); }
.wstack__card--case.wstack__card--media-first > .wcase__media { order: -1; }
/* upright stills need the photo side to be a full half */
.wstack__card--case.wstack__card--upright { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
/* one still: it takes the larger share */
.wstack__card--case.wstack__card--solo { grid-template-columns: minmax(0, 1fr) minmax(0, 1.4fr); }
.wstack__card--case.wstack__card--solo.wstack__card--media-first { grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr); }
.wstack__card--case.wstack__card--solo.wstack__card--upright { grid-template-columns: minmax(0, 2.2fr) minmax(0, 1fr); }
.wstack__card--case.wstack__card--solo.wstack__card--upright.wstack__card--media-first { grid-template-columns: minmax(0, 1fr) minmax(0, 2.2fr); }
.wstack__card--case.wstack__card--textonly { grid-template-columns: minmax(0, 1fr); }

/* one inset hairline: the card's edge, and each picture's */
.wstack__card--case::after,
.wcase__frame::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  box-shadow: inset 0 0 0 1px var(--case-rim);
  pointer-events: none;
}

/* ---- the words ---------------------------------------------------------- */
.wstack__card--case > .wcase {
  position: relative;            /* above the depth sheet */
  min-width: 0;
  min-height: 0;
  display: flex;
  flex-direction: column;
  padding: var(--case-text-pad);
  overflow: hidden;
}
/* every text rule below is two classes deep: the type layer styles bare
   p, h3, a, dt and dd at (0,1,1) */
.wstack--case .wcase__top {
  display: flex;
  align-items: center;
  gap: 14px;
  margin: 0;
  max-width: none;
  font-family: var(--stack-font);
  font-size: var(--type-micro, 13px);
  font-weight: 600;
  line-height: 1;
  letter-spacing: 0.04em;
  font-variant-numeric: tabular-nums;
  color: var(--on-dark, #FFFFFF);
}
.wcase__rule { flex: 1 1 auto; height: 1px; background: var(--case-line); }

.wstack--case .wcase__client {
  margin: auto 0 0;              /* pushes the name and the facts to the foot */
  padding-top: 20px;
  max-width: none;
  font-family: var(--stack-font-display);
  font-size: var(--type-heading, 37px);
  font-weight: 400;
  line-height: 1.04;
  letter-spacing: var(--type-track-display, -0.028em);
  text-transform: none;
  text-wrap: balance;
  color: inherit;
}
.wstack--case .wcase__line {
  margin: 12px 0 0;
  max-width: 46ch;
  font-family: var(--stack-font);
  font-size: var(--type-body, 16px);
  line-height: 1.5;
  letter-spacing: 0;
  color: var(--case-soft);
}
.wstack--case .wcase__facts {
  margin: clamp(14px, 1.8vw, 22px) 0 0;
  padding: 0;
  border-top: 1px solid var(--case-line);
}
.wstack--case .wcase__row {
  display: grid;
  grid-template-columns: minmax(0, 10.5em) minmax(0, 1fr);
  gap: 12px;
  padding: 9px 0;
  border-bottom: 1px solid var(--case-line);
}
.wstack--case .wcase__row dt,
.wstack--case .wcase__row dd {
  margin: 0;
  font-family: var(--stack-font);
  font-size: var(--type-small, 14px);
  line-height: 1.45;
  letter-spacing: 0;
}
.wstack--case .wcase__row dt { font-weight: 400; color: var(--case-dim); }
.wstack--case .wcase__row dd { font-weight: 500; color: var(--on-dark, #FFFFFF); }

/* ---- the pictures ------------------------------------------------------- */
.wstack__card--case > .wcase__media {
  position: relative;            /* above the depth sheet */
  min-width: 0;
  min-height: 0;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: minmax(0, 1fr) minmax(0, 1fr);
  gap: var(--case-pad);
}
.wstack__card--case.wstack__card--upright > .wcase__media {
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  grid-template-rows: minmax(0, 1fr);
}
.wstack__card--case.wstack__card--solo > .wcase__media {
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: minmax(0, 1fr);
}
.wcase__frame {
  position: relative;
  margin: 0;
  min-width: 0;
  min-height: 0;
  overflow: hidden;
  border-radius: var(--bto-radius-m, 8px);
  background: var(--accent-color-2, #1A1A1A);
}
.wcase__frame img {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* ---- the flat list: small screens, short windows, reduced motion, no script.
   The card is as tall as its content, so the frames take a shape of their own
   and the name no longer has a foot to be pushed to. ------------------------ */
.wstack:not(.is-stacked) .wstack__card--case { aspect-ratio: auto; }
.wstack:not(.is-stacked) .wstack__card--case > .wcase { overflow: visible; }
.wstack--case:not(.is-stacked) .wcase__client { margin-top: clamp(28px, 5vw, 64px); padding-top: 0; }
.wstack:not(.is-stacked) .wstack__card--case > .wcase__media { grid-template-rows: none; grid-auto-rows: auto; align-content: center; }
.wstack:not(.is-stacked) .wcase__frame { aspect-ratio: 16 / 9; }
.wstack:not(.is-stacked) .wstack__card--upright .wcase__frame { aspect-ratio: 4 / 5; }

@media (max-width: 899.98px) {
  /* one column: the pictures first, side by side, then the words */
  .wstack__card--case,
  .wstack__card--case.wstack__card--media-first,
  .wstack__card--case.wstack__card--upright,
  .wstack__card--case.wstack__card--solo,
  .wstack__card--case.wstack__card--solo.wstack__card--media-first,
  .wstack__card--case.wstack__card--solo.wstack__card--upright,
  .wstack__card--case.wstack__card--solo.wstack__card--upright.wstack__card--media-first { grid-template-columns: minmax(0, 1fr); }
  .wstack__card--case > .wcase__media { order: -1; }
  .wstack:not(.is-stacked) .wstack__card--case > .wcase__media { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
  .wstack:not(.is-stacked) .wstack__card--solo > .wcase__media { grid-template-columns: minmax(0, 1fr); }
  .wstack:not(.is-stacked) .wcase__frame { aspect-ratio: 4 / 3; }
  .wstack:not(.is-stacked) .wstack__card--upright .wcase__frame { aspect-ratio: 4 / 5; }
  .wstack:not(.is-stacked) .wstack__card--solo .wcase__frame { aspect-ratio: 16 / 9; }
  .wstack--case:not(.is-stacked) .wcase__client { margin-top: 28px; }
}
@media (max-width: 575.98px) {
  .wstack--case .wcase__client { font-size: var(--type-heading-sm, 25px); line-height: 1.12; }
  .wstack--case .wcase__row { grid-template-columns: minmax(0, 8.5em) minmax(0, 1fr); }
  /* two wide stills side by side are thumbnails on a phone: one above the
     other, each a full 16:9. Upright pairs stay side by side. */
  .wstack:not(.is-stacked) .wstack__card--case:not(.wstack__card--upright) > .wcase__media { grid-template-columns: minmax(0, 1fr); }
  .wstack:not(.is-stacked) .wstack__card--case:not(.wstack__card--upright) .wcase__frame { aspect-ratio: 16 / 9; }
}
/* the list takes the page's own section rhythm, and its heading the page's
   own distance from what it heads */
.wstack--case:not(.is-stacked) { padding-block: var(--bto-section-space, clamp(72px, 8vw, 132px)); }
.wstack--case:not(.is-stacked) .wstack__pin { gap: var(--bto-content-gap, 40px); }
