/* styles/tokens.css */
/* Design tokens. Section CSS uses these, never raw colour values. */
:root {
  /* Colour */
  --bone: #F2EFE9;
  --paper: #E8E4DC;
  --ink: #141413;
  /* Stone and clay are a shade deeper than first specced so small text passes WCAG AA (4.5:1)
     for stone on paper and bone on clay. */
  --stone: #66625A;
  --line: rgba(20, 20, 19, .14);
  --line-inverse: rgba(242, 239, 233, .18);
  --clay: #A84B25;

  --bg: var(--bone);
  --fg: var(--ink);
  --muted: var(--stone);

  /* Type families */
  --font-display: 'Instrument Serif', 'Times New Roman', serif;
  --font-body: 'Geist', ui-sans-serif, system-ui, -apple-system, 'Segoe UI', sans-serif;
  --font-mono: 'Geist Mono', ui-monospace, 'SFMono-Regular', Menlo, monospace;

  /* Fluid type scale */
  --fs-display: clamp(4rem, 14vw, 16rem);
  --fs-h1: clamp(3rem, 7vw, 8rem);
  --fs-h2: clamp(2.25rem, 4.6vw, 5rem);
  --fs-h3: clamp(1.5rem, 2.2vw, 2.25rem);
  --fs-body: clamp(1.0625rem, 0.3vw + 1rem, 1.125rem);
  --fs-small: 0.875rem;
  --fs-label: clamp(0.6875rem, 0.1vw + 0.65rem, 0.75rem);

  --lh-tight: 0.92;
  --lh-heading: 1.05;
  --lh-body: 1.55;
  --tracking-display: -0.02em;
  --tracking-label: 0.08em;

  /* Spacing */
  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 0.75rem;
  --space-4: 1rem;
  --space-5: 1.5rem;
  --space-6: 2rem;
  --space-7: 3rem;
  --space-8: 4rem;
  --space-9: 6rem;
  --space-10: 8rem;

  /* Layout */
  --gutter: 16px;
  --container: 1680px;
  --section-pad: clamp(96px, 14vw, 220px);
  --radius-pill: 999px;

  /* Motion */
  --ease-out: cubic-bezier(.16, 1, .3, 1);
  --ease-in-out: cubic-bezier(.65, 0, .35, 1);
  --dur-fast: 0.3s;
  --dur: 0.6s;
  --dur-slow: 1.2s;
}

@media (min-width: 768px) {
  :root {
    --gutter: 24px;
  }
}

/* styles/base.css */
/* Reset, base typography, focus styles and JS-gated reveal start states. */
*,
*::before,
*::after {
  box-sizing: border-box;
}

* {
  margin: 0;
}

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

body {
  min-height: 100vh;
  background: var(--bg);
  color: var(--fg);
  font-family: var(--font-body);
  font-size: var(--fs-body);
  font-weight: 400;
  line-height: var(--lh-body);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
}

img,
picture,
video,
canvas,
svg {
  display: block;
  max-width: 100%;
}

img {
  height: auto;
}

input,
button,
textarea,
select {
  font: inherit;
  color: inherit;
}

button {
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
}

a {
  color: inherit;
  text-decoration: none;
}

ul[role='list'],
ol[role='list'] {
  list-style: none;
  padding: 0;
}

h1,
h2,
h3,
h4 {
  font-family: var(--font-display);
  font-weight: 400;
  line-height: var(--lh-heading);
  letter-spacing: var(--tracking-display);
  text-wrap: balance;
}

h1 { font-size: var(--fs-h1); }
h2 { font-size: var(--fs-h2); }
h3 { font-size: var(--fs-h3); }

p {
  text-wrap: pretty;
}

em,
i {
  font-style: italic;
}

::selection {
  background: var(--clay);
  color: var(--bone);
}

:focus-visible {
  outline: 2px solid var(--clay);
  outline-offset: 3px;
}

/* Skip-link and in-page targets get focus from script; the outline would only frame the whole section. */
[tabindex='-1']:focus {
  outline: none;
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

.skip-link {
  position: fixed;
  top: var(--space-3);
  left: var(--space-3);
  z-index: 1000;
  padding: var(--space-2) var(--space-4);
  background: var(--ink);
  color: var(--bone);
  border-radius: var(--radius-pill);
  transform: translateY(-200%);
  transition: transform var(--dur-fast) var(--ease-out);
}

.skip-link:focus-visible {
  transform: none;
}

/* Reveal start states: only with JS and motion allowed, so content is visible otherwise.
   Line reveals are hidden by reveal.js itself once SplitText has wrapped the lines. */
@media (prefers-reduced-motion: no-preference) {
  html.js [data-reveal='fade'] {
    opacity: 0;
  }

  html.js [data-reveal='image'] {
    clip-path: inset(100% 0 0 0);
  }
}

/* styles/layout.css */
/* Container, 12-column grid and section spacing. */
.container {
  width: 100%;
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.grid {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  column-gap: var(--gutter);
}

.section {
  position: relative;
  padding-block: var(--section-pad);
}

.section--dark {
  --bg: var(--ink);
  --fg: var(--bone);
  --muted: var(--paper);
  --line: var(--line-inverse);
  background: var(--bg);
  color: var(--fg);
}

/* styles/components.css */
/* Shared components: labels, buttons, underline links, image frames, rules. */
.label {
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  font-weight: 400;
  line-height: 1.4;
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
}

.btn {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
  padding: 0.95em 1.6em;
  overflow: hidden;
  isolation: isolate;
  border: 1px solid var(--ink);
  border-radius: var(--radius-pill);
  background: var(--ink);
  color: var(--bone);
  font-size: var(--fs-small);
  font-weight: 500;
  line-height: 1;
  transition: color var(--dur) var(--ease-out), border-color var(--dur) var(--ease-out);
}

.btn::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  background: var(--clay);
  border-radius: inherit;
  transform: translateY(101%);
  transition: transform var(--dur) var(--ease-out);
}

.btn:hover,
.btn:focus-visible {
  border-color: var(--clay);
}

.btn:hover::before,
.btn:focus-visible::before {
  transform: translateY(0);
}

.link-underline {
  position: relative;
  display: inline-block;
  padding-bottom: 2px;
}

.link-underline::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 1px;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform var(--dur) var(--ease-out);
}

