/* ═══════════════════════════════════════════════════════════════════════
   GORDAN HORVAT — part one: hero / intro / hospitality
   ═══════════════════════════════════════════════════════════════════════ */

/* ── type ─────────────────────────────────────────────────────────────── */
@font-face {
  font-family: 'Archivo';
  font-style: normal;
  font-weight: 100 900;
  font-stretch: 62% 125%;
  font-display: block;
  src: url('../assets/fonts/archivo-latin-wdth-normal.woff2') format('woff2-variations');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
                 U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Archivo';
  font-style: normal;
  font-weight: 100 900;
  font-stretch: 62% 125%;
  font-display: block;
  src: url('../assets/fonts/archivo-latin-ext-wdth-normal.woff2') format('woff2-variations');
  unicode-range: U+0100-02BA, U+1E00-1E9F, U+2020, U+20A0-20AB, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Instrument Serif';
  font-style: italic;
  font-weight: 400;
  font-display: swap;
  src: url('../assets/fonts/instrument-serif-latin-400-italic.woff2') format('woff2');
}
@font-face {
  font-family: 'Instrument Serif';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('../assets/fonts/instrument-serif-latin-400-normal.woff2') format('woff2');
}

/* ── scene system ─────────────────────────────────────────────────────── */
@property --sheen {
  syntax: '<percentage>';
  inherits: false;
  initial-value: 22%;
}

@property --sheen2 {
  syntax: '<percentage>';
  inherits: false;
  initial-value: 30%;
}

@property --scene {
  syntax: '<number>';
  inherits: true;
  initial-value: 0;
}

/* how far the detail has developed over the room in Hospitality, 0 to 1.
   Registered so the browser can interpolate it and so the calc() below it
   resolve as numbers rather than as substituted tokens. */
@property --rise {
  syntax: '<number>';
  inherits: true;
  initial-value: 0;
}

:root {
  --black: #0b0b0d;
  --black-deep: #050506;
  --black-lift: #17171b;
  --white: #f2f0ed;
  --paper: #eeecea;
  --ink: #121214;
  --grey: #8b8b90;
  --red: #d3222b;
  --red-deep: #8e1319;
  --grey-pale: #c2c2c7;

  /* the black room is the default state of the page */
  --bg: var(--black);
  --fg: var(--white);
  --dim: var(--grey);
  --hair: rgb(242 240 237 / 0.16);

  /* --scene 0 = the chrome sits over black · 1 = over the red room.
     White reads on both, so the chrome's type never changes; only the red
     progress fill turns white while it is over red, where it would vanish.
     The sections themselves change with a hard edge, never a fade. */
  --scene: 0;
  --chrome-fg: var(--white);

  --gut: clamp(1.25rem, 4.2vw, 4.5rem);
  --ease: cubic-bezier(0.22, 1, 0.36, 1);

  color-scheme: dark;
}

/* ── reset ────────────────────────────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--fg);
  font-family: 'Archivo', 'Helvetica Neue', Arial, sans-serif;
  font-weight: 300;
  font-synthesis: none;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: clip;
  transition: background-color 0.1s linear;
}

body.is-loading { cursor: progress; }

img, video { display: block; max-width: 100%; }
h1, h2, h3, p, figure, figcaption { margin: 0; }
em { font-style: normal; }

::selection { background: var(--fg); color: var(--bg); }

/* Lenis */
html.lenis, html.lenis body { height: auto; }
.lenis.lenis-smooth { scroll-behavior: auto !important; }
.lenis.lenis-stopped { overflow: hidden; }

/* ── line reveal primitive ────────────────────────────────────────────── */
.reveal-line {
  display: block;
  overflow: hidden;
  /* the clip box is a little taller than the line so a tilted entry and any
     descender still have room; the negative margin keeps layout unchanged */
  padding: 0.12em 0 0.14em;
  margin: -0.12em 0 -0.14em;
}
.reveal-line > span {
  display: block;
  will-change: transform;
}
html.js:not(.no-motion) .reveal-line > span {
  transform: translate3d(0, 115%, 0) rotate(1.2deg);
}

/* ═══ the opening ══════════════════════════════════════════════════════
   Black, a thin red line in the middle, and then the line opens like an
   aperture and the portrait is already forming behind it.

   Deliberately not a title card. The hero spells GORDAN HORVAT across the
   whole screen; an intro that spells it first, smaller, spends the reveal
   before the reveal. The line is the instrument that opens the page, not a
   scene of its own — one name, one arrival.

   It is display:none until html.js is set, so a page whose script never runs
   is never left sitting under a black cover. Under reduced motion the script
   removes it outright.                                                    */
.ignite { display: none; }
html.js .ignite {
  display: block;
  position: fixed;
  inset: 0;
  z-index: 80;
  pointer-events: none;
}
.ignite__shade {
  position: absolute;
  left: 0;
  right: 0;
  height: 50.2%;            /* the overlap keeps a hairline seam off the join */
  background: var(--black-deep);
  will-change: transform;
}
.ignite__shade[data-ignite-shade="top"] { top: 0; }
.ignite__shade[data-ignite-shade="bottom"] { bottom: 0; }
.ignite__line {
  position: absolute;
  top: 50%;
  left: 0;
  width: 100%;
  height: 1px;
  margin-top: -0.5px;
  background: var(--red);
  opacity: 0;
  /* scaled rather than resized: a width tween is a layout tween, and this one
     runs while the portrait is decoding */
  transform: scaleX(0);
  transform-origin: 50% 50%;
  will-change: transform, opacity;
}

/* The way in: two words under the line, the only things on the screen that
   can be pressed. "Enter" takes the film with its sound; the quieter line
   below takes it without. They appear once the line has been drawn. */
.ignite__gate {
  position: absolute;
  left: 0;
  right: 0;
  top: calc(50% + clamp(1.6rem, 3.4vh, 2.4rem));
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.35rem;
  pointer-events: auto;
  opacity: 0;
  visibility: hidden;
  transform: translateY(6px);
  transition: opacity 0.8s var(--ease), transform 0.8s var(--ease), visibility 0s linear 0.8s;
}
.ignite__gate.is-open {
  opacity: 1;
  visibility: visible;
  transform: none;
  transition: opacity 0.9s var(--ease), transform 0.9s var(--ease), visibility 0s;
}
.ignite__enter,
.ignite__quiet {
  font: inherit;
  text-transform: uppercase;
  background: none;
  border: 0;
  padding: 0.7em 1.2em;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.ignite__enter {
  font-size: clamp(0.7rem, 0.82vw, 0.84rem);
  font-weight: 340;
  letter-spacing: 0.36em;
  text-indent: 0.36em;            /* the tracking after the last letter, given back to the first */
  color: var(--white);
}
.ignite__quiet {
  font-size: clamp(0.54rem, 0.6vw, 0.62rem);
  font-weight: 330;
  letter-spacing: 0.28em;
  text-indent: 0.28em;
  color: var(--grey);
  transition: color 0.3s var(--ease);
}
.ignite__quiet:hover { color: var(--grey-pale); }
.ignite__enter:focus-visible,
.ignite__quiet:focus-visible { outline: 1px solid var(--red); outline-offset: 2px; }

/* ═══ pointer ══════════════════════════════════════════════════════════
   An ivory ring that lags a few frames behind the real pointer, and fills
   red only over something that actually does something.

   Red is the wrong colour to sit under the hand all day. On this site red is
   material and accent — the red room, the Automoto chapter, the one word a
   sentence turns on — so a pointer that is always red would take that meaning
   away. It would also disappear: the bordo section, the Automoto imagery and
   the red-lit villa film are most of the page. Ivory is visible on all of
   them, and red arriving on hover then means something.

   No label inside it. Three things on this page can be clicked, and none of
   them is a film — a cursor saying PLAY over a video that does not play is a
   cursor that lies. Labels belong here once project pages exist.

   The element is built by JS, which also adds .has-cursor. If the script
   never runs the class is never set and the system cursor is untouched.     */
html.has-cursor body,
html.has-cursor body * { cursor: none; }

.cursor {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 90;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.3s var(--ease);
  will-change: transform;
}
.cursor--on { opacity: 1; }
.cursor--out { opacity: 0; }
.cursor i {
  display: block;
  width: 13px;
  height: 13px;
  margin: -6.5px 0 0 -6.5px;
  border: 1px solid var(--white);
  border-radius: 50%;
  background: transparent;
  /* width and height rather than a scale: a scaled ring scales its own
     border with it, and a 0.3px hairline is not a thin outline, it is a
     missing one */
  transition:
    width 0.38s var(--ease),
    height 0.38s var(--ease),
    margin 0.38s var(--ease),
    background-color 0.38s var(--ease),
    border-color 0.38s var(--ease),
    transform 0.2s var(--ease);
}
/* Open, not solid. All three controls on this page are small text, and a
   filled 40px disc sits on top of the very word it is inviting you to click —
   the label disappears at the moment you aim at it. The ring turns red and
   carries a thin wash instead, so the signal arrives and the word stays
   readable inside it. A solid fill belongs over images, once there are any. */
.cursor--live i {
  width: 40px;
  height: 40px;
  margin: -20px 0 0 -20px;
  border-color: var(--red);
  background: rgb(211 34 43 / 0.16);
}
.cursor--press i { transform: scale(0.84); }

/* ═══ fixed chrome ═════════════════════════════════════════════════════ */
.chrome {
  position: fixed;
  inset: 0 0 auto 0;
  z-index: 60;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: clamp(1.1rem, 2.4vw, 2.1rem) var(--gut);
  pointer-events: none;
  color: var(--chrome-fg);
}
.chrome > * { pointer-events: auto; }

/* The top left of a portfolio is the identity slot, and it holds one name.
   It used to hold the studio's logo while the hero, two centimetres below,
   held his — so the first screen offered the visitor two identities and let
   them guess which one they were reading. The author takes the slot; the
   studio signs the page at the end instead.

   It is set in the site's own type rather than as a second logotype. A
   portfolio whose header is a badge is a company's site; a portfolio whose
   header is a name set in the same hand as everything else is a person's. */
.chrome__mark {
  display: flex;
  align-items: center;
  color: inherit;
  text-decoration: none;
  text-transform: uppercase;
  font-variation-settings: 'wdth' 92;
}
.chrome__name {
  font-size: clamp(0.63rem, 0.75vw, 0.78rem);
  font-weight: 340;
  letter-spacing: 0.24em;
  /* the trailing letter's tracking would otherwise push the block off centre */
  margin-right: -0.24em;
  line-height: 1;
}

.chrome__meta {
  display: flex;
  align-items: center;
  gap: 0.9rem;
  font-size: clamp(0.58rem, 0.68vw, 0.7rem);
  letter-spacing: 0.3em;
  text-transform: uppercase;
  opacity: 0.72;
  transition: opacity 0.6s var(--ease);
}
/* Not on the first screen. "Opening" was the one chapter name that said where
   you were rather than what you were looking at, and the opening needs no
   caption — up there the header holds his name and the sound, nothing else.
   The read-out fades in as the first chapter arrives and stays from then on.
   It is hidden with opacity, not display, so the header does not reflow. */
body[data-scene="hero"] .chrome__meta { opacity: 0; }
.chrome__chapter { min-width: 8.5em; text-align: right; }
.chrome__rule {
  display: block;
  width: clamp(48px, 7vw, 108px);
  height: 1px;
  background: currentColor;
  opacity: 0.3;
  overflow: hidden;
}
.chrome__rule i {
  display: block;
  height: 100%;
  width: 100%;
  background: color-mix(in oklab, var(--red) calc((1 - var(--scene)) * 100%), var(--white));
  transform: scaleX(0);
  transform-origin: 0 50%;
}

/* the way in from anywhere on the page. The one button, "What's your
   industry?", is the culmination at the end of the three chapters; this is the
   quiet door for whoever decides earlier. A plain phrase rather than the
   question, which repeated on every screen would become a label and lose its
   weight where it matters. Not on the first screen, like the chapter read-out:
   up there the header holds the name and the sound. It arrives with the first
   chapter and opens the same brief. */
.chrome__end {
  display: flex;
  align-items: center;
  gap: clamp(1.6rem, 2.8vw, 3rem);
}
.talk {
  position: relative;
  font-size: clamp(0.58rem, 0.68vw, 0.7rem);
  font-weight: 400;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: inherit;
  text-decoration: none;
  transition: opacity 0.6s var(--ease), visibility 0s linear 0s;
}
.talk::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0.28em;              /* the tracking after the last letter is air */
  bottom: -0.7em;
  height: 1px;
  background: var(--red);
  transform: scaleX(0);
  transform-origin: 0 50%;
  transition: transform 0.6s var(--ease);
}
.talk:hover::after,
.talk:focus-visible::after { transform: scaleX(1); }
body[data-scene="hero"] .talk {
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.6s var(--ease), visibility 0s linear 0.6s;
}

