/* ==========================================================================
   STONEGRAPH — the studio page
   --------------------------------------------------------------------------
   A port of the Zayne Forge studio page from the Aksutra platform: same
   structure, same rhythm, same furniture — a dark cinematic hero, mono
   eyebrows over large light serif headlines with an italic accent line, warm
   cards on a cream ground, numbered principles, a metric row, a timeline, an
   alternating department split, a rotating globe, and a centred contact.

   Two things are deliberately NOT copied.

   The palette is Stonegraph's own. The original runs cream and a gold clay
   (#D4AF6A) on near-black; this runs the site's existing sand, marble, ink and
   --accent-dark, so the page belongs to the rest of the site rather than
   looking like a page borrowed from somewhere else. The variables are the same
   ones every other stylesheet here reads.

   And nothing is React. The original is Next + Tailwind + framer-motion; this
   is one stylesheet and the site's own .reveal system, because the page it has
   to live inside is static HTML rewritten on the way out. Every entrance
   animation below is a class site.js already knows how to drive.
   ========================================================================== */

/* THE ACCENT IS TWO COLOURS, NOT ONE, AND THE GROUND DECIDES WHICH.
   --------------------------------------------------------------------------
   This file had a single `--stu-gold: var(--accent-dark)`. --accent-dark is
   #C2AA7E, and the palette in site.css says exactly what it is for: "7.7:1 on
   basalt". It is the gold for the DARK hero.

   Every other section on this page is cream — --sand, --sand-2, --marble — and
   the same gold was being used for text on all of them. Measured on the page:
   the italic accent line of every heading came out at 1.54:1, the metric
   figures at 1.99:1, the numbered principles at 1.47:1. The floor for large
   display text is 3:1. So "Two disciplines, ONE BENCH.", "What has come off
   THE BENCHES.", "Crafted in Jaipur. ADMIRED WORLDWIDE." — the accent half of
   every headline on the page — was a pale wash on a pale ground. Indoors on a
   laptop you can just about read it; on a phone in daylight it is not there.
   That is the "text is not visible properly" report.

   The palette already carries the right colour for a light ground: --accent,
   #6F5A3C, documented as "4.6:1 on sand". Nothing new is introduced and no
   design decision is reversed — the accent is still the same warm bronze, in
   the correct one of its two values for the ground it is standing on.

   Written as a variable the hero overrides rather than as a second class, so
   the twenty-one rules below did not have to be sorted into light and dark by
   hand and cannot drift apart later. */