.link-underline:hover::after,
.link-underline:focus-visible::after {
  transform: scaleX(1);
}

.media {
  position: relative;
  overflow: hidden;
  background: var(--paper);
}

.media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.rule {
  height: 1px;
  border: 0;
  background: var(--line);
}

/* Custom cursor (cursor.js): created only for mouse-like pointers with motion allowed. Positioned by GSAP;
   the inner shape changes size per state: default dot, ring over links and buttons, "View" over work cards. */
.cursor {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 300;
  pointer-events: none;
  mix-blend-mode: difference;
  opacity: 0;
  transition: opacity var(--dur-fast) var(--ease-out);
}

.cursor.is-visible {
  opacity: 1;
}

.cursor__shape {
  position: absolute;
  top: 0;
  left: 0;
  display: grid;
  place-items: center;
  width: 8px;
  height: 8px;
  border: 1px solid var(--bone);
  border-radius: 50%;
  background: var(--bone);
  transform: translate(-50%, -50%);
  transition: width var(--dur) var(--ease-out), height var(--dur) var(--ease-out),
    background-color var(--dur-fast) var(--ease-out), opacity var(--dur-fast) var(--ease-out);
}

.cursor__label {
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--ink);
  opacity: 0;
  transition: opacity var(--dur-fast) var(--ease-out);
}

.cursor[data-state='link'] .cursor__shape {
  width: 48px;
  height: 48px;
  background-color: transparent;
}

/* Over photographs the label must read as ink on bone, so this state drops the blending. */
.cursor[data-state='view'] {
  mix-blend-mode: normal;
}

.cursor[data-state='view'] .cursor__shape {
  width: 88px;
  height: 88px;
}

.cursor[data-state='view'] .cursor__label {
  opacity: 1;
}

.cursor[data-state='hidden'] .cursor__shape {
  opacity: 0;
}

/* !important: section rules such as cursor: pointer would otherwise bring the native cursor back. */
@media (hover: hover) and (pointer: fine) {
  html.has-cursor,
  html.has-cursor * {
    cursor: none !important;
  }

  html.has-cursor :is(input, textarea, select, [contenteditable='true']) {
    cursor: auto !important;
  }
}

/* Film grain: inline SVG noise (no network request), shifted in steps so it reads as film, not a pattern. */
@media (prefers-reduced-motion: no-preference) {
  body::after {
    content: '';
    position: fixed;
    inset: -50%;
    z-index: 250;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='240' height='240'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.8' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
    opacity: 0.05;
    pointer-events: none;
    animation: grain 0.6s steps(1) infinite;
  }
}

@keyframes grain {
  0% { transform: translate(0, 0); }
  20% { transform: translate(-6%, 4%); }
  40% { transform: translate(5%, -7%); }
  60% { transform: translate(-3%, 8%); }
  80% { transform: translate(7%, 2%); }
  100% { transform: translate(-4%, -5%); }
}

@media (prefers-reduced-motion: reduce) {
  .btn,
  .btn::before,
  .link-underline::after {
    transition: none;
  }
}

/* styles/preloader.css */
/* Preloader: ink screen with a mono counter, wiped up by preloader.js. Hidden without JS, with reduced motion
   and once it has been seen this session (html.is-preloaded, set by the inline script in <head>). */
.preloader {
  display: none;
}

@media (prefers-reduced-motion: no-preference) {
  html.js:not(.is-preloaded) .preloader {
    position: fixed;
    inset: 0;
    z-index: 200;
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    padding: var(--space-5) var(--gutter);
    background: var(--ink);
    color: var(--bone);
    clip-path: inset(0% 0% 0% 0%);
    /* Failsafe: if the script never runs, the screen still gets out of the way. */
    animation: preloader-failsafe 0s 6s forwards;
  }
}

.preloader__bottom {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--space-4);
}

.preloader__place {
  color: var(--paper);
}

.preloader__count {
  font-family: var(--font-mono);
  font-size: var(--fs-h2);
  line-height: 1;
  letter-spacing: var(--tracking-label);
  font-variant-numeric: tabular-nums;
}

@keyframes preloader-failsafe {
  to {
    visibility: hidden;
  }
}

/* styles/sections/nav.css */
/* Navigation: fixed, thin bar that inverts over light and dark sections, plus the mobile menu overlay.
   Under difference blending only pure bone inverts cleanly, so everything in the header is bone with no opacity. */