/* sound */
.sound {
  display: inline-flex;
  align-items: center;
  gap: 0.7rem;
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  font-size: clamp(0.58rem, 0.68vw, 0.7rem);
  letter-spacing: 0.28em;
  text-transform: uppercase;
  cursor: pointer;
  opacity: 0.72;
  transition: opacity 0.4s var(--ease);
}
.sound:hover, .sound:focus-visible { opacity: 1; }
.sound__bars { display: flex; align-items: flex-end; gap: 2px; height: 12px; }
.sound__bars i {
  width: 1.5px;
  height: 3px;
  background: currentColor;
  transform-origin: 50% 100%;
}
.sound[aria-pressed='true'] .sound__bars i { animation: eq 1.1s var(--ease) infinite alternate; }
.sound__bars i:nth-child(2) { animation-delay: 0.12s; }
.sound__bars i:nth-child(3) { animation-delay: 0.28s; }
.sound__bars i:nth-child(4) { animation-delay: 0.06s; }
.sound__bars i:nth-child(5) { animation-delay: 0.34s; }
@keyframes eq { from { height: 3px; } to { height: 12px; } }
.sound__label { min-width: 6.4em; text-align: right; }

/* ═══ 01 — HERO ════════════════════════════════════════════════════════ */
.hero {
  position: relative;
  height: 165vh;          /* scrub runway */
}
.hero__sticky {
  position: sticky;
  top: 0;
  height: 100svh;
  overflow: clip;
}

.hero__stage {
  position: absolute;
  inset: 0;
}

/* — portrait — */
/* Sized by height so the crop stays cinematic, but capped against the width
   so a tall narrow window never lets the portrait swallow the name. */
/* The portrait is deliberately too big for the frame. At the size it used to
   be, the whole head fitted with the crown grazing the top edge — neither air
   above it nor a decisive cut, which reads as a photograph that happened to
   fit rather than as a frame. It is now 18% larger: the crown leaves the
   picture, the shoulder runs out of the right edge, and he is closer to the
   lens than the frame can hold, which is what puts him in the room with you.

   Growing it from the bottom edge alone would have lifted the face with it,
   so the box is dropped by 9.45% of its own height — the face stays exactly
   where it was and only the frame tightens around it. That offset is set on
   the bottom property rather than as a transform, because the scroll already
   animates the transform of this same element. */
.hero__canvas {
  position: absolute;
  z-index: 2;
  right: -12.1vw;
  bottom: calc(min(144svh, 104vw) * -0.0945);
  aspect-ratio: 1122 / 1402;
  height: min(144svh, 104vw);
  width: auto;
  will-change: transform;
}
.hero__canvas canvas { display: block; width: 100%; height: 100%; }
/* Without JavaScript the photograph itself is the hero. Only once the
   script has announced itself does WebGL take the frame over. */
.hero__fallback {
  width: 100%;
  height: 100%;
  object-fit: contain;
  object-position: bottom center;
  transition: opacity 1.4s var(--ease);
}
html.js .hero__fallback { opacity: 0; }
html.js.no-webgl .hero__fallback { opacity: 1; }

/* — oversized name — */
/* Thin and wide rather than bold and condensed: at this size the weight is
   carried by the scale, so the strokes can stay fine. */
.hero__type {
  position: absolute;
  z-index: 1;
  left: var(--gut);
  bottom: 13svh;
  width: min(60vw, 1020px);
  /* Optical weight, not one weight. The name is set to the width of its column,
     so on a large screen it is enormous and a hairline reads as deliberate,
     while on a phone the same hairline is a physically thin stroke laid over a
     photograph and starts to shimmer. The weight therefore falls as the type
     grows. Thinner letters are also narrower, so fitName enlarges them to fill
     the same column — the block keeps its shape and only the stroke changes. */
  --name-wght: 118;
  font-weight: var(--name-wght);
  font-variation-settings: 'wght' var(--name-wght), 'wdth' var(--name-wdth, 100);
  letter-spacing: -0.062em;
  text-transform: uppercase;
  color: var(--white);
  pointer-events: none;
  will-change: transform;
}
/* Each line is drawn exactly ONCE. The two layers carry the same words so
   that one can sit behind the portrait and the other in front of it, but if
   both drew in full they would overlap wherever the portrait is transparent
   — and at this weight the doubled antialiasing reads as a heavier word.
   So the layers split the block between them, meeting in the gap between
   the two lines. */
.hero__type--back { clip-path: inset(-14% -12% 53% -12%); }
.hero__type--front {
  z-index: 3;
  clip-path: inset(47% -12% -14% -12%);
}
/* the mask runs a little past the column so a glyph's side bearing is never
   shaved off by it */
.hero__line { display: block; overflow: hidden; font-size: var(--name-size, 15vw); margin-right: -0.12em; }
.hero__line + .hero__line { margin-top: -0.30em; font-size: var(--name-size, 15vw); }
.hero__word {
  display: block;
  font-size: var(--name-size, 15vw);
  line-height: 1;
  padding-bottom: 0.14em;
  white-space: nowrap;
  will-change: transform;
}
html.js:not(.no-motion) .hero__word { transform: translate3d(0, 112%, 0); }

/* — footer row — */
.hero__foot {
  position: absolute;
  z-index: 4;
  inset: auto var(--gut) clamp(1.3rem, 2.6vw, 2.2rem) var(--gut);
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 2rem;
  pointer-events: none;
}
/* One line, the three roles divided by thin red uprights. The rule is drawn
   by each role after the first, on the inner span that the reveal moves, so it
   rises with its word instead of standing there before the words arrive. The
   spacing either side is equal to the eye, not to the numbers: tracking also
   falls after the last letter of the word before, so the gap on the left is
   given 0.3em less. */
.hero__roles {
  display: flex;
  flex-direction: row;
  align-items: center;
  white-space: nowrap;
  font-size: clamp(0.6rem, 0.72vw, 0.74rem);
  letter-spacing: 0.3em;
  font-weight: 330;
  text-transform: uppercase;
  color: var(--grey-pale);
  font-variation-settings: 'wdth' 92;
}
.hero__roles .reveal-line + .reveal-line > span::before {
  content: '';
  display: inline-block;
  width: 1px;
  height: 1em;
  margin: 0 1.25em 0 0.95em;
  vertical-align: -0.16em;
  background: var(--red);
}
html.js .hero__cue { opacity: 0; }
.hero__cue {
  display: flex;
  align-items: center;
  gap: 0.85rem;
  font-size: clamp(0.58rem, 0.68vw, 0.7rem);
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--grey);
}
.hero__cue-track {
  position: relative;
  display: block;
  width: clamp(40px, 5vw, 76px);
  height: 1px;
  background: rgb(242 240 237 / 0.2);
  overflow: hidden;
}
.hero__cue-track i {
  position: absolute;
  inset: 0 auto 0 0;
  width: 38%;
  background: var(--red);
  animation: cue 2.6s var(--ease) infinite;
}
@keyframes cue {
  0%   { transform: translateX(-110%); }
  55%  { transform: translateX(175%); }
  100% { transform: translateX(175%); }
}

/* ═══ 02 — REAL ESTATE ═════════════════════════════════════════════════
   One shot, dragged by the scroll. The film is the section: the visitor's
   own scrolling is the dolly, so the walk through the house happens at
   exactly the pace they choose.
   ═══════════════════════════════════════════════════════════════════════ */
.estate {
  position: relative;
  z-index: 4;
  background: var(--black-deep);
  color: var(--white);
}

.estate__track { height: 420vh; }
.estate__sticky {
  position: sticky;
  top: 0;
  height: 100svh;
  overflow: clip;
}