.stu {
  --stu-gold-lite: var(--accent-dark, #C2AA7E);   /* on the dark hero */
  --stu-gold: var(--accent, #6F5A3C);             /* on the cream ground */
  --stu-dark: #141413;
  --stu-card: rgba(255, 253, 247, .62);
}
.stu-hero { --stu-gold: var(--stu-gold-lite); }

/* ==========================================================================
   1. HERO — full height, dark, one photograph behind everything
   ========================================================================== */
.stu-hero {
  position: relative;
  overflow: hidden;
  background: var(--stu-dark);
  min-height: 100svh;
  display: flex;
  flex-direction: column;
  isolation: isolate;
}
/* The hero reel.

   `.hero__film` is the homepage's shell and it is sized for the homepage — a
   panel down the right-hand side, `inset: 0 0 0 auto; width: min(72vw,1080px)`.
   This hero is full bleed, so the geometry is taken back here while everything
   else that class carries — the object-fit, the focal point, the poster's
   is-hidden rule, and the whole of site.js's loader — is inherited unchanged. */
.stu-hero__media,
.stu-hero.stu-hero .hero__film {
  position: absolute;
  /* Overscan, top and bottom, not inset: 0.

     studio.js drifts this shell up to 110px as the hero is scrolled past. At
     inset: 0 the shell is exactly the hero's size, so translating it by any
     amount pulls a strip of empty ground into view at one edge — the parallax
     would have been paid for with a black band. 130px of overscan each way is
     more than the drift can ever use. */
  inset: -130px 0;
  width: auto;
  /* height: auto, and it has to be said out loud.

     On a phone components.css gives `.hero__film` a fixed
     `height: clamp(310px, 52vh, 460px)` — the homepage's treatment, where the
     reel is a strip across the top of the band rather than the whole of it.
     That rule won here, so the shell came out 422px tall inside an 812px hero
     and the film covered half the screen. The inset above only sets where the
     box starts and ends; without this the height overrides both. */
  height: auto;
  z-index: 0;
  overflow: hidden;
  pointer-events: none;
}
.stu-hero__media video,
.stu-hero__media img {
  width: 100%; height: 100%;
  object-fit: cover;
  object-position: center 42%;
  display: block;
  /* The video is its own layer too, so the layers above it composite over the
     top rather than forcing it to be re-rasterised with them. */
  transform: translateZ(0);
  backface-visibility: hidden;
}

/* ---- and the shell stops transforming once it has arrived ----
   A scale on an element containing video makes the browser re-rasterise every
   frame of that video at the new size, continuously. It is affordable for the
   two seconds of the settle and it is not affordable forever, so studio.js
   drops the class the moment the settle ends and the shell goes back to a
   plain translate for the parallax — which the compositor does for free. */
.stu-hero__media { will-change: auto; }
.stu-hero__media.is-settling { will-change: transform; }
.stu-hero__media.is-flat { transform: translate3d(0, var(--hp, 0px), 0) !important; }
@media (max-width: 759px) {
  /* The portrait cut is already framed for this shape, so it is shown whole
     rather than cropped a second time by the object-position above. */
  .stu-hero__media video,
  .stu-hero__media img { object-position: center center; }
}
.stu-hero__media img {
  width: 100%; height: 100%;
  object-fit: cover;
  object-position: center 38%;
  display: block;
}
/* Two washes rather than one: a vertical gradient for legibility top and
   bottom, and a flat veil so the type never sits directly on a highlight. */
.stu-hero__veil {
  position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background:
    linear-gradient(to bottom, rgba(10,10,10,.55) 0%, rgba(10,10,10,.28) 38%, rgba(10,10,10,.72) 100%),
    rgba(10, 10, 10, .18);
}
/* The dot grid from the original, at the same 32px pitch. */
.stu-hero__grid {
  position: absolute; inset: 0; z-index: 2; pointer-events: none;
  opacity: .28;
  background-image: radial-gradient(rgba(255,255,255,.08) 1px, transparent 1px);
  background-size: 32px 32px;
}
@media (max-width: 640px) { .stu-hero__grid { display: none; } }

/* ---- the status pill ---- */
.stu-hero__status {
  position: absolute; left: 0; right: 0; top: 96px; z-index: 5;
  display: flex; justify-content: center; padding: 0 24px;
}
.stu-pill {
  display: inline-flex; align-items: center; gap: 9px;
  border: 1px solid rgba(255,255,255,.15);
  background: rgba(255,255,255,.03);
  border-radius: 999px;
  padding: 7px 15px;
  font-size: .75rem;
  letter-spacing: .02em;
  color: rgba(240,238,230,.8);
}
.stu-pill__dot { position: relative; display: inline-flex; width: 6px; height: 6px; flex: none; }
.stu-pill__dot::before,
.stu-pill__dot::after {
  content: ""; position: absolute; inset: 0; border-radius: 999px; background: var(--stu-gold);
}
.stu-pill__dot::before { animation: stu-ping 2.4s cubic-bezier(0,0,.2,1) infinite; opacity: .7; }
@keyframes stu-ping { 75%, 100% { transform: scale(2.6); opacity: 0; } }

/* ---- the centred content ---- */
.stu-hero__inner {
  position: relative; z-index: 4;
  flex: 1;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  text-align: center;
  padding: 140px 24px 120px;
}
.stu-eyebrow-row {
  display: flex; align-items: center; justify-content: center; gap: 14px;
  margin-bottom: 34px;
}
.stu-eyebrow-row .stu-rule { width: 26px; height: 1px; background: rgba(255,255,255,.25); }
.stu-eyebrow-row .stu-key {
  font-family: var(--f-mono, ui-monospace, monospace);
  font-size: .68rem; text-transform: uppercase; letter-spacing: .32em;
  color: rgba(240,238,230,.6);
}
.stu-hero__h1 {
  font-family: var(--f-display, Georgia, serif);
  font-weight: 300;
  font-size: clamp(1.75rem, 5.5vw, 5rem);
  line-height: 1.06;
  letter-spacing: -.02em;
  color: #FFFDF7;
  margin: 0;
}
/* The accent lines are italic and gold in the original, and that is the single
   strongest signature of the layout — kept exactly. */
.stu-hero__h1 em { font-style: italic; color: var(--stu-gold); }
.stu-hero__sub {
  margin-top: 30px;
  font-family: var(--f-mono, ui-monospace, monospace);
  font-size: clamp(.72rem, 1.1vw, .84rem);
  text-transform: uppercase;
  letter-spacing: .2em;
  color: var(--stu-gold);
}
.stu-hero__act { margin-top: 42px; }

/* The pill button: white on dark, gold on hover, exactly as the original. */
.stu-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 9px;
  border-radius: 999px;
  border: 0;
  padding: 14px 26px;
  font-size: .88rem;
  font-weight: 500;
  cursor: pointer;
  background: #FFFDF7;
  color: #141413;
  text-decoration: none;
  transition: background-color .35s var(--ease), color .35s var(--ease), transform .35s var(--ease);
}
/* The pill fills with the LIGHT gold on hover, on both grounds: the label
   inside it is #141413, and near-black needs the pale value under it. */
.stu-btn:hover { background: var(--stu-gold-lite); color: #141413; }
.stu-btn svg { transition: transform .35s var(--ease); }
.stu-btn:hover svg { transform: translateX(3px); }
.stu-btn--ink { background: var(--ink); color: var(--sand); }
.stu-btn--ink:hover { background: var(--stu-gold-lite); color: #141413; }

/* ---- the bottom signature ---- */
.stu-hero__sig {
  position: absolute; bottom: 40px; left: 0; right: 0; z-index: 4;
  display: flex; flex-direction: column; align-items: center; gap: 12px;
  pointer-events: none;
}
.stu-hero__sig p {
  font-family: var(--f-mono, ui-monospace, monospace);
  font-size: .62rem; text-transform: uppercase; letter-spacing: .3em;
  color: rgba(240,238,230,.5);
  margin: 0;
}
.stu-hero__tick {
  width: 1px; height: 32px;
  background: linear-gradient(to bottom, var(--stu-gold), transparent);
}
@media (max-width: 640px) { .stu-hero__sig { display: none; } }

/* ==========================================================================
   2. SECTIONS — the shared furniture
   ========================================================================== */
.stu-sec { position: relative; overflow: hidden; padding: clamp(72px, 11vw, 160px) 0; }
.stu-sec--tint { background: var(--sand-2); border-block: 1px solid var(--rule); }
.stu-wrap { position: relative; width: min(1180px, 100% - 48px); margin-inline: auto; }
.stu-wrap--narrow { width: min(980px, 100% - 48px); }

/* The soft radial the original puts behind its tinted sections. */
.stu-glow {
  position: absolute; inset: 0; z-index: 0; pointer-events: none;
  background: radial-gradient(ellipse 80% 50% at 50% 0%, rgba(194,170,126,.16) 0%, transparent 70%);
}

.stu-head { margin-bottom: clamp(40px, 5vw, 66px); }
.stu-head--split {
  display: flex; flex-direction: column; gap: 22px;
}
@media (min-width: 860px) {
  .stu-head--split { flex-direction: row; align-items: flex-end; justify-content: space-between; }
  .stu-head--split .stu-head__aside { max-width: 27rem; }
}
.stu-head--centre { text-align: center; }
.stu-head--centre .stu-key-row { justify-content: center; }

.stu-key-row { display: flex; align-items: center; gap: 12px; margin-bottom: 14px; }
.stu-key {
  font-family: var(--f-mono, ui-monospace, monospace);
  font-size: .68rem; text-transform: uppercase; letter-spacing: .3em;
  color: var(--stu-gold);
  margin: 0;
}
.stu-h2 {
  font-family: var(--f-display, Georgia, serif);
  font-weight: 300;
  font-size: clamp(2rem, 4.5vw, 3.25rem);
  line-height: 1.05;
  letter-spacing: -.02em;
  color: var(--ink);
  margin: 0;
}
.stu-h2--big { font-size: clamp(2rem, 5vw, 3.5rem); }
.stu-h2 em { font-style: italic; color: var(--stu-gold); }
.stu-head__aside {
  font-size: .95rem; font-weight: 300; line-height: 1.7; color: var(--ink-mute);
  margin: 0;
}
.stu-lede {
  margin: 24px auto 0; max-width: 36rem;
  font-size: .95rem; font-weight: 300; line-height: 1.7; color: var(--ink-mute);
}

/* ---- the warm card, used everywhere ---- */
.stu-card {
  position: relative;
  border-radius: 16px;
  border: 1px solid var(--rule);
  background: var(--stu-card);
  padding: clamp(22px, 2.4vw, 32px);
  overflow: hidden;
  transition: border-color .5s var(--ease), background-color .5s var(--ease);
}
.stu-card::before {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  opacity: 0; transition: opacity .5s var(--ease);
  background: radial-gradient(ellipse 60% 50% at 50% 0%, rgba(194,170,126,.14) 0%, transparent 70%);
}
.stu-card:hover { border-color: rgba(194,170,126,.45); }
.stu-card:hover::before { opacity: 1; }
.stu-card__t {
  font-family: var(--f-display, Georgia, serif);
  font-weight: 400;
  font-size: clamp(1.1rem, 1.7vw, 1.4rem);
  letter-spacing: -.01em;
  color: var(--ink);
  margin: 0;
}
.stu-card__d {
  margin: 12px 0 0;
  font-size: .92rem; font-weight: 300; line-height: 1.65; color: var(--ink-mute);
}

.stu-grid3 { display: grid; gap: 18px; }
.stu-grid2 { display: grid; gap: 18px; }
@media (min-width: 860px) {
  .stu-grid3 { grid-template-columns: repeat(3, 1fr); }
  .stu-grid2 { grid-template-columns: repeat(2, 1fr); }
}

/* The icon plate on a capability card. */
.stu-ico {
  display: inline-flex; align-items: center; justify-content: center;
  width: 46px; height: 46px; margin-bottom: 20px;
  border-radius: 12px;
  background: rgba(194,170,126,.12);
  box-shadow: inset 0 0 0 1px rgba(194,170,126,.2);
  color: var(--stu-gold);
}

/* ---- the bordered differentiator panel ---- */
.stu-panel {
  margin-top: clamp(34px, 4vw, 60px);
  border: 1px solid var(--rule);
  border-radius: 16px;
  background: var(--marble);
  padding: clamp(24px, 3.4vw, 48px);
}
.stu-panel__grid { display: grid; gap: 26px; }
@media (min-width: 860px) { .stu-panel__grid { grid-template-columns: repeat(2, 1fr); gap: 34px; } }
.stu-panel p {
  margin: 0 0 14px;
  font-size: .9rem; font-weight: 300; line-height: 1.7; color: var(--ink-mute);
}
.stu-panel p:last-child { margin-bottom: 0; }
.stu-h3 {
  font-family: var(--f-display, Georgia, serif);
  font-weight: 300;
  font-size: clamp(1.25rem, 2.5vw, 1.75rem);
  line-height: 1.2; letter-spacing: -.02em; color: var(--ink);
  margin: 0;
}
.stu-h3 em { font-style: italic; color: var(--stu-gold); }

/* ==========================================================================
   3. PRINCIPLES — a number in the margin, the principle beside it
   ========================================================================== */
.stu-principles { display: grid; gap: clamp(44px, 6vw, 84px); }
.stu-prin { display: grid; gap: 14px; }
@media (min-width: 860px) {
  .stu-prin { grid-template-columns: 2fr 10fr; gap: 40px; }
}
.stu-prin__n {
  font-family: var(--f-mono, ui-monospace, monospace);
  font-size: .84rem; font-weight: 500; letter-spacing: .2em;
  /* Was rgba(194,170,126,.7) — the dark-ground gold, written as a literal, so
     it escaped the variable above. On sand it measured 1.47:1, which made the
     numerals 01 to 04 beside each principle a texture rather than a number.
     The alpha goes with it: at 13px this is body-sized text and needs the
     4.5:1 floor, which the flat colour clears at 5.1:1. */
  color: var(--stu-gold);
}
.stu-prin__t {
  font-family: var(--f-display, Georgia, serif);
  font-weight: 300;
  font-size: clamp(1.5rem, 3vw, 2rem);
  line-height: 1.15; letter-spacing: -.02em; color: var(--ink);
  margin: 0;
}
.stu-prin__b {
  margin: 16px 0 0; max-width: 44rem;
  font-size: 1rem; font-weight: 300; line-height: 1.72; color: var(--ink-mute);
}
.stu-closing {
  margin-top: clamp(46px, 6vw, 90px);
  padding-top: clamp(30px, 3.4vw, 48px);
  border-top: 1px solid var(--rule);
  text-align: center;
}
.stu-closing p {
  margin: 0 auto; max-width: 40rem;
  font-family: var(--f-display, Georgia, serif);
  font-weight: 300; font-style: italic;
  font-size: clamp(1.2rem, 2.5vw, 1.7rem);
  line-height: 1.42; letter-spacing: -.02em;
  color: var(--ink-soft);
}

/* ==========================================================================
   4. METRICS AND THE TIMELINE
   ========================================================================== */
.stu-metric { border: 1px solid var(--rule); border-radius: 16px; background: var(--marble); padding: clamp(22px, 2.6vw, 34px); }
.stu-metric__v {
  font-family: var(--f-display, Georgia, serif);
  font-weight: 300; font-size: clamp(2.4rem, 5vw, 3.4rem); line-height: 1;
  color: var(--stu-gold); margin: 0;
}
.stu-metric__l { margin: 12px 0 0; font-size: .92rem; font-weight: 500; letter-spacing: -.01em; color: var(--ink); }
.stu-metric__t { margin: 8px 0 0; font-size: .82rem; font-weight: 300; line-height: 1.6; color: var(--ink-mute); }

.stu-track { position: relative; margin-top: clamp(46px, 6vw, 84px); }
/* The rule the markers sit on. Pulled to the marker's own centre so the line
   runs through them rather than beside them. */
.stu-track::before {
  content: ""; position: absolute; top: 0; bottom: 0; left: 7px; width: 1px;
  background: var(--rule);
}
@media (min-width: 860px) { .stu-track::before { left: calc(16.666% - .5px); } }
.stu-areas { display: grid; gap: clamp(34px, 4.4vw, 64px); }
.stu-area { display: grid; gap: 12px; }
@media (min-width: 860px) { .stu-area { grid-template-columns: 2fr 10fr; gap: 40px; } }
.stu-area__mark { position: relative; }
.stu-area__mark::before {
  content: ""; position: absolute; left: 0; top: 7px;
  width: 15px; height: 15px; border-radius: 999px;
  background: var(--stu-gold);
  box-shadow: 0 0 0 4px var(--sand-2);
}
@media (min-width: 860px) { .stu-area__mark::before { left: auto; right: -7.5px; } }
/* On a phone the marker used to occupy a collapsed grid row. Because the dot
   itself is absolutely positioned, the following heading then began in that
   same row and the dot printed over its first letters. Give the timeline a
   real marker column: the line and dot keep their original axis while every
   heading receives a stable, readable text measure. The extra selector is
   intentional; it keeps this Studio rule authoritative over the later shared
   small-screen parity sheet without changing the desktop composition. */
@media (max-width: 859px) {
  .stu-track .stu-area {
    grid-template-columns: 16px minmax(0, 1fr);
    gap: clamp(16px, 4.5vw, 22px);
    align-items: start;
  }
  .stu-area__mark { min-height: 22px; }
  .stu-area > :last-child { min-width: 0; }
}
.stu-area__t {
  font-family: var(--f-display, Georgia, serif);
  font-weight: 300; font-size: clamp(1.2rem, 2.5vw, 1.7rem);
  line-height: 1.2; letter-spacing: -.02em; color: var(--ink); margin: 0;
}
.stu-area__b { margin: 12px 0 0; max-width: 42rem; font-size: .95rem; font-weight: 300; line-height: 1.7; color: var(--ink-mute); }

/* ==========================================================================
   5. DEPARTMENTS — alternating split
   ========================================================================== */
.stu-depts { display: grid; gap: clamp(54px, 8vw, 128px); }
.stu-dept { display: grid; gap: 26px; align-items: center; }
@media (min-width: 860px) {
  .stu-dept { grid-template-columns: repeat(2, 1fr); gap: 64px; }
  .stu-dept--flip .stu-dept__fig { order: 2; }
  .stu-dept--flip .stu-dept__txt { order: 1; }
}
.stu-dept__t {
  font-family: var(--f-display, Georgia, serif);
  font-weight: 300; font-size: clamp(1.5rem, 3vw, 2.25rem);
  line-height: 1.15; letter-spacing: -.02em; color: var(--ink); margin: 0;
}
.stu-dept__b { margin: 16px 0 0; font-size: 1rem; font-weight: 300; line-height: 1.72; color: var(--ink-mute); }

/* ==========================================================================
   THE DEPARTMENT PLATE
   --------------------------------------------------------------------------
   The Zayne Forge page puts a dark, near-black card beside each department:
   concentric rings radiating from the centre, a small line-art glyph at the
   middle in the accent, and a faint warm bloom behind it. It is a diagram of
   nothing in particular — its job is to give the writing a visual partner
   without pretending to be a photograph of a room nobody has photographed.

   That is exactly the right answer here too, and for exactly that reason:
   there is no picture in this library of a drawing office or a crating bay.
   So the same device, drawn in Stonegraph's own vocabulary — the rings are the
   turned profile of a lathe-cut base, and each glyph is the tool or the mark
   that department actually works with.

   The rings draw themselves when the section is reached, the glyph fades up
   behind them, and the whole plate breathes. Every part of it is stroke and
   opacity, so it costs one compositor layer and no repaints.
   ========================================================================== */
.stu-plate {
  position: relative;
  /* margin: 0, because this is a <figure> and the UA stylesheet gives one
     `margin: 1em 40px`. Eighty pixels of that on a 158px column left the plate
     78px wide — half of it was browser default. */
  margin: 0;
  aspect-ratio: 4 / 3;
  border-radius: 16px;
  border: 1px solid rgba(239, 234, 220, .10);
  background:
    radial-gradient(ellipse 60% 60% at 50% 46%, rgba(194,170,126,.13), transparent 68%),
    linear-gradient(160deg, #1A1815 0%, #131211 55%, #0E0D0C 100%);
  overflow: hidden;
  display: grid;
  place-items: center;
  isolation: isolate;
}
.stu-plate { padding-bottom: 34px; }
.stu-plate svg { width: 62%; height: 62%; display: block; overflow: visible; margin-top: -10px; }

/* ---- what the glyph IS ----
   The drawings were abstract enough that nobody could tell a chisel from a
   pair of dividers, which makes them decoration pretending to be information.
   Naming each one fixes that in four words and adds the line of mono the rest
   of the page is set in. The label is the point; the drawing illustrates it. */
.stu-plate__cap {
  position: absolute;
  left: 0; right: 0; bottom: 16px;
  text-align: center;
  font-family: var(--f-mono, ui-monospace, monospace);
  font-size: .58rem;
  letter-spacing: .16em;
  text-transform: uppercase;
  /* Same literal, same ground, same fix — the plate captions. */
  color: var(--stu-gold);
  padding: 0 14px;
  opacity: 0;
  transform: translateY(8px);
  transition: opacity .9s var(--ease, cubic-bezier(.16,1,.3,1)) .8s,
              transform .9s var(--ease, cubic-bezier(.16,1,.3,1)) .8s;
}
.stu-dept.is-bench .stu-plate__cap,
html:not(.js-reveal) .stu-plate__cap { opacity: 1; transform: none; }
@media (max-width: 620px) {
  /* The caption is set in px here, not rem. The type ramp in parity.css takes
     the root down to 13.5px on a phone, and .5rem of that is 6.75px — a size
     at which the label may as well not be there, and the label is the entire
     reason the plate is legible. Nine pixels of mono is small and readable;
     below that it is neither. */
  .stu-plate { padding-bottom: 40px; aspect-ratio: 5 / 4; }
  .stu-plate svg { width: 54%; height: 54%; margin-top: -14px; }
  .stu-plate__cap {
    font-size: 9px;
    letter-spacing: .07em;
    line-height: 1.35;
    bottom: 9px;
    padding: 0 6px;
    text-wrap: balance;
  }
  .stu-plate__n { font-size: 1.6rem; right: 9px; bottom: 4px; }
}

/* ---- the rings ----
   Drawn, not faded: each is a circle whose dash pattern is its own
   circumference, pushed one full turn out of phase, so bringing the offset
   back to zero draws it round. The same mechanism the constellation's
   connectors use, and the same reason — a line that appears was never drawn. */
.stu-ring {
  fill: none;
  stroke: rgba(194, 170, 126, .34);
  stroke-width: .7;
  stroke-linecap: round;
  transform-box: view-box;
  transform-origin: 50% 50%;
}
.stu-plate--armed .stu-ring { stroke-dashoffset: var(--len); }
.stu-dept.is-bench .stu-ring,
html:not(.js-reveal) .stu-ring {
  stroke-dashoffset: 0;
  transition: stroke-dashoffset 1.7s cubic-bezier(.22,.61,.36,1) var(--rd, 0s);
}
/* Once drawn, the outer rings turn very slowly against each other. */
.stu-dept.is-bench .stu-ring--turn { animation: stu-ring-turn 64s linear infinite; }
.stu-dept.is-bench .stu-ring--turn-back { animation: stu-ring-turn 88s linear infinite reverse; }
@keyframes stu-ring-turn { to { transform: rotate(360deg); } }

/* ---- the glyph ---- */
.stu-glyph {
  fill: none;
  stroke: var(--stu-gold);
  stroke-width: 1.15;
  stroke-linecap: round;
  stroke-linejoin: round;
  opacity: 0;
  transform-box: view-box;
  transform-origin: 50% 50%;
}
.stu-dept.is-bench .stu-glyph,
html:not(.js-reveal) .stu-glyph {
  opacity: 1;
  transition: opacity 1.1s var(--ease, cubic-bezier(.16,1,.3,1)) .55s;
}
.stu-glyph-fill { fill: var(--stu-gold); stroke: none; opacity: 0; }
.stu-dept.is-bench .stu-glyph-fill { opacity: .9; transition: opacity 1.1s ease .7s; }

/* ---- the bloom behind the glyph ---- */
.stu-plate__bloom {
  position: absolute; left: 50%; top: 46%;
  width: 46%; aspect-ratio: 1;
  transform: translate(-50%, -50%);
  border-radius: 999px;
  background: radial-gradient(circle, rgba(194,170,126,.30) 0%, rgba(194,170,126,.08) 42%, transparent 70%);
  opacity: 0;
  transition: opacity 1.4s var(--ease, cubic-bezier(.16,1,.3,1)) .5s;
  pointer-events: none;
}
.stu-dept.is-bench .stu-plate__bloom { opacity: 1; animation: stu-bloom 7s ease-in-out infinite alternate 2s; }
@keyframes stu-bloom {
  from { transform: translate(-50%, -50%) scale(1); opacity: .82; }
  to   { transform: translate(-50%, -50%) scale(1.16); opacity: 1; }
}

/* ---- the department's number, kept, as a watermark ---- */
.stu-plate__n {
  position: absolute; right: 16px; bottom: 10px;
  font-family: var(--f-display, Georgia, serif);
  font-weight: 300;
  font-size: clamp(2.2rem, 5vw, 3.6rem);
  line-height: 1;
  letter-spacing: -.04em;
  /* Was rgba(239,234,220,.07) — marble at seven per cent, on a marble
     ground. A watermark that cannot be seen at all is not a watermark; the
     numeral is cut INTO the plate, so it takes the ink tone at the same
     weight and reads as the faint engraved figure it was drawn to be. */
  color: rgba(36, 31, 24, .07);
  pointer-events: none;
}

/* ==========================================================================
   6. GLOBAL DELIVERY — the globe and the country grid
   ========================================================================== */
.stu-globe { position: relative; width: min(280px, 62vw); aspect-ratio: 1; margin: 0 auto clamp(34px, 4vw, 60px); }
.stu-globe__ring { position: absolute; inset: 0; animation: stu-spin 34s linear infinite; }
.stu-globe__ring svg { width: 100%; height: 100%; display: block; }
@keyframes stu-spin { to { transform: rotate(360deg); } }
.stu-globe__halo {
  position: absolute; inset: -14%; z-index: -1; border-radius: 999px;
  background: radial-gradient(circle, rgba(194,170,126,.2) 0%, transparent 66%);
}

.stu-countries { display: grid; grid-template-columns: repeat(2, 1fr); gap: 16px; }
@media (min-width: 860px) { .stu-countries { grid-template-columns: repeat(3, 1fr); } }
.stu-country { text-align: center; display: flex; flex-direction: column; align-items: center; }
.stu-country__f { font-size: 1.9rem; line-height: 1; }
.stu-country__n {
  margin: 12px 0 0;
  font-family: var(--f-display, Georgia, serif);
  font-weight: 400; font-size: 1.06rem; letter-spacing: -.01em; color: var(--ink);
}
.stu-country__v { margin: 4px 0 0; font-family: var(--f-mono, ui-monospace, monospace); font-weight: 300; font-size: 1.2rem; color: var(--stu-gold); }
.stu-country__d { margin: 4px 0 0; font-size: .76rem; font-weight: 300; color: var(--ink-mute); }

.stu-stats {
  margin-top: clamp(30px, 3.4vw, 48px);
  padding-top: clamp(22px, 2.6vw, 34px);
  border-top: 1px solid var(--rule);
  display: flex; flex-direction: column; align-items: center; gap: 24px;
}
@media (min-width: 640px) { .stu-stats { flex-direction: row; justify-content: center; gap: 48px; } }
.stu-stat { text-align: center; }
.stu-stat__v {
  font-family: var(--f-display, Georgia, serif);
  font-weight: 300; font-size: clamp(2rem, 4vw, 3rem); line-height: 1; color: var(--stu-gold); margin: 0;
}
.stu-stat__l {
  margin: 8px 0 0;
  font-family: var(--f-mono, ui-monospace, monospace);
  font-size: .62rem; text-transform: uppercase; letter-spacing: .2em; color: var(--ink-mute);
}
.stu-stat__bar { display: none; width: 1px; height: 46px; background: var(--rule); }
@media (min-width: 640px) { .stu-stat__bar { display: block; } }

/* ==========================================================================
   7. CONTACT
   ========================================================================== */
.stu-contact { text-align: center; }
.stu-contact__rule { display: flex; align-items: center; justify-content: center; gap: 14px; margin-bottom: 30px; }
.stu-contact__rule span.stu-rule { width: 32px; height: 1px; background: rgba(194,170,126,.4); }
.stu-contact__sub {
  margin: 0 0 clamp(30px, 3.6vw, 50px);
  font-family: var(--f-mono, ui-monospace, monospace);
  font-size: .74rem; text-transform: uppercase; letter-spacing: .2em; color: var(--ink-mute);
}
.stu-ways { display: grid; gap: 16px; max-width: 42rem; margin-inline: auto; }
@media (min-width: 640px) { .stu-ways { grid-template-columns: repeat(2, 1fr); } }
.stu-way {
  display: flex; flex-direction: column; align-items: center; gap: 12px;
  text-decoration: none; text-align: center;
}
.stu-way__i {
  display: inline-flex; align-items: center; justify-content: center;
  width: 46px; height: 46px; border-radius: 999px;
  background: rgba(194,170,126,.12);
  box-shadow: inset 0 0 0 1px rgba(194,170,126,.24);
  color: var(--stu-gold);
  transition: background-color .35s var(--ease), box-shadow .35s var(--ease);
}
.stu-way:hover .stu-way__i { background: rgba(194,170,126,.2); box-shadow: inset 0 0 0 1px rgba(194,170,126,.44); }
.stu-way__k {
  font-family: var(--f-mono, ui-monospace, monospace);
  font-size: .62rem; text-transform: uppercase; letter-spacing: .2em; color: var(--ink-mute); margin: 0;
}
.stu-way__v {
  margin: 5px 0 0;
  font-family: var(--f-display, Georgia, serif);
  font-size: 1rem; letter-spacing: -.01em; color: var(--ink);
  transition: color .35s var(--ease);
}
.stu-way:hover .stu-way__v { color: var(--stu-gold); }
.stu-hair {
  width: 96px; height: 1px; margin: clamp(38px, 4.4vw, 64px) auto 0;
  background: linear-gradient(to right, transparent, var(--stu-gold), transparent);
}

@media (prefers-reduced-motion: reduce) and (prefers-reduced-motion: no-preference) { /* OFF by the motion policy — see motion-always.css */
  .stu-pill__dot::before,
  .stu-globe__ring { animation: none; }
}


/* ==========================================================================
   THE OPENING SEQUENCE
   --------------------------------------------------------------------------
   Driven by studio.js. Nothing here hides anything until that script has said
   it is going to bring it back: .stu-hero--armed is added by the script, so a
   page whose JavaScript never runs shows a finished hero rather than an empty
   one. Same contract site.js uses for its reveals and motion.js for its layer.
   ========================================================================== */
.stu-hero--armed .stu-in {
  opacity: 0;
  transform: translate3d(0, 34px, 0);
}
.stu-hero--armed .stu-hero__status.stu-in { transform: translate3d(0, -22px, 0); }

/* The headline's own mask. Deliberately not .rise-mask — see the note in
   studio.js. Padding and negative margin match the site's mask so a descender
   is never clipped and the heading occupies the same space either way. */
.stu-mask {
  display: block;
  overflow: hidden;
  padding-block: .54em .44em;
  margin-block: -.54em -.44em;
}
.stu-mask > span { display: block; }
.stu-hero--armed .stu-mask > span { transform: translate3d(0, 112%, 0); }

.stu-hero--open .stu-in {
  opacity: 1;
  transform: none;
  transition:
    opacity .9s var(--ease, cubic-bezier(.16,1,.3,1)) var(--rd, 0s),
    transform 1.15s var(--ease, cubic-bezier(.16,1,.3,1)) var(--rd, 0s);
}
/* The line itself does not fade — it rises out of the mask, which is what
   makes it read as type being set rather than type appearing. */
.stu-hero--open .stu-mask > span {
  transform: none;
  transition: transform 1.25s var(--ease, cubic-bezier(.16,1,.3,1)) var(--rd, 0s);
}

/* ---- the photograph: settle, then drift ---- */
.stu-hero__media {
  transform: translate3d(0, var(--hp, 0px), 0) scale(var(--hs, 1));
  will-change: transform;
}
.stu-hero__media.is-settling {
  transition: transform 2.4s cubic-bezier(.16, 1, .3, 1);
}

/* ---- the pill's own arrival, under the stagger ---- */
.stu-hero--open .stu-pill { animation: stu-pill-in 1s var(--ease, cubic-bezier(.16,1,.3,1)) both .1s; }
@keyframes stu-pill-in {
  from { letter-spacing: .16em; opacity: 0; }
  to   { letter-spacing: .02em; opacity: 1; }
}

/* ---- the tick under the signature draws down ---- */
.stu-hero--open .stu-hero__tick {
  animation: stu-tick 1.1s cubic-bezier(.16,1,.3,1) both 1.05s;
  transform-origin: top;
}
@keyframes stu-tick { from { transform: scaleY(0); } to { transform: scaleY(1); } }

/* ==========================================================================
   SCROLL EFFECTS
   --------------------------------------------------------------------------
   The cards, principles, departments and countries all carry .reveal, so
   site.js already brings them in. What is added here is the part that makes
   the arrival read as premium rather than as a fade: a lift on the card, a
   line that draws itself down the timeline, and a lit edge that follows the
   pointer across a card.
   ========================================================================== */
.stu-card { transform: translateZ(0); }
.stu-card.is-in { animation: stu-card-in 1s var(--ease, cubic-bezier(.16,1,.3,1)) both; }
@keyframes stu-card-in {
  from { opacity: 0; transform: translate3d(0, 44px, 0) scale(.97); }
  to   { opacity: 1; transform: none; }
}

/* The timeline rule draws from the top as the section is entered, rather than
   simply being there — the same gesture the constellation's connectors use. */
.stu-track::before { transform-origin: top; transform: scaleY(0); transition: transform 1.6s cubic-bezier(.22,.61,.36,1) .2s; }
.stu-track.is-in::before,
html:not(.js-reveal) .stu-track::before { transform: scaleY(1); }

/* The gold marker lands rather than appears. */
.stu-area .stu-area__mark::before { transform: scale(0); transition: transform .7s var(--ease, cubic-bezier(.16,1,.3,1)) var(--rd, 0s); }
.stu-area.is-in .stu-area__mark::before,
html:not(.js-reveal) .stu-area__mark::before { transform: scale(1); }

/* A lift under the pointer, on anything that is a card. */
@media (hover: hover) and (pointer: fine) {
  .stu-card { transition: border-color .5s var(--ease), background-color .5s var(--ease), transform .6s var(--ease), box-shadow .6s var(--ease); }
  .stu-card:hover {
    transform: translate3d(0, -7px, 0);
    box-shadow: 0 26px 60px -40px rgba(36, 31, 24, .5);
  }
}

/* The plate behind each department number lifts its glow on approach. */
.stu-plate::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  opacity: 0; transition: opacity 1.2s var(--ease, cubic-bezier(.16,1,.3,1));
  background: radial-gradient(ellipse 60% 60% at 70% 80%, rgba(194,170,126,.26), transparent 70%);
}
.stu-dept.is-bench .stu-plate::after { opacity: 1; }

/* ---- the calm sequence ----
   Set by studio.js when the reader has asked for reduced motion — which on
   Android includes every phone with Battery Saver on. The order and the fade
   are kept; the travel is not. A page that simply appears all at once reads as
   broken, and "reduce" has never meant "remove". */
.stu-hero--calm.stu-hero--armed .stu-in { transform: none; }
.stu-hero--calm.stu-hero--armed .stu-mask > span { transform: none; }
.stu-hero--calm .stu-in {
  transition: opacity .8s ease var(--rd, 0s) !important;
}
.stu-hero--calm .stu-mask { overflow: visible; }
.stu-hero--calm .stu-mask > span {
  opacity: 0;
  transition: opacity .8s ease var(--rd, 0s);
}
.stu-hero--calm.stu-hero--open .stu-mask > span { opacity: 1; }
.stu-hero--calm .stu-hero__media { transform: none !important; }
.stu-hero--calm .stu-hero__tick { animation: none; }

@media (prefers-reduced-motion: reduce) and (prefers-reduced-motion: no-preference) { /* OFF by the motion policy — see motion-always.css */
  /* The card entrance keeps its fade and loses its rise. */
  .stu-card.is-in { animation: stu-card-calm .8s ease both; }
  @keyframes stu-card-calm { from { opacity: 0; } to { opacity: 1; } }

  /* A rule drawing itself across is travel; it fades in place instead. */
  .stu-track::before { transform: none; opacity: 0; transition: opacity 1s ease .2s; }
  .stu-track.is-in::before { opacity: 1; }
  .stu-area .stu-area__mark::before { transform: none; opacity: 0; transition: opacity .7s ease var(--rd, 0s); }
  .stu-area.is-in .stu-area__mark::before { opacity: 1; }

  /* Letter-spacing is a size change, not a position change, so the pill keeps
     its arrival — but without the tracking travel. */
  .stu-hero--open .stu-pill { animation: stu-pill-calm .9s ease both .1s; }
  @keyframes stu-pill-calm { from { opacity: 0; } to { opacity: 1; } }
}


/* ==========================================================================
   THE AMBIENT LAYER
   --------------------------------------------------------------------------
   Four things the Zayne Forge page has that this one did not. All of them are
   background — none carries meaning, and the page is complete without any of
   it — so all four are built by script and none is in the markup.

   Everything here runs on the compositor: transform, opacity and a background
   position. Nothing animates a layout property, so a phone renders the lot on
   its GPU without touching the main thread.
   ========================================================================== */

/* ---- 1. the ambient mark ----
   A large, very faint studio mark turning behind the hero. The original does
   this with its own logo at 7% opacity and a 360-degree CSS rotation; this is
   the same gesture with Stonegraph's own geometry — the eight-point mandala
   that runs through the site's line art. */
.stu-mark {
  position: absolute;
  left: 50%; top: 50%;
  width: min(74vw, 620px);
  aspect-ratio: 1;
  margin: -1px;
  transform: translate(-50%, -50%);
  z-index: 1;
  pointer-events: none;
  opacity: .07;
  color: #FFFDF7;
}
.stu-mark svg {
  width: 100%; height: 100%; display: block;
  animation: stu-mark-turn 96s linear infinite;
  will-change: transform;
  transform: translateZ(0);
}
@keyframes stu-mark-turn { to { transform: rotate(360deg); } }
@media (max-width: 640px) { .stu-mark { width: 86vw; opacity: .05; } }

/* ---- 2. ambient light drift ----
   A warm wash that moves on its own, on the tinted bands. The pointer-driven
   glow in motion.css only exists on a device with a cursor; this one is what a
   phone gets, and it is the reason those sections stop looking like flat
   panels of colour on a touchscreen. */
.stu-drift {
  position: absolute; inset: -20%;
  z-index: 0; pointer-events: none;
  background: radial-gradient(ellipse 42% 38% at 50% 50%,
              rgba(194, 170, 126, .20) 0%, rgba(194, 170, 126, .07) 45%, transparent 72%);
  animation: stu-drift 34s ease-in-out infinite alternate;
  will-change: transform;
}
@keyframes stu-drift {
  from { transform: translate3d(-14%, -8%, 0) scale(1); }
  to   { transform: translate3d(14%, 10%, 0) scale(1.18); }
}
.stu-sec--tint > .stu-wrap { position: relative; z-index: 1; }

/* ---- 3. grain ----
   A single tile of monochrome noise at very low opacity, moved a few pixels
   every frame-and-a-bit. It is what stops a large flat dark area from banding
   on an 8-bit screen, and it is most of the difference between "a dark
   section" and "film". */
.stu-grain {
  position: absolute; inset: -60px;
  z-index: 3; pointer-events: none;
  opacity: .13;
  background-image: var(--stu-grain);
  background-size: 180px 180px;
  animation: stu-grain 1.2s steps(6) infinite;
  /* Its own compositor layer. Without this the grain is repainted INTO the
     hero's layer six times a second, and because the hero contains a playing
     video that repaint drags the video frame with it — which is the stutter.
     Promoted, the grain is a texture the compositor slides over the video
     instead of something the video has to be redrawn underneath. */
  will-change: transform;
  transform: translateZ(0);
  backface-visibility: hidden;
}
@keyframes stu-grain {
  0%   { transform: translate3d(0, 0, 0); }
  20%  { transform: translate3d(-24px, 12px, 0); }
  40%  { transform: translate3d(18px, -20px, 0); }
  60%  { transform: translate3d(-14px, -12px, 0); }
  80%  { transform: translate3d(22px, 16px, 0); }
  100% { transform: translate3d(0, 0, 0); }
}

/* ---- 4. the hero releases the page ----
   The content lifts and fades as the hero is scrolled past, so the section
   below arrives into a clear frame rather than sliding over a headline that is
   still at full strength. --stu-exit is written by studio.js from the scroll
   position; at rest it is 0 and nothing here does anything. */
.stu-hero__inner {
  opacity: 1;
  transform: none;
}
.stu-hero__sig { opacity: 1; }

/* Ambient loops keep their polish only while they can be seen. */
.stu-sec--tint.is-rest .stu-drift,
.stu-globe.is-rest .stu-globe__ring { animation-play-state: paused; }


/* ---- the phone keeps the material layers ----
   The slow mark is transform-only and the grain is static, so both retain the
   cinematic depth without asking Safari to repaint a moving noise field over
   every video frame. */
@media (max-width: 640px) {
  .stu-grain {
    display: block;
    animation: none;
    opacity: .055;
    will-change: auto;
  }
  .stu-mark { display: block; width: 86vw; opacity: .045; }
}
/* A device without the memory to decode the reel comfortably should not be
   compositing a noise field over it either. */
@media (max-width: 900px) and (pointer: coarse) {
  .stu-grain { animation: none; opacity: .08; }
}


/* ==========================================================================
   THE HERO REEL, CORRECTED
   --------------------------------------------------------------------------
   The homepage's reel plays cleanly on a phone and this one did not, and the
   difference was never the footage. It was that this hero asks the element
   CONTAINING a playing video to do five things the homepage never asks of it:
   carry a scrim built for a sand-coloured band, hold a poster that the video
   pushes out of the frame, overscan by 130px at each edge, scale for two and
   a half seconds on arrival, and translate on every scroll frame after that.

   Each of those is cheap on a still photograph. On a video every one of them
   forces the frame to be re-rasterised at new geometry, continuously, for as
   long as it lasts — which on an iPhone is the stutter, and the black blink
   between the poster and the first frame is the rest of it.

   Everything below either removes one of those five or makes the two layers
   agree with each other. Nothing here changes the composition: same reel,
   same crop, same veil, same opening sequence.
   ========================================================================== */

/* ---- 1. the homepage's scrim comes off ----
   `.hero__film::after` is a sand wash — it exists so a dark reel cannot take
   the contrast away from a headline standing on the homepage's stone-100
   band. This hero is #141413 with white and gold type and has its own veil
   two layers up, so here that gradient is simply a beige film over the
   footage. On a phone it is worse than that: the mobile rule takes it to a
   FULLY OPAQUE rgb(233,227,213) at its lower edge, so the bottom third of a
   dark cinematic hero was a solid sand panel with the veil greyed over it.
   That is most of "it looks unprofessional", and it is one line. */
.stu-hero .hero__film::after { content: none; }

/* ---- 2. the poster and the reel are stacked, not queued ----
   Both were in normal flow. The poster ships in the markup and the reel is
   inserted BEFORE it by site.js, and because the reel is `height: 100%` of
   the shell, the moment it appears the poster is pushed a full shell-height
   below the frame — 1072px down on a 375px screen. Measured, it sat at
   y 942-2014 on an 812px hero: gone.

   So the poster stopped covering anything at the instant the <video> element
   was created, which is BEFORE that element has decoded a single frame. In
   between there is nothing to show and the hero blinks to its own black
   ground. On the homepage that gap lands on a sand band and reads as the
   page's own colour; here it reads as the video dropping out.

   Both layers are taken out of flow and laid on top of each other instead.
   The poster is above the reel, so it covers it until there is genuinely
   something behind it — and then it fades rather than being switched off, so
   even a poster that is a frame or two out of step never shows a cut. */
.stu-hero .stu-hero__media > video {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
}
.stu-hero .stu-hero__media > video { z-index: 0; }
.stu-hero .stu-hero__media > picture { display: none !important; }
.stu-hero .stu-hero__media > picture > img.hero__film-poster {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 1;
  transition: opacity .5s ease;
}
/* site.js adds .is-hidden once `playing` proves frames are moving. components.css answers that with
   `display: none`, which is a cut. Here it is a half-second cross-fade, and
   visibility is what finally takes the layer out of compositing once the fade
   has finished — an element left at opacity 0 is still a layer over a video. */
.stu-hero .stu-hero__media > picture > img.hero__film-poster.is-hidden {
  display: block;
  opacity: 0;
  visibility: hidden;
  transition: opacity .5s ease, visibility 0s linear .5s;
}

/* ---- 3. one focal point, read by both layers ----
   They disagreed, in both directions. On a laptop the poster was anchored at
   `center 38%` and the reel at `center 42%`; on a phone components.css pulled
   the poster to `50% 38%` while this file put the reel at `center center`.
   A poster exists so the swap to the reel is invisible, and a poster framed
   differently from the reel guarantees the opposite: the picture jumps at the
   exact moment it is meant to be seamless.

   One custom property now, set on the shell and read by both, so they cannot
   drift apart again. */
.stu-hero .stu-hero__media { --stu-focal: 50% 42%; }
@media (max-width: 759px) {
  /* The portrait cut is already framed for this shape — it is shown whole
     rather than cropped a second time. */
  .stu-hero .stu-hero__media { --stu-focal: 50% 50%; }
}
.stu-hero .stu-hero__media > video {
  object-position: var(--stu-focal);
}

/* ---- 4. the status pill lands ----
   `.stu-hero--armed .stu-hero__status.stu-in` sets the pill's start position
   at three classes; `.stu-hero--open .stu-in` releases it at two. Specificity
   is not source order, so the start position won permanently and the pill
   finished its arrival 22px above where it belongs, every time. */
.stu-hero--open .stu-hero__status.stu-in { transform: none; }

/* ---- 5. nothing transforms the element that holds the video ----
   This is the whole fix, and the earlier version of it was too timid: it took
   these off a phone and left them on a laptop, and the report came back saying
   the laptop stutters too. It does, for the same reason, so they come off
   everywhere. The homepage's reel does none of this and the homepage's reel is
   the one that plays cleanly — that is not a coincidence, it is the answer.

   Three separate things were being done to the element that CONTAINS a playing
   video, and each of them makes the browser re-rasterise the video frame at
   new geometry rather than letting the compositor slide an existing texture:

     the overscan. `inset: -130px 0` existed only to give the parallax
     somewhere to travel from. It made the shell 1072px tall on an 812px phone
     and 1005px on an 875px laptop hero, so a fifth to a third of every decoded
     frame was composited purely in order to be clipped away — on the largest
     and most expensive element on the page.

     the settle. A 2.4s transition on `scale()` over a video is 2.4 seconds of
     the frame being re-rasterised at a new size, every frame, and they are the
     first 2.4 seconds of the visit. There is no version of this that is free:
     scale is the one transform a video cannot be given cheaply.

     the parallax. `--hp` was rewritten on the shell on every scroll frame.
     Worse than the scale, because it is a CUSTOM PROPERTY — those are not
     compositor-animatable, so each write is a full style recalculation of the
     shell and everything in it, the <video> included, for the whole length of
     the hero. That is the lag while scrolling.

   The hero keeps its opening sequence, its veil, its grid, its grain and its
   turning mark. What it stops doing is moving the reel. */
.stu-hero.stu-hero .stu-hero__media,
.stu-hero.stu-hero .hero__film { inset: 0; }

.stu-hero .stu-hero__media,
.stu-hero .stu-hero__media.is-settling,
.stu-hero .stu-hero__media.is-flat {
  transform: none !important;
  transition: none !important;
  will-change: auto;
}

/* And the reel stops asking for a layer of its own. `translateZ(0)` plus
   `backface-visibility: hidden` inside a transformed, overflow-hidden,
   isolated ancestor is the exact combination iOS Safari flashes black on, and
   with the ancestor no longer transforming there is nothing left for the
   promotion to buy: every engine composites a <video> natively.

   The background is the hero's own ground, so that any moment the decoder has
   nothing to hand back — the first frames, a loop, a tab coming forward —
   paints as the dark the hero is already made of instead of as a hole. */
.stu-hero .stu-hero__media > video {
  transform: none;
  backface-visibility: visible;
  background: var(--stu-dark);
}

/* ---- 5. the first decoded frame arrives as a film, not a flash ----------
   There is deliberately still no poster. Until `playing` has produced a real
   decoded frame the hero shows its own charcoal ground; site.js then adds
   `.is-film-ready` and opacity alone performs the reveal. Opacity stays on the
   compositor, so this supplies a premium opening without scaling, filtering or
   re-rasterising the playing video. The delayed keyframe is a no-script safety
   net and normally never gets a chance to run. */
@keyframes stu-film-failsafe {
  to { opacity: 1; }
}
.stu-hero .stu-hero__media > video {
  opacity: 0;
  transition: opacity .48s cubic-bezier(.22,.61,.36,1);
  animation: stu-film-failsafe .48s cubic-bezier(.22,.61,.36,1) .7s forwards;
}
.stu-hero .stu-hero__media.is-film-ready > video {
  opacity: 1;
  animation: none;
}

/* Gold remains a decorative material, but small information labels on cream
   need ink-level contrast. The dark contact and film hero retain their light
   treatment; every light Studio band gets a measured, opaque bronze label. */
/* The contact section used to be excluded from this rule and given #d7bf8e
   instead, on the understanding that it was a dark band. It is not, and has
   not been for some time: .stu-contact carries no background of its own, so it
   stands on the page's cream ground with an ink heading beside it. A pale gold
   there measures 1.4:1 — which made "Start here", the label over the one block
   on the page whose whole job is to be acted on, the least readable text on
   the site. Every key on this ground now takes the same bronze. */
.stu-sec .stu-key {
  color: #665136;
  font-weight: 600;
}

/* ---- 6. the grain keeps its texture and gives up its animation ----
   A 90px noise tile stepped to a new offset six times a second, full bleed,
   directly over a playing video. Promoted or not, it is a translucent layer
   changing on top of the most expensive element on the page, five times a
   second, forever.

   What the grain is FOR — the note above says so — is stopping a large flat
   dark area from banding on an 8-bit screen. A still tile does that exactly as
   well as a moving one. The motion was the part that read as film, and it is
   also the only part that cost anything, so over the hero it goes and the
   texture stays. Below 640px the grain is not rendered at all, which has not
   changed. */
.stu-hero .stu-grain {
  animation: none;
  will-change: auto;
}


/* ==========================================================================
   THE SIX BENCHES — each one arrives on its own, and arrives again
   --------------------------------------------------------------------------
   The plate art used to hang off `.is-in`, which is site.js's reveal class,
   and site.js's reveal is a ONE-SHOT: it adds the class and unobserves. Worse,
   its section-level floor sweeps every `.reveal` inside a section 700ms after
   that section comes within a viewport, so in practice all six benches drew
   themselves at the same moment — before most of them had been scrolled to at
   all. By the time you reached bench four its rings had been drawn for ten
   seconds and there was nothing left to watch.

   The art now hangs off `.is-bench`, which studio.js owns and toggles both
   ways. `.is-in` is left to site.js and stays one-shot, so the department's
   heading and paragraph settle once and never move again — text that
   re-animates every time it is scrolled past is a different and worse problem.

   What re-triggers is the plate: the rings draw, the glyph fades up behind
   them, the bloom lifts, the caption rises. Leave the bench and the plate
   resets; come back and it draws again.
   ========================================================================== */

/* The reset is instant, and it has to be said out loud.

   Three of these declare their transition inside the `.is-bench` rule, so
   dropping the class already snaps them back for free. Two do not — the
   caption and the bloom carry theirs on the base rule, so without this they
   would spend 1.4s and 0.9s fading back OUT, on delays of 0.8s and 0.5s.
   Nobody would ever see it, because the class is only dropped once the bench
   is completely off screen — but a reader who turns straight back around
   would arrive at a plate still halfway through un-drawing itself and have to
   wait for it to finish before it could start. So the exit costs nothing and
   takes no time: the plate is simply armed again. */
.stu-depts .stu-dept:not(.is-bench) .stu-plate__cap,
.stu-depts .stu-dept:not(.is-bench) .stu-plate__bloom,
.stu-depts .stu-dept:not(.is-bench) .stu-plate::after,
.stu-depts .stu-dept:not(.is-bench) .stu-ring,
.stu-depts .stu-dept:not(.is-bench) .stu-glyph,
.stu-depts .stu-dept:not(.is-bench) .stu-glyph-fill {
  transition: none;
  animation: none;
}

/* ---- what a phone stops paying for ----
   Two of these animations are arrivals and three of them are idles, and the
   difference matters on a battery.

   The arrival is the whole effect: 1.7s of stroke-dashoffset per ring, a
   glyph fading up, a caption rising. It runs once per visit to a bench and
   then it is over. It stays.

   The idles never stop: the outer ring turns once every 64 seconds, the inner
   one every 88, and the bloom breathes on a 7s alternate — forever, on all six
   plates, for as long as the page is open. A 64-second rotation is four
   degrees per second. On a phone held at arm's length that is not slow motion,
   it is no motion, and it was being paid for in repaints on the SVG and on a
   gradient-filled box for the entire session.

   They come off on any touch device. Nothing a phone reader can perceive is
   lost, and what is gained is that a plate costs exactly its 1.7s of arrival
   and then nothing at all.

   (Off-screen this is now moot on every device — studio.js drops `.is-bench`
   when a bench leaves, which stops its idles too. This is about the two or
   three plates that ARE on screen.) */
@media (hover: none) and (pointer: coarse) {
  .stu-dept.is-bench .stu-ring--turn,
  .stu-dept.is-bench .stu-ring--turn-back,
  .stu-dept.is-bench .stu-plate__bloom { animation: none; }
}


/* ==========================================================================
   THE FLAG, WHERE THERE ARE NO FLAGS
   --------------------------------------------------------------------------
   studio.js measures whether the renderer can draw a regional-indicator pair
   as one glyph and marks the document when it cannot. Windows cannot: Segoe UI
   Emoji ships no country flags, so every flag in the delivery grid was there on
   a phone and gone on a laptop.

   What replaces it is the two-letter code the emoji already encodes, set in the
   site's mono inside a bordered chip — the same gold hairline the plates and
   the mounts use, at the same size the flag occupied, so the row does not move
   when it swaps. A country code in a chip is a thing somebody chose. Two empty
   boxes are not.
   ========================================================================== */
/* Only the cells that HAVE a code. The attribute is written by studio.js and
   only ever onto a regional-indicator pair, so scoping to it is what keeps the
   globe on "Elsewhere" alive — without this the rule collapsed every cell to
   nothing and the sixth row lost its mark entirely, because a globe is in
   every font and never needed replacing. */
html.no-flag-emoji .stu-country__f[data-code] {
  /* The emoji is taken out of the text flow rather than hidden, so a screen
     reader is not handed two unpronounceable code points. */
  font-size: 0;
  line-height: 0;
}
html.no-flag-emoji .stu-country__f[data-code]::before {
  content: attr(data-code);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 2.6em;
  height: 1.75em;
  padding: 0 .5em;
  border: 1px solid rgba(194, 170, 126, .55);
  border-radius: 3px;
  background: rgba(194, 170, 126, .07);
  font-family: var(--f-mono, ui-monospace, monospace);
  font-size: .82rem;
  font-weight: 400;
  letter-spacing: .14em;
  text-indent: .14em;          /* the tracking would otherwise sit it left */
  line-height: 1;
  color: var(--stu-gold);
}
/* "Elsewhere" carries a globe rather than a country, so it has no data-code
   and keeps its emoji — the globe is in every font on every platform. */