.nav {
  position: fixed;
  inset: 0 0 auto;
  z-index: 100;
  color: var(--bone);
  mix-blend-mode: difference;
  will-change: transform;
}

.nav__bar {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  column-gap: var(--gutter);
  padding-block: var(--space-4);
}

.nav__wordmark {
  display: inline-flex;
  align-items: flex-start;
  justify-self: start;
  gap: 0.12em;
  font-family: var(--font-display);
  font-size: clamp(1.5rem, 1.2vw + 1rem, 1.875rem);
  line-height: 1;
  letter-spacing: var(--tracking-display);
}

.nav__dot {
  width: 0.22em;
  height: 0.22em;
  margin-top: 0.12em;
  border-radius: 50%;
  background: var(--bone);
}

.nav__clock {
  justify-self: center;
  color: var(--bone);
  font-variant-numeric: tabular-nums;
}

/* Without JS there is no time to show, so the clock stays out of the way. */
.nav__clock:has(time:empty) {
  visibility: hidden;
}

.nav__links {
  justify-self: end;
}

/* A clay outline would invert to blue under the blend, so focus rings in the bar are bone too. */
.nav :focus-visible {
  outline-color: var(--bone);
}

.nav__wordmark,
.nav__links a,
.nav__toggle {
  color: var(--bone);
}

.nav__links ul {
  display: flex;
  gap: clamp(var(--space-4), 2vw, var(--space-6));
}

.nav__toggle {
  display: none;
  justify-self: end;
  padding-block: var(--space-2);
}

/* Mobile menu overlay: sits under the bar so the wordmark and Close button stay on top. */
.nav-menu {
  position: fixed;
  inset: 0;
  z-index: 90;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  padding-block: calc(var(--space-9) + var(--space-4)) var(--space-6);
  background: var(--ink);
  color: var(--bone);
  overscroll-behavior: contain;
}

.nav-menu[hidden] {
  display: none;
}

.nav-menu__mask {
  overflow: hidden;
  padding-bottom: 0.06em;
  border-bottom: 1px solid var(--line-inverse);
}

.nav-menu__link {
  display: block;
  padding-block: var(--space-2);
  font-family: var(--font-display);
  font-size: clamp(3rem, 15vw, 6rem);
  line-height: var(--lh-tight);
  letter-spacing: var(--tracking-display);
  transition: color var(--dur) var(--ease-out);
}

.nav-menu__link:hover,
.nav-menu__link:focus-visible {
  color: var(--clay);
}

.nav-menu__meta {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  color: var(--paper);
}

.nav-menu__dot {
  width: 0.5em;
  height: 0.5em;
  border-radius: 50%;
  background: var(--clay);
}

@media (max-width: 799px) {
  .nav__clock {
    display: none;
  }

  .nav__bar {
    grid-template-columns: 1fr auto;
  }

  /* With JS the links move into the overlay; without JS they stay in the bar. */
  html.js .nav__links {
    display: none;
  }

  html.js .nav__toggle {
    display: block;
  }

  html:not(.js) .nav__links ul {
    gap: var(--space-3);
  }
}

@media (prefers-reduced-motion: reduce) {
  .nav-menu__link {
    transition: none;
  }
}

/* styles/sections/hero.css */
/* Hero: full-bleed photograph with the studio wordmark set large, bottom-left. */
.hero {
  position: relative;
  height: 100vh;
  height: 100svh;
  min-height: 560px;
  overflow: hidden;
  background: var(--bg);
}

.hero__media,
.hero__frame {
  position: absolute;
  inset: 0;
  overflow: hidden;
}

.hero__img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 40%;
}

.hero__content {
  position: relative;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  height: 100%;
  padding-block: calc(var(--space-9) + var(--space-4)) var(--gutter);
}

.hero__intro {
  align-self: flex-end;
  max-width: 24ch;
  font-size: clamp(1.125rem, 0.6vw + 0.95rem, 1.375rem);
  line-height: 1.35;
}

.hero__labels {
  padding-top: var(--space-3);
  border-top: 1px solid var(--line);
}

.hero__labels > :nth-child(1) { grid-column: 1 / span 6; }
.hero__labels > :nth-child(2) { grid-column: 7 / span 6; }

.hero__scroll {
  display: none;
}

.hero__title {
  margin-top: var(--space-4);
  font-size: var(--fs-display);
  line-height: var(--lh-tight);
}

/* Each line is its own mask; the padding keeps italic overhangs inside it. */
.hero__line {
  display: block;
  overflow: hidden;
  padding: 0 0.08em 0.08em 0;
  margin-bottom: -0.08em;
}

.hero__line--indent {
  padding-left: 0.9em;
}

.hero__line-inner {
  display: inline-block;
}

@media (min-width: 800px) {
  .hero__labels > :nth-child(1) { grid-column: 1 / span 3; }
  .hero__labels > :nth-child(2) { grid-column: 5 / span 3; }

  .hero__scroll {
    display: block;
    grid-column: 12;
    justify-self: end;
  }

  .hero__line--indent {
    padding-left: 1.6em;
  }
}

/* Load-animation start states: only with JS and motion allowed. hero.js removes
   [data-hero-intro] when motion is off, so the composed end state shows instead. */