.estate__frame {
  position: absolute;
  inset: 0;
  background: var(--black-deep);
}
.estate__frame video,
.estate__still {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
/* the first frame carries the section until the film can be scrubbed */
.estate__still { opacity: 0; transition: opacity 0.5s var(--ease); }
html.js .estate__frame video { opacity: 0; transition: opacity 0.5s var(--ease); }
html.js .estate__still { opacity: 1; }
html.js .estate--rolling .estate__frame video { opacity: 1; }
html.js .estate--rolling .estate__still { opacity: 0; }

/* light held off the corners, so the type on top always has ground */
.estate__shade {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    linear-gradient(to bottom, rgb(5 5 6 / 0.62) 0%, rgb(5 5 6 / 0) 26%),
    linear-gradient(to top, rgb(5 5 6 / 0.72) 0%, rgb(5 5 6 / 0) 34%),
    radial-gradient(120% 80% at 10% 100%, rgb(5 5 6 / 0.5), rgb(5 5 6 / 0) 60%);
}

.estate__chapter {
  position: absolute;
  z-index: 3;
  top: clamp(4.6rem, 12vh, 8rem);
  left: var(--gut);
  display: flex;
  align-items: center;
  gap: 0.95rem;
  font-size: clamp(0.62rem, 0.74vw, 0.76rem);
  font-weight: 330;
  letter-spacing: 0.3em;
  text-transform: uppercase;
}
html.js .estate__chapter { opacity: 0; }
.estate__chapter-num { font-weight: 420; color: var(--red); }
.estate__chapter-sep { width: clamp(26px, 3.4vw, 54px); height: 1px; background: var(--red); }

/* the foot of the frame: which part of the walk we are in, and how far */
.estate__foot {
  position: absolute;
  z-index: 3;
  left: var(--gut);
  right: var(--gut);
  bottom: clamp(2.2rem, 6vh, 4rem);
  display: flex;
  align-items: center;
  gap: clamp(1rem, 3vw, 2.4rem);
}
html.js .estate__foot { opacity: 0; }
.estate__beat {
  flex: none;
  min-width: 11em;
  font-size: clamp(0.62rem, 0.74vw, 0.76rem);
  font-weight: 330;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--grey-pale);
}
.estate__progress {
  flex: 1 1 auto;
  height: 1px;
  background: rgb(242 240 237 / 0.2);
  overflow: hidden;
}
.estate__progress i {
  display: block;
  height: 100%;
  background: var(--red);
  transform: scaleX(0);
  transform-origin: 0 50%;
}

.estate__say {
  position: absolute;
  z-index: 3;
  left: var(--gut);
  bottom: clamp(7rem, 20svh, 12rem);
  margin: 0;
  white-space: nowrap;
  font-size: clamp(2.1rem, 5.9vw, 7.2rem);
  font-weight: 160;
  line-height: 0.95;
  letter-spacing: -0.048em;
  word-spacing: 0.08em;
  text-transform: uppercase;
}

/* ── after the walk ─────────────────────────────────────────────────── */
/* One frame, used twice: the exterior wipes in from the left, and once it has
   landed the interior rises through it from below and takes the frame over.
   The two sit in exactly the same rectangle, so the second reads as a cut in
   the same shot rather than as a second picture on the page.

   The frame runs edge to edge, which leaves no room on the page to scroll past
   while it changes — so it is pinned instead. The track is what the exchange is
   paced against: the wipe early, then a long hold on the exterior, then the
   takeover. Lengthen the track and the whole thing simply gets slower. */
.estate__spread { height: 250vh; }
.estate__spread-sticky {
  position: sticky;
  top: 0;
  height: 100svh;
  display: flex;
  align-items: center;
  overflow: clip;
}
.estate__pair {
  position: relative;
  width: 100%;
}
.estate__wide {
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 9;
  max-height: 86svh;
  margin: 0;
  overflow: clip;
  background: var(--black);
}
.estate__inset {
  position: absolute;
  inset: 0;
  margin: 0;
  overflow: clip;
  background: var(--black);
}

/* "Emotion before the viewing." sits exactly in the middle of the black
   between the picture above and the red room below. The black above is two
   things added together — the band left under the frame when the pin lets it
   go, and the padding — while the black below is the padding alone, so equal
   paddings put the line about 30px low. The band is measured instead of
   guessed: the frame is 16:9 of the full width, capped at 86svh, centred in a
   100svh sticky, so the band is (100svh - frame) / 2. Percentages in padding
   resolve against the width of the containing block, which is exactly the
   frame's width, scrollbar and all.

   Where that band is taller than the air wanted (a phone, where the frame is
   short), the bottom grows to match it instead. */
.estate__after {
  --air: clamp(7rem, 20vh, 13rem);
  --band: calc((100svh - min(56.25%, 86svh)) / 2);
  position: relative;
  padding: max(0px, calc(var(--air) - var(--band))) var(--gut) max(var(--air), var(--band));
  max-width: 1640px;
  margin: 0 auto;
}
.estate__wide img,
.estate__inset img {
  position: absolute;
  inset: -8% 0;
  width: 100%;
  height: 116%;
  object-fit: cover;
  will-change: transform;
}
html.js:not(.no-motion) .estate__wide { clip-path: inset(0 100% 0 0); }
html.js:not(.no-motion) .estate__inset { clip-path: inset(100% 0 0 0); }

.estate__note {
  margin: 0;
  font-size: clamp(1rem, 1.5vw, 1.55rem);
  font-weight: 190;
  letter-spacing: -0.018em;
}

/* ═══ 03 — INTRO ═══════════════════════════════════════════════════════ */
.intro {
  /* The red room, finished like lacquered metal rather than painted flat:
     a deep oxblood body, one broad reflection falling from the top left, the
     far corner sinking into shadow, and a narrow specular band that the
     scroll drags across the panel (--sheen). White on this depth reads at
     roughly 9:1, so the type stays razor sharp. */
  --bg: #5e0810;
  --fg: var(--white);
  --dim: rgb(242 240 237 / 0.72);
  --hair: rgb(242 240 237 / 0.22);

  position: relative;
  z-index: 5;
  min-height: 104svh;
  display: flex;
  align-items: center;
  padding: clamp(7rem, 18vh, 14rem) var(--gut) clamp(6rem, 14vh, 11rem);
  color: var(--fg);
  /* What reads as metal rather than paint: two reflections travelling at
     different speeds (the parallax between them is the tell), and a shadow
     side that turns cool and near-black — the "flop" of a metallic finish. */
  background:
    /* the specular band, swept across by the scroll */
    linear-gradient(112deg,
      rgb(255 214 214 / 0) calc(var(--sheen) - 16%),
      rgb(255 214 214 / 0.07) calc(var(--sheen) - 5%),
      rgb(255 236 236 / 0.19) var(--sheen),
      rgb(255 214 214 / 0.06) calc(var(--sheen) + 4%),
      rgb(255 214 214 / 0) calc(var(--sheen) + 17%)),
    /* a broad environment reflection, moving slower than the band */
    linear-gradient(112deg,
      rgb(255 180 184 / 0) calc(var(--sheen2) - 17%),
      rgb(255 180 184 / 0.085) var(--sheen2),
      rgb(255 180 184 / 0) calc(var(--sheen2) + 17%)),
    /* the top of a curved panel catching the light */
    radial-gradient(115% 80% at 20% -10%, rgb(218 42 54 / 0.72), rgb(218 42 54 / 0) 60%),
    linear-gradient(180deg, rgb(255 130 138 / 0) 0%, rgb(255 130 138 / 0.1) 6%, rgb(255 130 138 / 0) 15%),
    /* flop: the far corner turns cool and almost black */
    radial-gradient(100% 90% at 104% 108%, rgb(10 0 7 / 0.86), rgb(10 0 7 / 0) 66%),
    linear-gradient(158deg, #8e111b 0%, #600a12 42%, #31040a 100%);
}
/* a machined edge where the panel meets the black, top and bottom */
.intro::before,
.intro::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  height: 1px;
  background: linear-gradient(90deg, rgb(230 70 78 / 0), rgb(230 70 78 / 0.85) 30%, rgb(255 190 190 / 0.9) 50%, rgb(230 70 78 / 0.85) 70%, rgb(230 70 78 / 0));
  pointer-events: none;
}
.intro::before { top: 0; z-index: 2; }
.intro::after { bottom: 0; opacity: 0.55; z-index: 2; }
/* the painted panel covers the CSS lacquer, which stays underneath as the
   fallback when WebGL is unavailable */
.intro__paint {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
}
.intro__inner {
  position: relative;
  z-index: 1;
  width: 100%;
  max-width: 1560px;
  margin: 0 auto;
}
/* three words, set as large as the room allows */
.intro { --intro-size: clamp(3rem, 11.5vw, 14rem); }
.intro__statement {
  font-size: var(--intro-size);
  font-weight: 180;
  line-height: 0.9;
  letter-spacing: -0.05em;
  word-spacing: 0.06em;
  text-transform: uppercase;
  font-variation-settings: 'wdth' var(--intro-wdth, 100);
}
/* Sits directly under the headline, on the edge of its ink rather than its
   box: at this size the I stands 0.094em inside its own box (measured), and
   the small C only 0.047em inside its own, so the line is moved in by the
   difference and the two left edges fall on one vertical. */
.intro__aside {
  margin-top: clamp(2rem, 6vh, 4.5rem);
  margin-left: 0;
  padding-left: calc(var(--intro-size) * 0.094 - 0.047em);
  /* one line, or none. Broken in two, the sentence leaves "AI film" hanging
     alone under it, which reads as a mistake rather than as a second line. */
  max-width: 68ch;
  text-align: left;
  font-size: clamp(0.92rem, 1.15vw, 1.22rem);
  line-height: 1.5;
  letter-spacing: 0.005em;
  color: var(--dim);
}

/* ═══ 04 — HOSPITALITY ═════════════════════════════════════════════════ */
.hosp {
  position: relative;
  z-index: 6;
  background: var(--bg);
}
.hosp__track { height: 340vh; }
.hosp__sticky {
  position: sticky;
  top: 0;
  height: 100svh;
  overflow: clip;
}

html.js .hosp__chapter { opacity: 0; }
/* The work this chapter covers is named here, at the top, with the chapter
   number — not under the closing line. Set beneath "From the space to the
   detail." it turned a statement into a caption; the sentence stopped being
   something said and became a label on the pictures above it. Real Estate lost
   the same line earlier and reads better for it. Here it keeps its job by
   changing places: it introduces the chapter instead of explaining it. */
/* Light held off the top and the bottom corner, the same device Real Estate
   uses. The films run edge to edge now, so the chapter mark and the captions
   always sit on picture — and the restaurant's top left is its brightest
   part, where small grey type simply disappeared. */