@media (prefers-reduced-motion: no-preference) {
  html.js .hero[data-hero-intro] .hero__media {
    clip-path: inset(18% 22% 18% 22%);
  }

  html.js .hero[data-hero-intro] .hero__line-inner {
    transform: translateY(110%);
  }

  html.js .hero[data-hero-intro] .hero__intro,
  html.js .hero[data-hero-intro] .hero__labels {
    opacity: 0;
  }
}

/* styles/sections/intro.css */
/* Intro: manifesto, framed image, stats and the client/press marquee. */
.intro__head {
  row-gap: var(--space-6);
}

.intro__label {
  grid-column: 1 / -1;
  color: var(--muted);
}

.intro__manifesto {
  grid-column: 1 / -1;
  font-family: var(--font-display);
  font-size: clamp(2rem, 4.6vw, 5.5rem);
  line-height: 1.08;
  letter-spacing: var(--tracking-display);
  text-wrap: balance;
}

.intro__body {
  row-gap: var(--space-8);
  align-items: end;
  margin-top: var(--space-10);
}

.intro__figure {
  grid-column: 1 / span 8;
}

.intro__media {
  aspect-ratio: 4 / 5;
}

.intro__caption {
  margin-top: var(--space-3);
  color: var(--muted);
}

.intro__stats {
  grid-column: 1 / -1;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--gutter);
}

.stat {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  padding-top: var(--space-4);
  border-top: 1px solid var(--line);
}

.stat__value {
  font-family: var(--font-display);
  font-size: var(--fs-h2);
  line-height: 1;
  font-variant-numeric: tabular-nums;
}

.stat__label {
  color: var(--muted);
  font-size: var(--fs-small);
}

@media (min-width: 900px) {
  .intro__label {
    grid-column: 1 / 4;
    padding-top: 1.2em;
  }

  .intro__manifesto {
    grid-column: 4 / -1;
  }

  .intro__figure {
    grid-column: 1 / span 4;
  }

  .intro__stats {
    grid-column: 6 / -1;
  }
}

/* Marquee: two identical lists side by side, so moving the track by one list width loops without a jump. */
.marquee {
  margin-top: var(--space-10);
  padding-block: var(--space-5);
  overflow: hidden;
  border-block: 1px solid var(--line);
}

.marquee__track {
  display: flex;
  width: max-content;
}

html.js .marquee__track {
  will-change: transform;
}

.marquee__list {
  display: flex;
  flex-shrink: 0;
}

.marquee__item {
  display: flex;
  align-items: center;
  font-family: var(--font-display);
  font-size: var(--fs-h1);
  line-height: 1.1;
  letter-spacing: var(--tracking-display);
  white-space: nowrap;
}

.marquee__dot {
  margin-inline: 0.45em;
  color: var(--clay);
  font-size: 0.5em;
}

/* styles/sections/services.css */
/* Services: hairline list of rows. Three states, set on the section by services.js:
   none (no JS: everything open), .is-accordion (touch, small screens, reduced motion)
   and .is-preview (desktop with a fine pointer: description inline, image follows the cursor). */
.services__head {
  row-gap: var(--space-5);
  margin-bottom: var(--space-9);
}

.services__label {
  grid-column: 1 / -1;
  color: var(--muted);
}

.services__title {
  grid-column: 1 / -1;
}

/* base.css resets padding on ol[role='list'], which outranks .container. */
.section--services .services__list {
  padding-inline: var(--gutter);
}

.service {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  column-gap: var(--gutter);
  border-top: 1px solid var(--line);
}

.service:last-child {
  border-bottom: 1px solid var(--line);
}

.service__heading {
  font-size: inherit;
  line-height: inherit;
  letter-spacing: normal;
}

.service__toggle {
  --service-size: clamp(2rem, 5vw, 5.5rem);
  display: flex;
  align-items: flex-start;
  gap: var(--space-5);
  width: 100%;
  padding-block: var(--space-5);
  text-align: left;
}

.service__index {
  flex-shrink: 0;
  /* Approximates the name's cap top where text-box trimming is unsupported. */
  margin-top: calc(var(--service-size) * 0.15 - 0.3em);
  color: var(--muted);
}

.service__name {
  display: inline-block;
  font-family: var(--font-display);
  font-size: var(--service-size);
  line-height: 1;
  letter-spacing: var(--tracking-display);
  transition: transform var(--dur) var(--ease-out);
}

/* Trim both to cap height so the top of the index meets the top of the name's capitals. */
@supports (text-box: trim-both cap alphabetic) {
  .service__index,
  .service__name {
    text-box: trim-both cap alphabetic;
  }

  .service__index {
    margin-top: 0;
  }

  .service__toggle {
    padding-block: calc(var(--space-5) + var(--service-size) * 0.15);
  }
}

.service__arrow {
  grid-column: 2;
  grid-row: 1;
  align-self: center;
  width: clamp(20px, 2vw, 32px);
  color: var(--clay);
  opacity: 0;
  transform: translateX(-12px);
  transition: opacity var(--dur) var(--ease-out), transform var(--dur) var(--ease-out);
}

.service__panel {
  grid-column: 1 / -1;
}

.service__panel-inner {
  display: grid;
  gap: var(--space-5);
  min-height: 0;
  overflow: hidden;
}

.service__desc {
  max-width: 40ch;
  color: var(--muted);
}

.service__media {
  width: min(100%, 320px);
  aspect-ratio: 4 / 5;
  margin-bottom: var(--space-6);
}

@media (hover: hover) {
  .service:hover .service__name {
    transform: translateX(clamp(8px, 1.5vw, 28px));
  }

  .service:hover .service__arrow {
    opacity: 1;
    transform: none;
  }
}

.service:focus-within .service__arrow {
  opacity: 1;
  transform: none;
}

/* Accordion */
.is-accordion .service__arrow {
  color: var(--fg);
  opacity: 1;
  transform: rotate(90deg);
}

.is-accordion .service.is-open .service__arrow {
  color: var(--clay);
  transform: rotate(-90deg);
}

.is-accordion .service__panel {
  display: grid;
  grid-template-rows: 0fr;
  visibility: hidden;
  transition: grid-template-rows var(--dur) var(--ease-out), visibility var(--dur);
}

.is-accordion .service.is-open .service__panel {
  grid-template-rows: 1fr;
  visibility: visible;
}

/* Desktop preview */
.is-preview .service {
  grid-template-columns: repeat(12, minmax(0, 1fr));
  align-items: center;
}

.is-preview .service__heading {
  grid-column: 1 / 8;
}

.is-preview .service__toggle {
  cursor: default;
}

.is-preview .service__panel {
  grid-column: 8 / 12;
  grid-row: 1;
}

.is-preview .service__media {
  display: none;
}

.is-preview .service__arrow {
  grid-column: 12;
  justify-self: end;
}

.services__preview {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 5;
  width: 320px;
  height: 400px;
  overflow: hidden;
  background: var(--paper);
  pointer-events: none;
  visibility: hidden;
}

.services__preview-frame {
  position: absolute;
  inset: 0;
  clip-path: inset(100% 0 0 0);
}

.services__preview-frame img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

@media (min-width: 900px) {
  .services__label {
    grid-column: 1 / 4;
    padding-top: 1em;
  }

  .services__title {
    grid-column: 4 / -1;
  }
}

@media (prefers-reduced-motion: reduce) {
  .service__name,
  .service__arrow,
  .is-accordion .service__panel {
    transition: none;
  }
}

/* styles/sections/work.css */
/* Selected work: art-directed vertical stack by default, pinned horizontal gallery under .work--pinned (set by work.js). */
.section--work {
  overflow: hidden;
}

.work__head {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  column-gap: var(--gutter);
  row-gap: var(--space-4);
  align-items: end;
}

.work__label,
.work__counter {
  color: var(--muted);
}

.work__label {
  grid-column: 1 / -1;
}

.work__title {
  grid-column: 1 / -1;
  max-width: 14ch;
}

.work__counter {
  display: none;
  font-variant-numeric: tabular-nums;
}

/* Cards: each sets its own frame ratio and offset so the layout reads as composed, not uniform. */
.work-card--1 { --ratio: 0.8; --h: 52vh; --offset: 0vh; }
.work-card--2 { --ratio: 1.5; --h: 38vh; --offset: 12vh; }
.work-card--3 { --ratio: 0.75; --h: 46vh; --offset: 4vh; }
.work-card--4 { --ratio: 1.6; --h: 34vh; --offset: 16vh; }
.work-card--5 { --ratio: 0.6667; --h: 48vh; --offset: 2vh; }

.work__track {
  display: flex;
  flex-direction: column;
  gap: var(--space-9);
  padding-inline: var(--gutter);
  padding-top: var(--space-8);
  max-width: var(--container);
  margin-inline: auto;
}

.work-card__media {
  aspect-ratio: var(--ratio);
}

.work-card__media img {
  transition: scale var(--dur-slow) var(--ease-out);
}

.work-card:hover .work-card__media img {
  scale: 1.04;
}

.work-card__pill {
  position: absolute;
  left: var(--space-4);
  bottom: var(--space-4);
  padding: 0.7em 1.2em;
  border-radius: var(--radius-pill);
  background: var(--bone);
  color: var(--ink);
  font-size: var(--fs-small);
  font-weight: 500;
  line-height: 1;
  opacity: 0;
  transform: translateY(8px);
  transition: opacity var(--dur) var(--ease-out), transform var(--dur) var(--ease-out);
  pointer-events: none;
}

.work-card:hover .work-card__pill {
  opacity: 1;
  transform: none;
}

/* With the custom cursor showing "View", the pill would say the same thing twice. */
html.has-cursor .work-card__pill {
  display: none;
}

.work-card__meta {
  display: grid;
  grid-template-columns: auto 1fr;
  column-gap: var(--space-4);
  row-gap: var(--space-1);
  padding-top: var(--space-4);
}

.work-card__index {
  grid-row: span 3;
  padding-top: 0.6em;
  color: var(--clay);
}

.work-card__title {
  overflow: hidden;
  padding-bottom: 0.08em;
  font-size: var(--fs-h3);
}

.work-card__title-inner {
  display: inline-block;
}

.work-card__place {
  display: flex;
  gap: var(--space-3);
  color: var(--muted);
}

.work-card__type {
  color: var(--muted);
  font-size: var(--fs-small);
}

.work__progress {
  display: none;
}