.hosp__shade {
  position: absolute;
  inset: 0;
  z-index: 3;
  pointer-events: none;
  background:
    linear-gradient(to bottom, rgb(5 5 6 / 0.58) 0%, rgb(5 5 6 / 0) 30%),
    linear-gradient(to top, rgb(5 5 6 / 0.5) 0%, rgb(5 5 6 / 0) 22%);
}

.hosp__chapter {
  position: absolute;
  z-index: 4;
  top: clamp(4.6rem, 12vh, 8rem);
  left: var(--gut);
  font-size: clamp(0.6rem, 0.72vw, 0.74rem);
  letter-spacing: 0.3em;
  font-weight: 330;
  text-transform: uppercase;
  color: var(--grey-pale);
}
.hosp__chapter-mark {
  display: flex;
  align-items: center;
  gap: 0.85rem;
  margin: 0;
}
/* On the page's one left edge — the gutter the header name and the red "02"
   already stand on. It used to be indented to sit under "Hospitality" and in
   fact landed on neither: 93px, against 41 for the name and the number and 114
   for the word. A private edge for one line is exactly the kind of small
   disorder the eye finds without knowing what it found. */
.hosp__chapter-work {
  margin: 0.95em 0 0;
  padding-left: 0;
  font-size: 0.92em;
  letter-spacing: 0.24em;
  /* pale rather than dim: this line lies over the brightest corner of the
     restaurant, where the standard muted grey reads as smudge */
  color: var(--grey-pale);
  opacity: 0.78;
}
.hosp__chapter-num { font-weight: 420; color: var(--red); }
.hosp__chapter-sep {
  width: clamp(22px, 3vw, 46px);
  height: 1px;
  background: currentColor;
  opacity: 0.4;
}

/* The stage is the whole screen. The wide film fills it edge to edge, and the
   vertical film is no longer a panel standing beside it but a second full
   screen that rises through it and takes the frame over — the same cut the
   Real Estate frame makes, so the two chapters speak one language.

   A 9:16 film cannot fill a 16:9 screen without throwing most of its frame
   away, so it stands at full height on the section's own black rather than
   being cropped to fit. Nothing of the first film is left showing behind it. */
.hosp__stage {
  position: absolute;
  inset: 0;
}

.hosp__film,
.hosp__panel {
  position: relative;
  margin: 0;
  background: var(--black-deep);
}
.hosp__film { width: 100%; aspect-ratio: 16 / 9; }
html.js .hosp__panel { opacity: 0; }
.hosp__panel { width: min(62vw, 420px); aspect-ratio: 810 / 1436; }
/* Both films are shown whole. Filling the screen would mean cropping, and on
   these two that costs the shot: the vertical loses everything but a band
   across the middle of the menu, and the wide one loses the edges of the room
   the moment the window is not 16:9. Each film is fitted inside the screen
   instead, centred on the section's black — which is the material the rest of
   the site is made of, not a gap. */
.hosp__film video,
.hosp__panel video {
  width: 100%;
  height: 100%;
  object-fit: contain;
}

/* The note. The menu film came with its own music, and it is good music, so
   the visitor may hear it: a small ring in the film's bottom-right corner.
   On, the ring fills red and the site's score steps aside; off, the film is
   silent again and the score comes back. It sits on the film itself, not on
   the section, so it is clearly the film's sound that it turns on. Its place
   is set by script from the film's own box, which is centred and sized
   differently in every layout. */
.hosp__music {
  position: absolute;
  left: var(--nx, auto);
  top: var(--ny, auto);
  right: auto;
  z-index: 2;
  display: grid;
  place-items: center;
  width: 2.5rem;
  height: 2.5rem;
  padding: 0;
  border: 1px solid rgb(242 240 237 / 0.5);
  border-radius: 50%;
  background: rgb(5 5 6 / 0.38);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
  color: var(--white);
  transition: border-color 0.4s var(--ease), background-color 0.4s var(--ease), transform 0.4s var(--ease);
}
html.js .hosp__music { opacity: 0; visibility: hidden; }
/* the ring stays small; what a thumb can hit is larger (48px) */
.hosp__music::before {
  content: '';
  position: absolute;
  inset: -4px;
  border-radius: 50%;
}
.hosp__music svg {
  width: 1.05rem;
  height: 1.05rem;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.3;
  stroke-linejoin: round;
}
.hosp__music svg circle { fill: currentColor; stroke: none; }
.hosp__music:hover,
.hosp__music:focus-visible { border-color: var(--red); }
.hosp__music:focus-visible { outline: 1px solid var(--red); outline-offset: 3px; }
.hosp__music[aria-pressed='true'] {
  border-color: var(--red);
  background: var(--red);
}
/* while it plays, one slow ring breathes out of the button */
.hosp__music::after {
  content: '';
  position: absolute;
  inset: -1px;
  border: 1px solid var(--red);
  border-radius: 50%;
  opacity: 0;
  pointer-events: none;
}
.hosp__music[aria-pressed='true']::after { animation: note-ring 2.4s var(--ease) infinite; }
@keyframes note-ring {
  from { transform: scale(1); opacity: 0.7; }
  to   { transform: scale(1.7); opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .hosp__music[aria-pressed='true']::after { animation: none; }
}

/* The captions' base rule has to come BEFORE the pinned layout below. It used
   to follow it, and at equal specificity the later rule wins — so "left: 0"
   beat the pinned "left: var(--gut)" and THE ROOM sat flush against the edge
   of the screen, off the one left edge everything else stands on. */
html.js .hosp__tag { opacity: 0; }
.hosp__tag {
  position: absolute;
  left: 0;
  bottom: -1.9em;
  font-size: clamp(0.6rem, 0.7vw, 0.72rem);
  letter-spacing: 0.3em;
  font-weight: 330;
  text-transform: uppercase;
  color: var(--grey-pale);
  white-space: nowrap;
}

/* pinned: the room surrounds you, the detail is presented to you.

   These two films cannot share one frame. A 16:9 film and a 9:16 film want
   opposite things from the screen: give them the same air top and bottom and
   the wide one shrinks into an island; give the wide one its width and the
   tall one runs off both edges. So they stop being the same frame.

   The room is the screen — full bleed, no border, nothing around it. On a
   16:9 monitor covering the screen costs it 0.3% of its height, because the
   file is 1920x1083 rather than 1920x1080; the room has nothing at its edges
   worth keeping, and being surrounded is the whole point of that shot.

   The detail is an object on a table: whole, untouched, standing in the
   middle of the black with a wide band of air above and below it. The
   contrast between the two is the effect — you are inside the room, then you
   are looking at the thing. */
@media (min-width: 721px) and (min-aspect-ratio: 19/20) {
  .hosp__film,
  .hosp__panel {
    position: absolute;
    inset: 0;
    width: auto;
    aspect-ratio: auto;
  }
  /* The room fills the screen, but the crop that costs is capped. The frame
     is never allowed to be squarer than 1.6, so on a 16:9 monitor it is the
     whole screen and loses 0.3%, while on a tall or squarish window it turns
     into a wide band with air above and below and still loses no more than a
     tenth of its width. The alternative — fitting the whole frame always —
     is what made it read as a small island on a wide screen. */
  .hosp__film {
    margin: auto;
    width: 100%;
    height: min(100svh, calc(100vw / 1.6));
  }
  .hosp__film video { object-fit: cover; }

  /* Centred the same way the room is — absolute with margin auto. A grid with
     place-items: center looked right and was not: the video resolved its own
     height against an indefinite row and landed low on the screen. */
  .hosp__panel video {
    position: absolute;
    inset: 0;
    margin: auto;
    height: min(72svh, 100%);
    width: auto;
    max-width: calc(100vw - 2 * var(--gut));
    object-fit: contain;
  }

  /* The detail does not cut in, it develops. The mask edge is a wide soft
     band rather than a line, so the film first appears as a ghost lying over
     the room and firms up as the band passes; behind that edge it is still
     out of focus and slightly too large, and it resolves as it arrives. The
     room meanwhile darkens and drifts a little towards the viewer, so the two
     read as depth — one falling back, one coming forward — instead of one
     sliding over the other.

     Only the arriving film is blurred. Blurring both would be the same
     picture and twice the work: the room is full screen, and a full-screen
     blur on a playing video costs frames on modest hardware. */
  html.js .hosp__panel {
    opacity: 1;
    overflow: hidden;
    -webkit-mask-image: linear-gradient(to top, #000 calc(var(--rise) * 138% - 38%), transparent calc(var(--rise) * 138%));
    mask-image: linear-gradient(to top, #000 calc(var(--rise) * 138% - 38%), transparent calc(var(--rise) * 138%));
  }
  .hosp__panel video {
    filter: blur(calc((1 - var(--rise)) * 13px));
    transform: scale(calc(1 + (1 - var(--rise)) * 0.055));
    will-change: filter, transform;
  }
  .hosp__film video {
    filter: brightness(calc(1 - var(--rise) * 0.5));
    transform: scale(calc(1 + var(--rise) * 0.045));
    will-change: filter, transform;
  }
  /* both captions land in the same corner, so the cut reads as one frame
     changing its contents rather than as two different layouts */
  .hosp__tag { left: var(--gut); bottom: clamp(3.2rem, 9vh, 5.4rem); }
}
/* closing statement */
.hosp__statement {
  position: relative;
  padding: clamp(8rem, 24vh, 18rem) var(--gut) clamp(7rem, 20vh, 14rem);
  max-width: 1560px;
  margin: 0 auto;
}
.hosp__headline {
  font-size: clamp(2.6rem, 8.6vw, 10rem);
  font-weight: 165;
  line-height: 0.92;
  letter-spacing: -0.05em;
  /* tight tracking closes the word gaps too — give them back a little */
  word-spacing: 0.09em;
  font-variation-settings: 'wdth' 100;
  text-transform: uppercase;
}
/* The word the sentence turns on: serif italic, and red like "want" in the
   Automoto close, which carries exactly the same treatment. Grey was a
   contradiction — it is darker than the white around it, so the emphasised
   word receded instead of landing. */
.hosp__headline em {
  font-family: 'Instrument Serif', Georgia, serif;
  font-style: italic;
  font-weight: 400;
  text-transform: none;
  letter-spacing: -0.005em;
  color: var(--red);
}

/* ═══ 05 — AUTOMOTO ════════════════════════════════════════════════════
   The chapter where red stops being an accent and becomes the material.
   Black room, photographs at full bleed, one idea per frame.            */
.auto {
  position: relative;
  z-index: 6;
  background: var(--black-deep);
  color: var(--white);
}

/* — opening frame — */
.auto__open {
  position: relative;
  height: 112svh;
  overflow: clip;
  background: var(--black-deep);
}
.auto__open img {
  position: absolute;
  inset: -6% 0 -6% 0;
  width: 100%;
  height: 112%;
  object-fit: cover;
  object-position: 50% 58%;
  will-change: transform, clip-path;
}
html.js:not(.no-motion) .auto__open img { clip-path: inset(0 0 100% 0); }
/* a floor of black under the frame so the chapter label always reads */
.auto__open::after {
  content: '';
  position: absolute;
  inset: auto 0 0 0;
  height: 42%;
  background: linear-gradient(to top, var(--black-deep) 4%, rgb(5 5 6 / 0));
  pointer-events: none;
}
.auto__chapter {
  position: absolute;
  z-index: 2;
  left: var(--gut);
  bottom: clamp(2rem, 7vh, 4.5rem);
  display: flex;
  align-items: center;
  gap: 0.95rem;
  font-size: clamp(0.62rem, 0.74vw, 0.76rem);
  font-weight: 330;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--white);
}
html.js .auto__chapter { opacity: 0; }
.auto__chapter-num { font-weight: 420; color: var(--red); }
.auto__chapter-sep {
  width: clamp(26px, 3.4vw, 54px);
  height: 1px;
  background: var(--red);
}

/* — the pinned stage — */
.auto__track { height: 420vh; }
.auto__sticky {
  position: sticky;
  top: 0;
  height: 100svh;
  overflow: clip;
}
.auto__stage {
  position: absolute;
  inset: 0;
}
.auto__stage canvas { display: block; width: 100%; height: 100%; }
.auto__fallback {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
html.js .auto__fallback { opacity: 0; }
html.js.no-webgl .auto__fallback { opacity: 1; }

/* deepens only while a statement is on screen, and only on its side, so the
   type always reads without dimming the whole photograph */
.auto__veil {
  position: absolute;
  inset: 0;
  opacity: 0;
  pointer-events: none;
}
.auto__veil--one {
  background: linear-gradient(20deg, rgb(5 5 6 / 0.86) 0%, rgb(5 5 6 / 0.4) 45%, rgb(5 5 6 / 0) 75%);
}
.auto__veil--two {
  background: linear-gradient(200deg, rgb(5 5 6 / 0.86) 0%, rgb(5 5 6 / 0.4) 45%, rgb(5 5 6 / 0) 75%);
}

.auto__say {
  position: absolute;
  z-index: 3;
  margin: 0;
  white-space: nowrap;
  font-size: clamp(2.1rem, 6.1vw, 7.4rem);
  font-weight: 160;
  line-height: 0.95;
  letter-spacing: -0.048em;
  word-spacing: 0.08em;
  font-variation-settings: 'wdth' 100;
  text-transform: uppercase;
  color: var(--white);
}
.auto__say--one {
  left: var(--gut);
  bottom: clamp(3rem, 14svh, 9rem);
}
.auto__say--two {
  right: var(--gut);
  top: clamp(6rem, 20svh, 12rem);
  text-align: right;
}
/* the word that moves, moves */
.auto__say em {
  display: inline-block;
  font-style: normal;
  color: var(--red);
  will-change: transform;
}

/* — notes beside a tall frame — */
.auto__notes {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: clamp(2rem, 6vw, 7rem);
  padding: clamp(7rem, 20vh, 14rem) var(--gut);
  max-width: 1640px;
  margin: 0 auto;
}
.auto__lines {
  list-style: none;
  counter-reset: note;
  margin: 0;
  padding: 0;
  flex: 1 1 auto;
  max-width: 40rem;
}
.auto__lines li {
  counter-increment: note;
  position: relative;
  padding: clamp(1.3rem, 3.2vh, 2.1rem) 0 clamp(1.3rem, 3.2vh, 2.1rem) 3.4em;
  font-size: clamp(1.2rem, 2.1vw, 2.2rem);
  font-weight: 190;
  line-height: 1.15;
  letter-spacing: -0.022em;
}
/* a red index and a hairline that draws itself in */
.auto__lines li::before {
  content: counter(note, decimal-leading-zero);
  position: absolute;
  left: 0;
  top: clamp(1.55rem, 3.6vh, 2.45rem);
  font-size: clamp(0.6rem, 0.7vw, 0.72rem);
  font-weight: 420;
  letter-spacing: 0.24em;
  color: var(--red);
}
.auto__lines li::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 1px;
  background: rgb(242 240 237 / 0.14);
  transform-origin: 0 50%;
  transform: scaleX(var(--rule, 1));
}
html.js .auto__lines li { --rule: 0; }

.auto__upright {
  position: relative;
  flex: 0 0 auto;
  width: min(34vw, 520px);
  aspect-ratio: 1500 / 1815;
  margin: 0;
  overflow: clip;
  background: var(--black);
}
.auto__upright img {
  position: absolute;
  inset: -8% 0;
  width: 100%;
  height: 116%;
  object-fit: cover;
  will-change: transform;
}
html.js:not(.no-motion) .auto__upright { clip-path: inset(100% 0 0 0); }

/* — the close — */
.auto__cta {
  position: relative;
  padding: clamp(8rem, 24vh, 16rem) var(--gut) clamp(8rem, 22vh, 15rem);
  text-align: center;
  background:
    radial-gradient(60% 55% at 50% 100%, rgb(211 34 43 / 0.16), rgb(211 34 43 / 0) 70%),
    var(--black-deep);
}
/* The line that drops into the close. It was a decoration painted by ::before;
   now it is an element, because a pseudo-element cannot be handed to the
   scroll. It is drawn downward as the visitor arrives — the gradient is fixed
   to the element, so the red tip is always the leading edge and what descends
   reads as a point of light rather than a growing bar.

   Without motion it simply stands there, full length, as it always did. */
.auto__cta-rule {
  position: absolute;
  top: 0;
  left: 50%;
  width: 1px;
  height: clamp(4rem, 12vh, 7rem);
  background: linear-gradient(to bottom, rgb(211 34 43 / 0), var(--red));
}
html.js:not(.no-motion) .auto__cta-rule {
  transform: scaleY(0);
  transform-origin: 50% 0;
  will-change: transform;
}
.auto__cta-eyebrow {
  font-size: clamp(0.6rem, 0.72vw, 0.74rem);
  font-weight: 330;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--grey-pale);
}
.auto__cta-line {
  margin: clamp(1.6rem, 4vh, 2.6rem) 0 clamp(2.6rem, 6vh, 4rem);
  font-size: clamp(2.3rem, 6.6vw, 7.4rem);
  font-weight: 165;
  line-height: 1;
  letter-spacing: -0.045em;
}
.auto__cta-line em {
  font-family: 'Instrument Serif', Georgia, serif;
  font-style: italic;
  font-weight: 400;
  letter-spacing: -0.01em;
  color: var(--red);
}
.auto__cta-btn {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 1.1rem;
  padding: 1.15em 1.9em 1.15em 2.1em;
  border: 1px solid rgb(242 240 237 / 0.34);
  border-radius: 999px;
  overflow: hidden;
  isolation: isolate;
  color: var(--white);
  text-decoration: none;
  font-size: clamp(0.64rem, 0.76vw, 0.78rem);
  font-weight: 360;
  letter-spacing: 0.26em;
  text-transform: uppercase;
  transition: border-color 0.5s var(--ease);
}
/* red fills the pill from the left on hover */
.auto__cta-btn::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  background: var(--red);
  transform: scaleX(0);
  transform-origin: 0 50%;
  transition: transform 0.7s var(--ease);
}
.auto__cta-btn:hover, .auto__cta-btn:focus-visible { border-color: var(--red); }
.auto__cta-btn:hover::before, .auto__cta-btn:focus-visible::before { transform: scaleX(1); }
/* the arrow is the one red thing in the button at rest: a small sign that
   this is where the page acts, without a red outline competing with the red
   "want" right above it. On hover the pill fills red and the arrow turns
   white, or it would vanish into the fill. */
.auto__cta-btn i {
  color: var(--red);
  transition: transform 0.7s var(--ease), color 0.5s var(--ease);
  position: relative;
  width: 1.6em;
  height: 1px;
  background: currentColor;
}
.auto__cta-btn i::after {
  content: '';
  position: absolute;
  right: 0;
  top: -3px;
  width: 7px;
  height: 7px;
  border-top: 1px solid currentColor;
  border-right: 1px solid currentColor;
  transform: rotate(45deg);
}
.auto__cta-btn:hover i, .auto__cta-btn:focus-visible i { transform: translateX(0.45em); color: var(--white); }

/* ═══ tail ═════════════════════════════════════════════════════════════ */
/* The close. It carried an eyebrow — "Three industries here. The method
   travels." — which was cut: it said the same thing as the line under it and
   said it first, so the sentence arrived already explained. The same mistake
   the services line was making under the Hospitality headline.

   What stood here before that was a list of the sections that do not exist yet
   — a premium site does not publish what it is missing — and the one button
   on the page pointed straight at it, promising a conversation and delivering
   a notice. Now it answers the button: three chapters turn into one claim, and
   the studio signs underneath. */
.close {
  position: relative;
  z-index: 6;
  padding: clamp(6rem, 17vh, 11rem) var(--gut) clamp(4rem, 10vh, 7rem);
  border-top: 1px solid var(--hair);
  background: var(--bg);
  max-width: 1560px;
  margin: 0 auto;
}
/* Optical alignment on the opening A. Its ink starts on the margin to the
   pixel — measured — but a capital A is a triangle: only its bottom vertex
   touches the edge and the rest of the letter leans away, so at reading height
   the word looks indented while "look" below it and the small line under that,
   both starting on flat stems, sit flush. The first line is pulled back by the
   difference; nothing else moves, so the page margin is still the margin. */
.close__line .reveal-line:first-child > span { margin-left: -0.022em; }
.close__line {
  margin: 0;
  max-width: 18ch;
  font-size: clamp(2.1rem, 5.6vw, 5.6rem);
  font-weight: 165;
  line-height: 0.98;
  letter-spacing: -0.045em;
  word-spacing: 0.08em;
}
.close__roles {
  margin: clamp(2.4rem, 6vh, 4rem) 0 0;
  font-size: clamp(0.62rem, 0.74vw, 0.78rem);
  letter-spacing: 0.24em;
  font-weight: 330;
  text-transform: uppercase;
  color: var(--grey-pale);
}
/* the studio's mark is a mask filled with currentColor, so it sits in the
   page at the same value as the type beside it instead of arriving as a
   chrome-and-gold badge from a different site */