/* Stack on mobile: alternate widths and alignment. */
.work-card:nth-child(even) {
  width: 86%;
  margin-left: auto;
}

@media (min-width: 900px) {
  .work__label { grid-column: 1 / 4; }
  .work__title { grid-column: 4 / 11; }

  .work__counter {
    display: block;
    grid-column: 11 / -1;
    justify-self: end;
  }

  .work__track {
    display: grid;
    grid-template-columns: repeat(12, minmax(0, 1fr));
    column-gap: var(--gutter);
    row-gap: var(--space-10);
  }

  .work-card:nth-child(even) {
    width: auto;
    margin-left: 0;
  }

  .work-card--1 { grid-column: 1 / 8; }
  .work-card--2 { grid-column: 6 / 13; }
  .work-card--3 { grid-column: 2 / 7; }
  .work-card--4 { grid-column: 7 / 13; margin-top: var(--space-10); }
  .work-card--5 { grid-column: 3 / 8; }

  /* Pinned gallery: the section fills the viewport and the track runs sideways. */
  .section--work.work--pinned {
    display: flex;
    flex-direction: column;
    height: 100vh;
    height: 100svh;
    padding-block: var(--space-8) var(--space-6);
  }

  .work--pinned .work__viewport {
    flex: 1;
    min-height: 0;
  }

  .work--pinned .work__track {
    display: flex;
    flex-direction: row;
    align-items: flex-start;
    gap: clamp(48px, 7vw, 140px);
    width: max-content;
    max-width: none;
    margin-inline: 0;
    padding-top: var(--space-7);
    padding-inline: var(--gutter) clamp(48px, 10vw, 200px);
    will-change: transform;
  }

  .work--pinned .work-card {
    grid-column: auto;
    flex: none;
    width: calc(var(--h) * var(--ratio));
    margin-top: var(--offset);
  }

  .work--pinned .work-card__media {
    height: var(--h);
    aspect-ratio: auto;
  }

  /* Wider than its frame so it can drift sideways inside the mask. */
  .work--pinned .work-card__media img {
    width: 116%;
    max-width: none;
    margin-left: -8%;
  }

  .work--pinned .work__progress {
    display: block;
  }

  .work__progress-line {
    display: block;
    height: 1px;
    background: var(--line);
  }

  .work__progress-bar {
    display: block;
    height: 100%;
    background: var(--fg);
    transform: scaleX(0);
    transform-origin: left center;
  }
}

@media (prefers-reduced-motion: reduce) {
  .work-card__media img,
  .work-card__pill {
    transition: none;
  }

  .work-card:hover .work-card__media img {
    scale: none;
  }
}

/* styles/sections/process.css */
/* Process section: sticky heading column and stacking step cards (desktop), plain list (mobile). */
.section--process {
  --bg: var(--paper);
  background: var(--bg);
}

.process {
  row-gap: var(--space-8);
}

.process__intro,
.process__steps {
  grid-column: 1 / -1;
}

.process__label {
  margin-bottom: var(--space-6);
  color: var(--muted);
}

.process__title {
  max-width: 10ch;
}

.process__lede {
  max-width: 36ch;
  margin-top: var(--space-6);
  color: var(--muted);
}

.process__steps {
  display: grid;
  gap: var(--space-5);
  margin: 0;
  padding: 0;
}

.process-card {
  position: relative;
  display: grid;
  gap: var(--space-6);
  padding: var(--space-6);
  overflow: hidden;
  background: var(--bone);
  border: 1px solid var(--line);
  transform-origin: 50% 0;
}

.process-card__body {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: auto 1fr;
  column-gap: var(--space-5);
  row-gap: var(--space-3);
  align-items: baseline;
}

.process-card__number {
  grid-row: span 2;
  font-family: var(--font-display);
  font-size: var(--fs-h2);
  line-height: var(--lh-tight);
  letter-spacing: var(--tracking-display);
}

.process-card__text {
  max-width: 44ch;
  color: var(--muted);
}

.process-card__media {
  aspect-ratio: 16 / 7;
}

/* Large outlined numeral for the text-only cards: in flow, bottom-right, so the card padding keeps it whole. */
.process-card__outline {
  align-self: end;
  justify-self: end;
  font-family: var(--font-display);
  font-size: clamp(7rem, 22vw, 20rem);
  line-height: 0.8;
  letter-spacing: var(--tracking-display);
  color: transparent;
  -webkit-text-stroke: 1px var(--stone);
  pointer-events: none;
  user-select: none;
}

@supports not (-webkit-text-stroke: 1px) {
  .process-card__outline {
    color: var(--line);
  }
}

@media (min-width: 1024px) {
  .process {
    --stack-top: clamp(96px, 12vh, 140px);
    --stack-step: 1.5rem;
    align-items: start;
  }

  .process__intro {
    grid-column: 1 / span 5;
    position: sticky;
    top: var(--stack-top);
  }

  .process__steps {
    grid-column: 6 / -1;
    gap: var(--space-9);
  }

  .process-card {
    position: sticky;
    top: calc(var(--stack-top) + var(--i, 0) * var(--stack-step));
    min-height: min(68vh, 640px);
    grid-template-rows: auto 1fr;
    padding: var(--space-7);
  }

  .process-card:nth-child(2) { --i: 1; }
  .process-card:nth-child(3) { --i: 2; }
  .process-card:nth-child(4) { --i: 3; }

  .process-card__media {
    align-self: end;
  }
}