.close__studio {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  margin: clamp(3.4rem, 9vh, 5.5rem) 0 0;
  font-size: clamp(0.56rem, 0.66vw, 0.68rem);
  letter-spacing: 0.26em;
  font-weight: 330;
  text-transform: uppercase;
  color: var(--dim);
}
/* the signature is also the way to the studio's own site */
.close__studio a {
  display: inline-flex;
  align-items: center;
  gap: 0.7rem;
  color: inherit;
  text-decoration: none;
  transition: color 0.4s var(--ease);
}
.close__studio a:hover,
.close__studio a:focus-visible { color: var(--white); }
.close__mono {
  flex: none;
  height: clamp(1.1rem, 1.5vw, 1.4rem);
  aspect-ratio: 448 / 395;
  background: currentColor;
  -webkit-mask: url('../assets/img/smartart-monogram.png') no-repeat center / contain;
  mask: url('../assets/img/smartart-monogram.png') no-repeat center / contain;
}

/* The proof: three awards the studio's own site won, each opening its award
   page, because a credential that cannot be checked is decoration.

   First they stood as three little columns squeezed between the roles and the
   contact line, and they read as what they were: something dropped into a
   block that was already finished. Now they are a small table with a name of
   its own, set on hairlines like the list in the brief, and on a wide screen
   they take the half of the close that was empty: the claim on the left, the
   evidence on the right, level with it. Not centred: everything in the close
   hangs from one left edge, and a centred block inside it is exactly what
   looks fallen in. */
.close__proof {
  container: proof / inline-size;
  max-width: 40rem;
  margin: clamp(3.2rem, 8vh, 5rem) 0 0;
}
.close__proof-tag {
  margin: 0 0 clamp(1rem, 2.4vh, 1.4rem);
  font-size: clamp(0.56rem, 0.64vw, 0.66rem);
  letter-spacing: 0.3em;
  font-weight: 330;
  text-transform: uppercase;
  color: var(--grey-pale);
}
.close__awards {
  margin: 0;
  padding: 0;
  list-style: none;
  border-bottom: 1px solid var(--hair);
}
.close__awards a {
  position: relative;
  display: grid;
  grid-template-columns: 10.25rem minmax(0, 1fr) auto;   /* fixed, so the titles line up */
  align-items: baseline;
  column-gap: clamp(1rem, 2vw, 2rem);
  padding: clamp(1.05rem, 2.3vh, 1.4rem) 0;
  border-top: 1px solid var(--hair);
  color: var(--white);
  text-decoration: none;
}
/* the red runs along the top of the row under the pointer */
.close__awards a::before {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  top: -1px;
  height: 1px;
  background: var(--red);
  transform: scaleX(0);
  transform-origin: 0 50%;
  transition: transform 0.7s var(--ease);
}
.close__awards a:hover::before,
.close__awards a:focus-visible::before { transform: scaleX(1); }
.close__award-org,
.close__award-date {
  font-size: clamp(0.56rem, 0.64vw, 0.66rem);
  letter-spacing: 0.24em;
  font-weight: 330;
  text-transform: uppercase;
  color: var(--grey-pale);
}
/* the tracking after the last letter is air; pulled back so the date ends
   where the arrow under it ends */
.close__award-date { color: var(--dim); margin-right: -0.24em; }
/* the date and, under it, the word that says the row can be opened */
.close__award-when {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 0.6rem;
}
.close__award-view {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  font-size: clamp(0.52rem, 0.58vw, 0.6rem);
  letter-spacing: 0.24em;
  font-weight: 400;
  text-transform: uppercase;
  color: var(--red);
  transition: color 0.4s var(--ease);
}
.close__award-view i {
  position: relative;
  width: 0.95rem;
  height: 1px;
  background: currentColor;
  transition: transform 0.6s var(--ease);
}
.close__award-view i::after {
  content: '';
  position: absolute;
  right: 0;
  top: -2.5px;
  width: 5px;
  height: 5px;
  border-top: 1px solid currentColor;
  border-right: 1px solid currentColor;
  transform: rotate(45deg);
}
.close__awards a:hover .close__award-view,
.close__awards a:focus-visible .close__award-view { color: var(--white); }
.close__awards a:hover .close__award-view i,
.close__awards a:focus-visible .close__award-view i { transform: translateX(0.3em); }
.close__award-title {
  font-size: clamp(1.15rem, 1.55vw, 1.65rem);
  font-weight: 240;
  letter-spacing: -0.02em;
  line-height: 1.1;
  transition: transform 0.6s var(--ease);
}
.close__awards a:hover .close__award-title,
.close__awards a:focus-visible .close__award-title { transform: translateX(0.3em); }

/* when the table is narrow (a phone, or the right column of a small laptop)
   each row folds: platform and date on one line, the award under them */
@container proof (max-width: 27rem) {
  .close__awards a { grid-template-columns: minmax(0, 1fr) auto; row-gap: 0.45rem; }
  .close__award-org { grid-column: 1; grid-row: 1; }
  .close__award-when { grid-column: 2; grid-row: 1 / span 2; }
  .close__award-title { grid-column: 1; grid-row: 2; }
}

/* claim left, evidence right, once there is room for both: below 1240px the
   claim itself needs the width, and the table follows it instead */
@media (min-width: 1240px) {
  .close {
    display: grid;
    /* the claim keeps its own width, never broken into three lines; the
       table takes what is left on the right edge */
    grid-template-columns: max-content minmax(20rem, 32rem);
    justify-content: space-between;
    column-gap: clamp(3rem, 6vw, 8rem);
    align-items: start;
  }
  .close__line { grid-column: 1; grid-row: 1; }
  /* these two take the width of the claim instead of setting it */
  .close__roles { grid-column: 1; grid-row: 2; contain: inline-size; }
  .close__reach { grid-column: 1; grid-row: 3; contain: inline-size; }
  /* no margin: the cap of "Recognition" then sits on the cap line of "Any" */
  .close__proof { grid-column: 2; grid-row: 1 / span 3; margin: 0; }
  .close__studio { grid-column: 1 / -1; grid-row: 4; margin-top: clamp(4.4rem, 12vh, 7.5rem); }
}

/* the ways to answer, under the claim: one quiet line in the same voice as
   the roles above it. The address is the last thing a visitor looks for, so
   it sits where the eye already is at the end, not in a block of its own. */
.close__reach {
  display: flex;
  flex-wrap: wrap;
  gap: 0.9rem 2.2rem;
  margin: clamp(2.2rem, 5.5vh, 3.6rem) 0 0;
  padding: 0;
  list-style: none;
  font-size: clamp(0.6rem, 0.72vw, 0.76rem);
  letter-spacing: 0.22em;
  font-weight: 360;
  text-transform: uppercase;
}
.close__reach a {
  position: relative;
  color: var(--white);
  text-decoration: none;
}
.close__reach a::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0.22em;              /* the tracking after the last letter is air */
  bottom: -0.55em;
  height: 1px;
  background: var(--red);
  transform: scaleX(0);
  transform-origin: 0 50%;
  transition: transform 0.6s var(--ease);
}
.close__reach a:hover::after,
.close__reach a:focus-visible::after { transform: scaleX(1); }

/* ═══ the brief ════════════════════════════════════════════════════════
   What the one button on the page opens. It arrives the way the site did: a
   red line is drawn across the middle of the screen and the dark opens from
   it. The answer is not collected, it is handed on: the chosen industry
   becomes the first line of a WhatsApp message or an email, so the visitor
   presses send once and arrives already introduced. Nothing is stored and
   nothing is sent by the page itself. */
.brief {
  position: fixed;
  inset: 0;
  z-index: 70;
  overflow-y: auto;
  overscroll-behavior: contain;
  background:
    radial-gradient(70% 60% at 50% 110%, rgb(211 34 43 / 0.14), rgb(211 34 43 / 0) 70%),
    var(--black-deep);
  color: var(--white);
  outline: none;
}
.brief[hidden] { display: none; }

.brief__inner {
  box-sizing: border-box;
  max-width: 1560px;
  min-height: 100%;
  margin: 0 auto;
  padding: clamp(6.5rem, 17vh, 10rem) var(--gut) clamp(4rem, 10vh, 6rem);
}
.brief__step[hidden] { display: none; }

.brief__eyebrow {
  margin: 0;
  font-size: clamp(0.6rem, 0.72vw, 0.74rem);
  font-weight: 330;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--grey-pale);
}
.brief__eyebrow span { color: var(--white); }
.brief__title {
  margin: clamp(1.2rem, 3vh, 2rem) 0 clamp(2.8rem, 7vh, 4.8rem);
  font-size: clamp(2.3rem, 5.8vw, 6.4rem);
  font-weight: 165;
  line-height: 1;
  letter-spacing: -0.045em;
  text-wrap: balance;         /* no single word left alone on a phone */
  outline: none;
}
.brief__title em {
  font-family: 'Instrument Serif', Georgia, serif;
  font-style: italic;
  font-weight: 400;
  letter-spacing: -0.01em;
  color: var(--red);
}

/* the nine answers read down, then across: two columns of a contents page */
.brief__list {
  display: grid;
  grid-template-rows: repeat(5, auto);
  grid-auto-flow: column;
  grid-auto-columns: minmax(0, 1fr);
  column-gap: clamp(2rem, 6vw, 7rem);
  margin: 0;
  padding: 0;
  list-style: none;
}
.brief__pick {
  display: flex;
  align-items: baseline;
  gap: clamp(1rem, 2vw, 1.8rem);
  width: 100%;
  padding: clamp(0.95rem, 2.1vh, 1.35rem) 0;
  border: 0;
  border-top: 1px solid var(--hair);
  background: none;
  color: var(--white);
  font: inherit;
  text-align: left;
}
.brief__no {
  flex: none;
  width: 2.2em;
  font-size: clamp(0.58rem, 0.66vw, 0.7rem);
  letter-spacing: 0.2em;
  font-variant-numeric: tabular-nums;
  color: var(--grey);
  transition: color 0.5s var(--ease);
}
.brief__name {
  flex: 1;
  font-size: clamp(1.15rem, 1.75vw, 1.9rem);
  font-weight: 240;
  letter-spacing: -0.02em;
  transition: transform 0.6s var(--ease);
}
/* the same arrow as on the button that opened this, drawn in on hover */
.brief__pick i,
.brief__way i {
  position: relative;
  flex: none;
  align-self: center;
  width: 1.6rem;
  height: 1px;
  background: var(--red);
  transform: scaleX(0);
  transform-origin: 0 50%;
  transition: transform 0.6s var(--ease);
}
.brief__pick i::after,
.brief__way i::after {
  content: '';
  position: absolute;
  right: 0;
  top: -3px;
  width: 7px;
  height: 7px;
  border-top: 1px solid var(--red);
  border-right: 1px solid var(--red);
  transform: rotate(45deg);
}
.brief__pick:hover .brief__no,
.brief__pick:focus-visible .brief__no { color: var(--red); }
.brief__pick:hover .brief__name,
.brief__pick:focus-visible .brief__name { transform: translateX(0.35em); }
.brief__pick:hover i,
.brief__pick:focus-visible i,
.brief__way:hover i,
.brief__way:focus-visible i { transform: scaleX(1); }
.brief__pick:focus-visible,
.brief__way:focus-visible,
.brief__close:focus-visible,
.brief__back:focus-visible { outline: 1px solid var(--red); outline-offset: 4px; }

/* step two: three ways to answer, as three lines of the same list */
.brief__ways {
  margin: 0;
  padding: 0;
  list-style: none;
  max-width: 1100px;
  border-bottom: 1px solid var(--hair);
}
.brief__way {
  display: flex;
  align-items: baseline;
  gap: clamp(1rem, 3vw, 3rem);
  padding: clamp(1.1rem, 2.6vh, 1.7rem) 0;
  border-top: 1px solid var(--hair);
  color: var(--white);
  text-decoration: none;
}
.brief__way-kind {
  flex: none;
  width: clamp(6rem, 11vw, 10rem);
  font-size: clamp(0.6rem, 0.72vw, 0.74rem);
  letter-spacing: 0.26em;
  font-weight: 330;
  text-transform: uppercase;
  color: var(--grey-pale);
}
.brief__way-value {
  flex: 1;
  min-width: 0;
  overflow-wrap: anywhere;
  font-size: clamp(1.2rem, 2.2vw, 2.3rem);
  font-weight: 220;
  letter-spacing: -0.025em;
  transition: transform 0.6s var(--ease);
}
.brief__way:hover .brief__way-value,
.brief__way:focus-visible .brief__way-value { transform: translateX(0.3em); }

.brief__social {
  display: flex;
  flex-wrap: wrap;
  gap: 0.9rem 2.2rem;
  margin: clamp(2.2rem, 5vh, 3.2rem) 0 0;
  font-size: clamp(0.6rem, 0.72vw, 0.74rem);
  letter-spacing: 0.24em;
  font-weight: 330;
  text-transform: uppercase;
}
.brief__social a {
  color: var(--grey-pale);
  text-decoration: none;
  transition: color 0.4s var(--ease);
}
.brief__social a:hover,
.brief__social a:focus-visible { color: var(--white); }
/* The same three awards, at the moment of deciding, and built the way the
   table at the end of the page is: the award in light type, and beside it a
   red "View award" with its arrow, the one red thing in each line. Grey and
   small they went unnoticed; all red they shouted and read as three links
   rather than three awards. The red now marks only what can be opened. */
.brief__awards {
  display: grid;
  grid-template-columns: max-content max-content;
  gap: 0.85rem 2.6rem;
  margin: clamp(2.6rem, 6vh, 4rem) 0 0;
  padding: 0;
  list-style: none;
  font-size: clamp(0.6rem, 0.7vw, 0.72rem);
  letter-spacing: 0.22em;
  text-transform: uppercase;
}
.brief__awards li { display: contents; }
/* each link is a whole row of the grid (subgrid), so the two columns line up
   across the three awards and the row, not just its words, is what you hit */
.brief__awards a {
  display: grid;
  grid-column: 1 / -1;
  grid-template-columns: subgrid;
  align-items: center;
  color: inherit;
  text-decoration: none;
}
.brief__awards a:focus-visible { outline: 1px solid var(--red); outline-offset: 4px; }
.brief__award-name {
  font-weight: 330;
  color: var(--white);
}
.brief__award-name span { color: var(--grey-pale); }
.brief__award-view {
  display: inline-flex;
  align-items: center;
  gap: 0.7em;
  font-weight: 400;
  color: var(--red);
  transition: color 0.4s var(--ease);
}
.brief__award-view i {
  position: relative;
  width: 1.1rem;
  height: 1px;
  background: currentColor;
  transition: transform 0.6s var(--ease);
}
.brief__award-view i::after {
  content: '';
  position: absolute;
  right: 0;
  top: -2.5px;
  width: 5px;
  height: 5px;
  border-top: 1px solid currentColor;
  border-right: 1px solid currentColor;
  transform: rotate(45deg);
}
.brief__awards a:hover .brief__award-view,
.brief__awards a:focus-visible .brief__award-view { color: var(--white); }
.brief__awards a:hover .brief__award-view i,
.brief__awards a:focus-visible .brief__award-view i { transform: translateX(0.3em); }
@media (max-width: 720px) {
  .brief__awards { grid-template-columns: minmax(0, 1fr); gap: 0.35rem; }
  .brief__award-view { margin-bottom: 0.9rem; }
}

.brief__close,
.brief__back {
  display: inline-flex;
  align-items: center;
  gap: 0.8rem;
  padding: 0.6rem 0;
  border: 0;
  background: none;
  color: var(--white);
  font: inherit;
  font-size: clamp(0.6rem, 0.72vw, 0.74rem);
  letter-spacing: 0.26em;
  font-weight: 360;
  text-transform: uppercase;
}
/* where the sound switch is, so the way out is where the eye goes for it */
.brief__close {
  position: fixed;
  top: clamp(1.1rem, 2.4vw, 2.1rem);
  right: var(--gut);
  z-index: 1;
}
.brief__close i {
  position: relative;
  width: 14px;
  height: 14px;
}
.brief__close i::before,
.brief__close i::after {
  content: '';
  position: absolute;
  left: 50%;
  top: 50%;
  width: 18px;
  height: 1px;
  margin-left: -9px;
  background: currentColor;
  transition: background-color 0.4s var(--ease), transform 0.6s var(--ease);
}
.brief__close i::before { transform: rotate(45deg); }
.brief__close i::after { transform: rotate(-45deg); }
.brief__close:hover i::before { transform: rotate(135deg); background: var(--red); }
.brief__close:hover i::after { transform: rotate(45deg); background: var(--red); }

.brief__back {
  margin: 0 0 clamp(1.4rem, 3.5vh, 2.4rem);
  color: var(--grey-pale);
  transition: color 0.4s var(--ease);
}
.brief__back:hover { color: var(--white); }
.brief__back i {
  position: relative;
  width: 1.3rem;
  height: 1px;
  background: currentColor;
}
.brief__back i::after {
  content: '';
  position: absolute;
  left: 0;
  top: -3px;
  width: 7px;
  height: 7px;
  border-bottom: 1px solid currentColor;
  border-left: 1px solid currentColor;
  transform: rotate(45deg);
}

/* the opening: the line first, then the dark opens from it, then the words */
html.js:not(.no-motion) .brief {
  clip-path: inset(calc(50% - 1px) 0 calc(50% - 1px) 0);
  transition: clip-path 0.55s var(--ease);
}
html.js:not(.no-motion) .brief.is-open {
  clip-path: inset(0 0 0 0);
  transition: clip-path 1s cubic-bezier(0.76, 0, 0.24, 1) 0.42s;
}
html.js:not(.no-motion) .brief::before {
  content: '';
  position: fixed;
  left: 0;
  right: 0;
  top: calc(50% - 0.5px);
  height: 1px;
  background: var(--red);
  transform: scaleX(0);
  pointer-events: none;
}
html.js:not(.no-motion) .brief.is-open::before { animation: brief-line 1.4s var(--ease) both; }
@keyframes brief-line {
  0%   { transform: scaleX(0); opacity: 1; }
  30%  { transform: scaleX(1); opacity: 1; }
  55%  { transform: scaleX(1); opacity: 1; }
  100% { transform: scaleX(1); opacity: 0; }
}
html.js:not(.no-motion) .brief__inner,
html.js:not(.no-motion) .brief__close {
  opacity: 0;
  transition: opacity 0.35s var(--ease), transform 0.35s var(--ease);
}
html.js:not(.no-motion) .brief__inner { transform: translateY(1.4rem); }
html.js:not(.no-motion) .brief.is-open .brief__inner,
html.js:not(.no-motion) .brief.is-open .brief__close {
  opacity: 1;
  transform: none;
  transition: opacity 1s var(--ease) 0.95s, transform 1.2s var(--ease) 0.95s;
}

@media (max-width: 720px) {
  .brief__inner { padding-top: 5.5rem; }
  .brief__list { grid-template-rows: none; grid-auto-flow: row; }
  .brief__way { flex-wrap: wrap; row-gap: 0.4rem; }
  .brief__way-kind { width: 100%; }
  .brief__way i { display: none; }
}

/* ═══════════════════════════════════════════════════════════════════════
   RESPONSIVE

   The dividing line is not a width but a shape. A landscape frame can hold
   the crossing composition — portrait beside the name. A portrait-shaped
   frame cannot: the picture fills the width and the name has nowhere to go,
   so those screens get a stacked cut of the same story instead.

   Keep these queries in step with STACK_Q / PIN_Q in js/app.js.
   ═══════════════════════════════════════════════════════════════════════ */

/* ── smaller landscape frames: re-cut, not shrunk ─────────────────────── */
@media (max-width: 1080px) {
  .hero__foot { flex-direction: column; align-items: flex-start; gap: 1.1rem; }

  .intro__statement { max-width: 16ch; }

}
@media (max-width: 1080px) and (min-aspect-ratio: 19/20) {
  .hero__canvas {
    right: -9vw;
    height: min(104svh, 92vw);
    bottom: calc(min(104svh, 92vw) * -0.0945);
  }
  .hero__type {
    width: min(64vw, 720px);
    bottom: 11svh;
    --name-wght: 132;
  }
}