/* styles/sections/testimonials.css */
/* Testimonials section: one large serif quote at a time, prev/next controls and a counter. */
.section--testimonials {
  background: var(--bg);
}

.testimonials {
  row-gap: var(--space-7);
}

.testimonials__head,
.testimonials__slider {
  grid-column: 1 / -1;
}

.testimonials__label {
  color: var(--muted);
}

.testimonials__slider:focus-visible {
  outline-offset: var(--space-4);
}

.testimonials__slides {
  display: grid;
  gap: var(--space-9);
}

.testimonial__quote {
  margin: 0;
  font-family: var(--font-display);
  font-size: var(--fs-h2);
  line-height: var(--lh-heading);
  letter-spacing: var(--tracking-display);
  text-wrap: balance;
}

.testimonial__quote p::before {
  content: '\201C';
}

.testimonial__quote p::after {
  content: '\201D';
}

.testimonial__cite {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-2) var(--space-5);
  margin-top: var(--space-7);
  padding-top: var(--space-4);
  border-top: 1px solid var(--line);
}

.testimonial__name {
  font-weight: 500;
}

.testimonial__role {
  color: var(--muted);
}

.testimonials__controls {
  display: flex;
  align-items: center;
  gap: var(--space-5);
  margin-top: var(--space-7);
}

.testimonials__btn {
  display: inline-grid;
  place-items: center;
  width: 3.25rem;
  height: 3.25rem;
  border: 1px solid var(--line);
  border-radius: var(--radius-pill);
  transition: border-color var(--dur) var(--ease-out), color var(--dur) var(--ease-out);
}

.testimonials__btn:hover,
.testimonials__btn:focus-visible {
  border-color: var(--clay);
  color: var(--clay);
}

.testimonials__count {
  min-width: 4ch;
  text-align: center;
  font-variant-numeric: tabular-nums;
}

/* Without JS every quote is listed and the controls are hidden. */
html:not(.js) .testimonials__controls {
  display: none;
}

/* With JS the slides share one grid cell so the slider keeps the height of the longest quote. */
html.js .testimonials__slides > .testimonial {
  grid-area: 1 / 1;
  visibility: hidden;
}

html.js .testimonials__slides > .testimonial.is-active,
html.js .testimonials__slides > .testimonial.is-leaving {
  visibility: visible;
}

@media (min-width: 1024px) {
  .testimonials__head {
    grid-column: 1 / span 3;
  }

  .testimonials__slider {
    grid-column: 4 / span 8;
  }

  .testimonial__quote {
    font-size: clamp(2.5rem, 4vw, 4.5rem);
  }
}

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

/* styles/sections/contact.css */
/* Contact section styles. Owned by the contact section task. */
.contact__grid {
  row-gap: var(--space-8);
  align-items: start;
}

.contact__label,
.contact__title,
.contact__aside,
.contact__panel {
  grid-column: 1 / -1;
}

.contact__label {
  color: var(--muted);
}

.contact__title {
  font-size: var(--fs-h1);
  line-height: var(--lh-tight);
  max-width: 14ch;
}

.contact__title em {
  color: var(--clay);
}

/* Magnetic button */
.contact__magnet {
  display: inline-grid;
  padding: var(--space-6);
  margin: calc(var(--space-6) * -1);
}

.contact__cta {
  display: grid;
  place-items: center;
  width: clamp(140px, 13vw, 184px);
  aspect-ratio: 1;
  border-radius: 50%;
  background: var(--clay);
  color: var(--bone);
  font-size: var(--fs-small);
  font-weight: 500;
  text-align: center;
  transition: background-color var(--dur) var(--ease-out), color var(--dur) var(--ease-out);
}

.contact__cta:hover,
.contact__cta:focus-visible {
  background: var(--bone);
  color: var(--ink);
}

.contact__cta-text {
  display: block;
  max-width: 8ch;
  line-height: 1.2;
}

/* Details */
.contact__details {
  display: grid;
  gap: var(--space-5);
  margin-top: var(--space-8);
}

.contact__detail dt {
  margin-bottom: var(--space-1);
  color: var(--muted);
}

.contact__detail dd {
  margin: 0;
  font-size: var(--fs-h3);
  font-family: var(--font-display);
  line-height: 1.2;
}

/* Enquiry form */
.enquiry {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  column-gap: var(--gutter);
  row-gap: var(--space-6);
}

.enquiry__intro {
  grid-column: 1 / -1;
  color: var(--muted);
}

.field {
  position: relative;
  grid-column: 1 / -1;
  padding-top: var(--space-5);
}

.field__control {
  display: block;
  width: 100%;
  padding: var(--space-2) 0;
  border: 0;
  border-bottom: 1px solid var(--line);
  border-radius: 0;
  background: transparent;
  color: var(--fg);
  transition: border-color var(--dur) var(--ease-out);
  appearance: none;
}

textarea.field__control {
  resize: vertical;
  min-height: 7.5rem;
}

/* The clay hairline (thickened by a shadow) is the focus indicator. */
.field__control:focus,
.field__control:focus-visible {
  outline: none;
  border-bottom-color: var(--clay);
  box-shadow: 0 1px 0 var(--clay);
}

.field__control[aria-invalid='true'] {
  border-bottom-color: var(--clay);
}

.field__label {
  position: absolute;
  left: 0;
  top: calc(var(--space-5) + var(--space-2));
  color: var(--muted);
  pointer-events: none;
  transform-origin: left top;
  transition: transform var(--dur) var(--ease-out), color var(--dur) var(--ease-out);
}

/* Float the label when the field has focus or content; selects always show a value, so theirs stays up. */
.field__control:focus + .field__label,
.field__control:not(:placeholder-shown) + .field__label,
.field--select .field__label {
  transform: translateY(-1.6rem) scale(0.72);
  color: var(--fg);
}

.field--select::after {
  content: '';
  position: absolute;
  right: var(--space-1);
  top: calc(var(--space-5) + var(--space-2) + 0.4em);
  width: 0.45em;
  height: 0.45em;
  border-right: 1px solid currentColor;
  border-bottom: 1px solid currentColor;
  transform: rotate(45deg);
  pointer-events: none;
}

.field--select select {
  padding-right: var(--space-5);
  cursor: pointer;
}

.field--select option {
  background: var(--ink);
  color: var(--bone);
}

.field__error {
  margin-top: var(--space-2);
  font-size: var(--fs-small);
}

.field__error::before {
  content: '';
  display: inline-block;
  width: 6px;
  height: 6px;
  margin-right: var(--space-2);
  border-radius: 50%;
  background: var(--clay);
  vertical-align: middle;
}

.enquiry__submit {
  grid-column: 1 / -1;
  justify-self: start;
  margin-top: var(--space-3);
  border-color: var(--bone);
  background: var(--bone);
  color: var(--ink);
}

.enquiry__submit:hover,
.enquiry__submit:focus-visible {
  color: var(--bone);
}

.contact__success {
  display: grid;
  gap: var(--space-4);
  padding-block: var(--space-6);
  border-top: 1px solid var(--line);
}

.contact__success[hidden] {
  display: none;
}

.contact__success:focus {
  outline: none;
}

.contact__success-title {
  max-width: 22ch;
  font-family: var(--font-display);
  font-size: var(--fs-h2);
  line-height: var(--lh-heading);
}

@media (min-width: 768px) {
  .field--half {
    grid-column: span 1;
  }
}

@media (min-width: 1024px) {
  .contact__title {
    grid-column: 1 / 12;
  }

  .contact__aside {
    grid-column: 1 / 6;
  }

  .contact__panel {
    grid-column: 7 / -1;
  }
}

@media (prefers-reduced-motion: reduce) {
  .contact__cta,
  .field__control,
  .field__label {
    transition: none;
  }
}

/* styles/sections/footer.css */
/* Footer section styles. Owned by the footer section task. */

/* Curtain reveal: main sits above the sticky footer and slides away to uncover it. */
main {
  position: relative;
  z-index: 1;
  background: var(--bg);
}

.section--footer {
  position: sticky;
  bottom: 0;
  z-index: 0;
  display: flex;
  flex-direction: column;
  min-height: 100svh;
  padding-block: var(--space-9) var(--space-5);
  background: var(--paper);
  color: var(--ink);
}

/* The footer is exactly one screen tall: columns and bottom row keep their size, the wordmark takes what is left. */
.section--footer > .container {
  display: flex;
  flex: 1;
  flex-direction: column;
  min-height: 0;
}

.footer__top {
  row-gap: var(--space-7);
}

.footer__col {
  grid-column: span 6;
}

.footer__heading {
  margin-bottom: var(--space-4);
  color: var(--stone);
}

.footer__list {
  display: grid;
  gap: var(--space-2);
  margin: 0;
}

.footer__address {
  font-style: normal;
}

.footer__top-btn {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-3) var(--space-4);
  border: 1px solid var(--line);
  border-radius: var(--radius-pill);
  transition: border-color var(--dur) var(--ease-out), color var(--dur) var(--ease-out);
}

.footer__top-btn:hover,
.footer__top-btn:focus-visible {
  border-color: var(--clay);
  color: var(--clay);
}

.footer__arrow {
  display: inline-block;
  transition: transform var(--dur) var(--ease-out);
}

.footer__top-btn:hover .footer__arrow {
  transform: translateY(-3px);
}

/* Giant wordmark: letters sit in a mask so they can rise in. It fills the space left over; footer.js fits the
   size to the full width, capped by that space's height. */
.footer__wordmark {
  display: flex;
  flex: 1 1 0;
  align-items: flex-end;
  min-height: 0;
  margin-top: var(--space-6);
  overflow: hidden;
  font-family: var(--font-display);
  font-size: min(22vw, 28svh);
  line-height: 0.82;
  letter-spacing: -0.03em;
  white-space: nowrap;
}

.footer__letters,
.footer__letter {
  display: inline-block;
}

.footer__bottom {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: var(--space-2) var(--space-5);
  margin-top: var(--space-5);
  padding-top: var(--space-4);
  border-top: 1px solid var(--line);
  color: var(--stone);
}

@media (min-width: 1024px) {
  .footer__col {
    grid-column: span 3;
  }

  .footer__col--top {
    justify-self: end;
  }
}

@media (prefers-reduced-motion: reduce) {
  .footer__top-btn,
  .footer__arrow {
    transition: none;
  }
}