/* ── stacked: phones, and any portrait-shaped screen up to tablet size ── */
@media (max-width: 720px), (max-width: 1080px) and (max-aspect-ratio: 19/20) {
  /* the portrait is cropped harder here, not made smaller, and the name sits
     clear underneath it so both still read at arm's length */
  /* Lifted off the bottom edge, the cutout would otherwise end in a hard
     horizontal cut floating in the middle of the frame. Fading the torso out
     lets him sit in the dark instead, and the name reads through it. */
  .hero__canvas {
    right: -21vw;
    height: min(70svh, 130vw);
    bottom: 31svh;
    -webkit-mask-image: linear-gradient(to bottom, #000 58%, transparent 96%);
    mask-image: linear-gradient(to bottom, #000 58%, transparent 96%);
  }
  .hero__type {
    left: var(--gut);
    right: var(--gut);
    width: auto;
    bottom: 10svh;
    --name-wght: 168;
  }
  .hero__type--back { clip-path: inset(-16% -12% 53% -12%); }
  .hero__type--front { clip-path: inset(47% -12% -16% -12%); }
  .hero__foot {
    inset: auto var(--gut) clamp(1rem, 4vw, 1.6rem) var(--gut);
  }
  .hero__cue { display: none; }

  .intro { min-height: 100svh; padding-top: clamp(6rem, 16vh, 9rem); }
  .intro__statement { letter-spacing: -0.045em; }

  /* hospitality becomes a directed vertical sequence */
  .hosp__track { height: auto; }
  .hosp__sticky { position: relative; height: auto; overflow: visible; padding: clamp(5rem, 14vh, 8rem) 0 0; }
  .hosp__chapter { position: relative; top: auto; left: auto; margin: 0 var(--gut) 1.6rem; }
  .hosp__stage { position: relative; inset: auto; display: block; }
  .hosp__film {
    position: relative;
    top: auto; left: auto;
    transform: none !important;
    width: 100%;
    max-height: none;
    margin-bottom: 4.2rem;
  }
  .hosp__panel {
    position: relative;
    top: auto; left: auto;
    transform: none !important;
    opacity: 1;
    width: 62%;
    max-height: none;
    /* centred: it hung to the right while a caption stood at the left edge
       to balance it; the captions are gone, and on a phone the film is the
       one thing on the screen */
    margin: 0 auto;
  }
  /* the same develop as on a large screen: a soft edge rising from below,
     the film out of focus and slightly large until it arrives */
  html.js:not(.no-motion) .hosp__panel {
    -webkit-mask-image: linear-gradient(to top, #000 calc(var(--rise) * 138% - 38%), transparent calc(var(--rise) * 138%));
    mask-image: linear-gradient(to top, #000 calc(var(--rise) * 138% - 38%), transparent calc(var(--rise) * 138%));
  }
  html.js:not(.no-motion) .hosp__panel video {
    filter: blur(calc((1 - var(--rise)) * 13px));
    transform: scale(calc(1 + (1 - var(--rise)) * 0.055));
  }
  .hosp__tag { left: var(--gut); bottom: -1.7em; }
  .hosp__statement { padding-top: clamp(7rem, 18vh, 10rem); }
}

/* ── real estate, stacked ──────────────────────────────────────────── */
@media (max-width: 720px), (max-width: 1080px) and (max-aspect-ratio: 19/20) {
  .estate__track { height: 300vh; }
  .estate__say { white-space: normal; right: var(--gut); font-size: clamp(2rem, 8.4vw, 3.4rem); bottom: clamp(6rem, 18svh, 9rem); }
  .estate__foot { flex-direction: column; align-items: flex-start; gap: 0.9rem; }
  .estate__progress { width: 100%; }
  .estate__spread { height: 200vh; }
}

/* ── phones only: the proportions of a tall screen ─────────────────────
   On a computer the two stills fill the screen and the black around them is
   a frame. On a phone the same rules left a 16:9 picture as a band a quarter
   of the screen high, 300px of black above and below it, held still for two
   full scrolls while only one picture changed: not air, an empty scroll.
   Here the exchange is the same (the exterior wipes in, the interior rises
   over it and replaces it), but the pin is only as tall as the picture, held
   in the middle of the screen, so the film above and the line below are no
   longer kept a whole screen away. The black around "Emotion before the
   viewing." is a third of what it was: still air, measured for a phone.

   The restaurant goes the other way. On a computer the room is the screen and
   the menu is an object standing in it; on a phone the room was a small band,
   so both films read as two pictures on black and the contrast was lost. The
   room now fills the phone's height like the villa film does, and the menu
   arrives after it at 62%, an object again. */
@media (max-width: 720px) {
  /* the pin is the picture's own height (16:9 of the width), held in the
     middle of the screen; the track is that plus one screen of scroll, which
     is what the exchange is paced against, as on a large screen */
  .estate__spread {
    height: calc(56.25vw + 100svh);
    margin-top: clamp(5rem, 13vh, 7rem);
  }
  .estate__spread-sticky {
    top: calc(50svh - 28.125vw);
    height: 56.25vw;
    display: block;
  }
  .estate__after { padding: clamp(5rem, 13vh, 7rem) var(--gut); }

  .hosp__film { aspect-ratio: auto; height: 100svh; margin-bottom: clamp(5rem, 13vh, 7rem); }
  .hosp__film video { object-fit: cover; }
}

/* ── automoto, stacked ─────────────────────────────────────────────── */
@media (max-width: 720px), (max-width: 1080px) and (max-aspect-ratio: 19/20) {
  .auto__open { height: 80svh; }
  .auto__open img { object-position: 38% 60%; }
  .auto__track { height: 320vh; }
  .auto__say { white-space: normal; font-size: clamp(1.9rem, 8vw, 3.2rem); }
  .auto__say--one { bottom: clamp(3rem, 12svh, 6rem); }
  .auto__say--two { left: var(--gut); right: var(--gut); top: clamp(5.5rem, 16svh, 8rem); text-align: left; }
  .auto__veil { background: linear-gradient(0deg, rgb(5 5 6 / 0.8), rgb(5 5 6 / 0.25) 60%, rgb(5 5 6 / 0.7)); }
  .auto__notes { flex-direction: column; align-items: stretch; padding-top: clamp(5rem, 12vh, 8rem); }
  .auto__upright { width: 100%; max-width: 560px; margin-left: auto; }
  .auto__lines { max-width: none; }
}

/* ── phone only: the chrome sheds everything it can ───────────────────── */
@media (max-width: 720px) {
  /* one line needs 443px here and a phone has 335: fitting it would take the
     type below 8px. On a phone the three roles stack, as they did, and the
     red rules — which only make sense between words on a line — step aside */
  .hero__roles {
    font-size: 0.6rem;
    letter-spacing: 0.24em;
    flex-direction: column;
    align-items: flex-start;
    gap: 0.28em;
  }
  .hero__roles .reveal-line + .reveal-line > span::before { display: none; }
  .chrome__meta { display: none; }
  /* the name is the only thing in the header now, so it tightens instead of
     disappearing the way the studio wordmark used to */
  .chrome__name { font-size: 0.6rem; letter-spacing: 0.2em; margin-right: -0.2em; }
  .sound__label { display: none; }
  .intro { --intro-size: clamp(2.4rem, 15vw, 4.8rem); }
  .intro__aside { margin-top: 2.6rem; font-size: 1rem; }

  /* The lists of services are set in wide-tracked capitals, which on a phone
     run past the column and drop a two-word tail onto a second line — "WEB
     EXPERIENCE" and "AI FILM" were landing alone and reading as a mistake.
     Tighter tracking buys room, balanced wrapping splits the line evenly
     instead of leaving an orphan, and the leading opens so two lines of
     capitals do not collide. */
  .auto__cta-eyebrow,
  .close__roles,
  .hosp__chapter-work {
    letter-spacing: 0.15em;
    text-wrap: balance;
    line-height: 1.85;
  }
  /* the chapter line is indented to clear the rule beside "02"; on a phone
     that indent only pushes the wrap further out */
  .hosp__chapter-work { padding-left: 0; }
}

/* ═══════════════════════════════════════════════════════════════════════
   REDUCED MOTION — the same sequence, told without movement. The sticky
   scene is unwound into a plain stack, since a pinned scrub has nothing
   left to say once the motion is removed.
   ═══════════════════════════════════════════════════════════════════════ */
@media (prefers-reduced-motion: reduce) {
  .hero__cue-track i, .sound__bars i { animation: none !important; }
  html .reveal-line > span, html .hero__word { transform: none !important; }
  .hero__cue { display: none; }

  .hero { height: 100svh; }
  .hero__type { opacity: 1 !important; }

  .hosp__track { height: auto; }
  .hosp__sticky {
    position: relative;
    height: auto;
    overflow: visible;
    padding: clamp(5rem, 14vh, 8rem) 0 0;
  }
  .hosp__chapter {
    position: relative;
    top: auto;
    left: auto;
    opacity: 1 !important;
    margin: 0 var(--gut) 1.8rem;
  }
  .hosp__stage { position: relative; inset: auto; display: block; }
  .hosp__film {
    position: relative;
    top: auto;
    left: auto;
    transform: none !important;
    width: min(88vw, 1640px);
    max-height: none;
    margin: 0 auto clamp(4rem, 9vh, 6.5rem);
  }
  .hosp__panel {
    position: relative;
    top: auto;
    left: auto;
    transform: none !important;
    opacity: 1 !important;
    width: min(30vw, 380px);
    max-height: none;
    margin: 0 auto;
  }
  .hosp__panel { --rise: 1; mask-image: none !important; -webkit-mask-image: none !important; aspect-ratio: 810 / 1436; }
  .hosp__panel video, .hosp__film video { filter: none !important; transform: none !important; }
  .hosp__tag { opacity: 1 !important; position: relative; left: auto; bottom: auto; margin-top: 1.1rem; }

  .auto__track { height: auto; }
  .auto__sticky { position: relative; }
  .auto__fallback { opacity: 1 !important; }
  .auto__veil { opacity: 1 !important; }
  .auto__chapter { opacity: 1 !important; }
  .auto__lines li { --rule: 1 !important; }

  .estate__track { height: auto; }
  .estate__sticky { position: relative; height: 82svh; }
  .estate__chapter, .estate__foot { opacity: 1 !important; }
  .estate__wide, .estate__inset { clip-path: none !important; }
  .estate__spread { height: auto; }
  .estate__after { padding-top: var(--air); padding-bottom: var(--air); }
  .estate__spread-sticky { position: relative; height: auto; display: block; }
  .estate__inset { position: relative; inset: auto; margin-top: clamp(1.2rem, 3vw, 2rem); aspect-ratio: 16 / 9; }
}
