/* AGAVE — Anthony Girgis, Lighting Design
   Palette and type preserved from the original design. */

:root {
  --bg:        oklch(0.15 0.006 55);
  --bg-raise:  oklch(0.19 0.006 55);
  --bg-raise2: oklch(0.22 0.008 55);
  --line:      oklch(0.30 0.006 55);
  --text:      oklch(0.95 0.004 60);
  --text-soft: oklch(0.78 0.006 60);
  --text-mute: oklch(0.65 0.010 60);
  --text-dim:  oklch(0.52 0.010 60);
  --accent:    oklch(0.74 0.14 70);

  --pad: clamp(20px, 5vw, 48px);
  --font: 'Titillium Web', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
}

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

html {
  -webkit-text-size-adjust: 100%;
  /* Reserve equal gutters on both sides regardless of whether a vertical
     scrollbar is showing, so centered content (the splash logo/nav/tag)
     doesn't sit a few pixels left of true center once the page scrolls. */
  scrollbar-gutter: stable both-edges;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font);
  font-weight: 300;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}

img { max-width: 100%; display: block; }

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

h1, h2, h3, p, figure, blockquote, dl, dd { margin: 0; }
ul { margin: 0; padding: 0; list-style: none; }

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

.visually-hidden {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}

.skip {
  position: absolute; left: 8px; top: -60px; z-index: 200;
  background: var(--accent); color: #171410; padding: 10px 16px;
  font-size: 13px; border-radius: 4px; transition: top .15s;
}
.skip:focus { top: 8px; }

/* ---------------------------------------------------------------- layout */

.wrap {
  max-width: 1120px;
  margin-inline: auto;
  padding: clamp(48px, 8vw, 80px) var(--pad) clamp(80px, 12vw, 140px);
}
.wrap--narrow { max-width: 860px; }

.display {
  font-size: clamp(34px, 6vw, 72px);
  font-weight: 300;
  line-height: 1.05;
  letter-spacing: -0.01em;
  margin: 0;
}
.display--sm {
  font-size: clamp(26px, 4vw, 46px);
  line-height: 1.25;
}

.eyebrow {
  font-size: 12px;
  font-weight: 400;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--text-mute);
  margin: 0;
}
.eyebrow--accent { color: var(--accent); }

.meta { font-size: 13px; color: var(--text-mute); margin-top: 10px; }

/* ------------------------------------------------------------ page shell */
/* Every page is a full-width header followed by its content block. The header
   gets its own .wrap so the title always starts on the 1120px grid, whether
   the content beneath it is that same wide grid (Work) or the 860px measure
   prose wants (Services, Press, Contact). Previously the header simply sat
   inside whichever container the body needed, so the title jumped 130px —
   half the difference between the two widths — as you moved between pages. */
.page-head.wrap { padding-bottom: 0; }
.page-body.wrap { padding-top: clamp(28px, 4vw, 40px); }
/* Prose pages sit in the same wide container as everything else so their
   content lines up under its own title; the readable measure is capped here
   instead — 764px, exactly the text width the old 860px container gave. */
.page-body--prose > * { max-width: 764px; }
.page-head .display { max-width: 20ch; }
.page-head { margin-bottom: 0; }

.prose {
  font-size: clamp(16px, 1.6vw, 19px);
  line-height: 1.75;
  color: var(--text-soft);
  display: flex;
  flex-direction: column;
  gap: 20px;
  max-width: 66ch;
}

.ghost {
  display: inline-block;
  font-size: 12.5px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  border-bottom: 1px solid var(--accent);
  padding-bottom: 6px;
  transition: color .18s;
}
.ghost:hover { color: var(--accent); }

.cta { margin-top: 56px; }

/* ---------------------------------------------------------------- header */

.site-header {
  position: sticky;
  top: 0;
  z-index: 50;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  padding: 18px var(--pad);
  background: oklch(0.15 0.006 55 / 0.92);
  backdrop-filter: blur(8px);
  border-bottom: 1px solid var(--line);
}

/* ------------------------------------------------------------- wordmark */
/* AGAVE is set in Arch Bold (Typomancer) and shipped as outlines rather than
   as a webfont: five glyphs of SVG is ~1KB against ~27KB for the face, there's
   no flash of fallback text while a font loads, and it renders identically in
   every browser. The paths carry fill="currentColor", so the mark still
   inherits the header's colour and any hover state. Height is set in em so it
   tracks .brand's font-size; 0.72em lands the cap height on Titillium's, which
   is what the nav beside it is set in. */
.brand {
  display: flex;
  align-items: center;
  gap: 12px;
  font-size: 15px;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
/* The wordmark only — the nav, the footer and body copy stay in Titillium. */
.brand__mark { height: 0.72em; width: auto; display: block; overflow: visible; }
.brand img { height: 30px; width: auto; }

.nav ul {
  display: flex;
  gap: clamp(18px, 3vw, 32px);
}
.nav a {
  font-size: 13px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  padding-bottom: 4px;
  border-bottom: 1px solid transparent;
  transition: color .18s, border-color .18s;
}
.nav a:hover { color: var(--accent); }
.nav a.is-active { color: var(--accent); border-bottom-color: var(--accent); }

.nav-toggle {
  display: none;
  align-items: center;
  gap: 10px;
  background: none;
  border: 0;
  color: inherit;
  font: inherit;
  font-size: 13px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  cursor: pointer;
  padding: 8px 0;
}
.nav-toggle__bars {
  width: 22px; height: 9px;
  border-top: 1px solid currentColor;
  border-bottom: 1px solid currentColor;
  transition: transform .2s;
}
.nav-toggle[aria-expanded="true"] .nav-toggle__bars { transform: rotate(90deg); }

@media (max-width: 760px) {
  .nav-toggle { display: inline-flex; }
  .nav {
    display: none;
    order: 3;
    width: 100%;
    border-top: 1px solid var(--line);
    padding-top: 18px;
    margin-top: 4px;
  }
  .site-header { flex-wrap: wrap; }
  .site-header.is-open .nav { display: block; }
  .nav ul { flex-direction: column; gap: 16px; }
  .nav a { font-size: 16px; }
}

/* ------------------------------------------------------- month of light */
/* One colour per month, revealed under the beam on the homepage mark and
   picked up by the cursor while it is over that mark. js/main.js stamps
   data-month on <html> from the visitor's own date, so the site turns over
   on the 1st with nothing to maintain.

   Each ramp is anchored to the same lightness span as the original amber
   (dark crown -> light base), so no month reads flatter than another; only
   hue and saturation change between them. Edit the values here — nothing
   about the colours lives in the JavaScript. */
html[data-month="jan"] { --m-dark:#A44C00; --m-mid:#DA8754; --m-light:#FFCBAD; }  /* January · Caramel */
html[data-month="feb"] { --m-dark:#855290; --m-mid:#BA8AC4; --m-light:#EEC8F6; }  /* February · Sheer Lilac */
html[data-month="mar"] { --m-dark:#047972; --m-mid:#60AFA7; --m-light:#AAE5DE; }  /* March · Fair Aqua */
html[data-month="apr"] { --m-dark:#C70005; --m-mid:#FF6455; --m-light:#FFC8C0; }  /* April · Cayenne */
html[data-month="may"] { --m-dark:#367C00; --m-mid:#72B354; --m-light:#B5E99F; }  /* May · Bud Green */
html[data-month="jun"] { --m-dark:#7F6600; --m-mid:#BF9A00; --m-light:#F6D56C; }  /* June · Aspen Gold */
html[data-month="jul"] { --m-dark:#A94437; --m-mid:#DF8072; --m-light:#FFC8BF; }  /* July · Coral Blush */
html[data-month="aug"] { --m-dark:#A14F00; --m-mid:#EF7900; --m-light:#FFCBAA; }  /* August · Sun Orange */
html[data-month="sep"] { --m-dark:#5362AC; --m-mid:#899ADF; --m-light:#CAD6FF; }  /* September · Baja Blue */
html[data-month="oct"] { --m-dark:#4B6C90; --m-mid:#83A1C4; --m-light:#C2DAF6; }  /* October · Cerulean */
html[data-month="nov"] { --m-dark:#AE3952; --m-mid:#E47989; --m-light:#FFC7CC; }  /* November · Claret Red */
html[data-month="dec"] { --m-dark:#34718D; --m-mid:#72A7C1; --m-light:#B5DFF4; }  /* December · Pagoda Blue */

/* Fallback: the amber the site has always used, if data-month is missing. */
:root { --m-dark:#A14F00; --m-mid:#EF7900; --m-light:#FFCBAA;
        --logo-url: url("../images/agave-logo.png"); }

/* ------------------------------------------------------------ cursor orb */
/* A lamp, not a pointer: a hard white centre that acts as the click point,
   inside a soft warm spill. Over the homepage mark the spill takes the
   month's hue and steps back, so a single light source casts a single
   colour instead of amber glow fighting a coloured reveal. */
html.js.has-orb body,
html.js.has-orb a,
html.js.has-orb button,
html.js.has-orb .filter,
html.js.has-orb .media.is-zoomable { cursor: none; }

.orb {
  position: fixed;
  left: 0; top: 0;
  z-index: 400;
  pointer-events: none;
  opacity: 0;
  transition: opacity .2s ease;
}
.orb.is-live { opacity: 1; }
.orb__halo {
  position: absolute;
  translate: -50% -50%;
  /* 50px across — a 25px radius of glow around the centre point. The
     gradient runs all the way to transparent at 100% rather than stopping
     short, so the element's size and the size you actually see are the
     same number; at this scale a hidden margin would make the glow read
     smaller than it measures. */
  width: 50px; height: 50px;
  border-radius: 50%;
  /* Light adds, so the halo screens over what is beneath it. */
  mix-blend-mode: screen;
  /* A Gaussian falloff sampled across 25 stops, so intensity slides
     continuously from full at the centre to nothing at the rim — three
     linear stops read as a soft-edged disc, not as light.
     `closest-side` is load-bearing: a radial-gradient defaults to
     farthest-corner, which in a square box puts the transparent stop 41%
     beyond the circle border-radius clips to. The glow was still ~2% opaque
     where it got cut off, leaving a faint but perfectly circular seam. */
  background: radial-gradient(circle closest-side,
    oklch(0.92 0.09 78 / 0.5500) 0.0%,
    oklch(0.92 0.09 78 / 0.5446) 4.2%,
    oklch(0.92 0.09 78 / 0.5288) 8.3%,
    oklch(0.92 0.09 78 / 0.5034) 12.5%,
    oklch(0.92 0.09 78 / 0.4699) 16.7%,
    oklch(0.92 0.09 78 / 0.4300) 20.8%,
    oklch(0.92 0.09 78 / 0.3859) 25.0%,
    oklch(0.92 0.09 78 / 0.3396) 29.2%,
    oklch(0.92 0.09 78 / 0.2930) 33.3%,
    oklch(0.92 0.09 78 / 0.2478) 37.5%,
    oklch(0.92 0.09 78 / 0.2056) 41.7%,
    oklch(0.92 0.09 78 / 0.1672) 45.8%,
    oklch(0.92 0.09 78 / 0.1333) 50.0%,
    oklch(0.92 0.09 78 / 0.1042) 54.2%,
    oklch(0.92 0.09 78 / 0.0799) 58.3%,
    oklch(0.92 0.09 78 / 0.0601) 62.5%,
    oklch(0.92 0.09 78 / 0.0443) 66.7%,
    oklch(0.92 0.09 78 / 0.0320) 70.8%,
    oklch(0.92 0.09 78 / 0.0227) 75.0%,
    oklch(0.92 0.09 78 / 0.0158) 79.2%,
    oklch(0.92 0.09 78 / 0.0107) 83.3%,
    oklch(0.92 0.09 78 / 0.0072) 87.5%,
    oklch(0.92 0.09 78 / 0.0047) 91.7%,
    oklch(0.92 0.09 78 / 0.0030) 95.8%,
    oklch(0.92 0.09 78 / 0.0019) 100.0%);
  transition: background .2s ease, opacity .2s ease, width .2s ease, height .2s ease;
}
.orb__core {
  position: absolute;
  translate: -50% -50%;
  width: 7px; height: 7px;
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 0 5px oklch(1 0 0 / 0.95), 0 0 1px #fff;
  transition: width .16s ease, height .16s ease;
}
/* Over the mark: the beam becomes this month's light. */
.orb.is-over-mark .orb__halo {
  background: radial-gradient(circle closest-side,
    color-mix(in oklab, var(--m-light) 62.0%, transparent) 0.0%,
    color-mix(in oklab, var(--m-light) 61.4%, transparent) 4.2%,
    color-mix(in oklab, var(--m-light) 59.6%, transparent) 8.3%,
    color-mix(in oklab, var(--m-light) 56.7%, transparent) 12.5%,
    color-mix(in oklab, var(--m-light) 53.0%, transparent) 16.7%,
    color-mix(in oklab, var(--m-light) 48.5%, transparent) 20.8%,
    color-mix(in oklab, var(--m-light) 43.5%, transparent) 25.0%,
    color-mix(in oklab, var(--m-mid) 38.3%, transparent) 29.2%,
    color-mix(in oklab, var(--m-mid) 33.0%, transparent) 33.3%,
    color-mix(in oklab, var(--m-mid) 27.9%, transparent) 37.5%,
    color-mix(in oklab, var(--m-mid) 23.2%, transparent) 41.7%,
    color-mix(in oklab, var(--m-mid) 18.8%, transparent) 45.8%,
    color-mix(in oklab, var(--m-mid) 15.0%, transparent) 50.0%,
    color-mix(in oklab, var(--m-mid) 11.8%, transparent) 54.2%,
    color-mix(in oklab, var(--m-mid) 9.0%, transparent) 58.3%,
    color-mix(in oklab, var(--m-mid) 6.8%, transparent) 62.5%,
    color-mix(in oklab, var(--m-mid) 5.0%, transparent) 66.7%,
    color-mix(in oklab, var(--m-mid) 3.6%, transparent) 70.8%,
    color-mix(in oklab, var(--m-mid) 2.6%, transparent) 75.0%,
    color-mix(in oklab, var(--m-mid) 1.8%, transparent) 79.2%,
    color-mix(in oklab, var(--m-mid) 1.2%, transparent) 83.3%,
    color-mix(in oklab, var(--m-mid) 0.8%, transparent) 87.5%,
    color-mix(in oklab, var(--m-mid) 0.5%, transparent) 91.7%,
    color-mix(in oklab, var(--m-mid) 0.3%, transparent) 95.8%,
    color-mix(in oklab, var(--m-mid) 0.2%, transparent) 100.0%);
  opacity: .55;
  /* Same proportional step-back as before, scaled to the new 50px glow. */
  width: 40px; height: 40px;
}
/* Something clickable is under the point — give the core a little presence
   so the affordance a pointer cursor normally provides is not lost. */
.orb.is-over-link .orb__core { width: 13px; height: 13px; }

@media (prefers-reduced-motion: reduce) {
  .orb, .orb__halo, .orb__core { transition: none; }
}
/* No pointer to follow: leave the native cursor alone. */
@media (pointer: coarse) { .orb { display: none; } }

/* ------------------------------------------------------------- home hero */
/* The homepage hero is a full-screen "splash" moment (logo, tagline, scroll
   cue) that lives above <main>. With JS enabled (html.js) it pins to the
   viewport and, driven by the --hero-p custom property (0 → 1, updated on
   scroll by js/main.js), shrinks and slides toward the top-left corner —
   landing on the same compact header used on every other page — while the
   real .site-header crossfades in underneath it. Without JS, or on every
   other page, it's just a static full-height intro block; nothing breaks. */

html.js body.home .site-header {
  /* Fixed rather than sticky on the homepage: a sticky header occupies a row
     of layout, which would push the pinned carousel down by its height and
     make the image jump up by that amount the moment sticky engaged. Fixed
     takes it out of flow, so the carousel starts flush at the top and never
     moves. Elsewhere the header stays sticky. */
  position: fixed;
  left: 0;
  right: 0;
  top: 0;
  visibility: hidden;
  /* --hero-q, so the header arrives in step with the logo reaching the
     corner rather than lagging behind until the curtain has fully lifted. */
  opacity: var(--hero-q, 0);
  transform: translateY(calc((1 - var(--hero-q, 0)) * -12px));
  transition: opacity .25s ease, transform .25s ease, visibility 0s linear .25s;
}
html.js body.home.is-scrolled .site-header {
  visibility: visible;
  transition: opacity .25s ease, transform .25s ease;
}

.hero { position: relative; text-align: center; }
.hero__stage {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: clamp(28px, 5vw, 48px);
  min-height: 100svh;
  padding: 64px var(--pad);
}
/* The mark is wrapped so a colour layer can sit exactly on top of it. The
   wrapper carries the sizing, the optical centring and the scroll animation
   that used to live on the <img>; the image itself is now just the amber
   artwork, and .hero__tint is the month colour revealed under the beam. */
.hero__markwrap {
  position: relative;
  width: clamp(120px, 26vw, 180px);
  /* The artwork inside agave-logo.png sits ~2.4% right of the PNG's own
     horizontal center, so shift the whole image left to optically center
     it over the nav/tagline below. */
  transform: translateX(-2.43%);
  animation: rise .8s ease;
}
.hero__mark { display: block; width: 100%; height: auto; }

.hero__tint {
  position: absolute;
  inset: 0;
  /* Same dark-crown → light-base ramp as the amber, in this month's hue. */
  background: linear-gradient(180deg, var(--m-dark) 0%, var(--m-mid) 50%, var(--m-light) 100%);
  /* Two masks intersected: the logo's own alpha, and a soft circle that
     follows the pointer — so the colour exists only inside the artwork AND
     only where the beam falls. */
  -webkit-mask-image:
    radial-gradient(circle var(--beam, 84px) at var(--mx, -999px) var(--my, -999px), #000 0%, #000 46%, transparent 100%),
    var(--logo-url);
  -webkit-mask-size: 100% 100%, 100% 100%;
  -webkit-mask-repeat: no-repeat;
  -webkit-mask-composite: source-in;
  mask-image:
    radial-gradient(circle var(--beam, 84px) at var(--mx, -999px) var(--my, -999px), #000 0%, #000 46%, transparent 100%),
    var(--logo-url);
  mask-size: 100% 100%, 100% 100%;
  mask-repeat: no-repeat;
  mask-composite: intersect;
  opacity: 0;
  transition: opacity .25s ease;
}
.hero__markwrap.is-lit .hero__tint { opacity: 1; }
.hero__tag {
  font-size: 13px;
  /* Full white, not the dim grey: this is the only line of text on the
     opening screen and it has to carry against the black. */
  color: var(--text);
  letter-spacing: 0.04em;
  max-width: 42ch;
}
.hero__cue {
  /* Wordless scroll hint: a hairline that fades in from nothing, with a
     small chevron at its foot. Drifts down slowly to read as an invitation
     rather than a static rule. */
  position: absolute;
  bottom: clamp(24px, 5vw, 48px);
  left: 50%;
  width: 1px;
  height: clamp(38px, 6vh, 54px);
  background: linear-gradient(to bottom, transparent, var(--text-mute));
  transform: translateX(-50%);
  animation: cue-drift 2.4s ease-in-out infinite;
}
.hero__cue::after {
  content: '';
  position: absolute;
  left: 50%;
  bottom: 1px;
  width: 7px; height: 7px;
  border-right: 1px solid var(--text-mute);
  border-bottom: 1px solid var(--text-mute);
  transform: translate(-50%, 0) rotate(45deg);
}

/* Transform only — opacity is left alone so the scroll-driven fade-out in
   `html.js body.home .hero__cue` isn't overridden by this animation. */
@keyframes cue-drift {
  0%, 100% { transform: translate(-50%, 0); }
  50%      { transform: translate(-50%, 7px); }
}

/* PINNED REVEAL
   The fixed stage carries an OPAQUE background: it is a curtain over the
   carousel, and fading the whole layer at once cross-dissolves hero → first
   project image. (Fading only the logo and text, over a transparent stage,
   is what previously let the two double-expose.)

   The hero reserves no scroll room of its own (height 0) — it purely hosts
   that fixed stage. Every bit of scroll room comes from .wc-pin, which is
   taller than the carousel inside it; the carousel is sticky, so it sits
   perfectly still, filling the viewport, while that extra height is
   consumed. The hero therefore dissolves off a stationary image rather than
   the image sliding up to meet it.
     .wc-pin height = carousel (100svh) + pin room (85svh)
     HERO_DISTANCE in js/main.js = 65svh — deliberately less than the 85svh
     of pin room, so the image is fully revealed and then held for a beat
     before the carousel unpins and the page carries on. */
html.js body.home .hero { height: 0; }
html.js body.home .wc-pin { height: 185svh; }
html.js body.home .work-carousel { position: sticky; top: 0; }
/* The carousel is full-screen behind the curtain from the very first frame,
   so without this a click anywhere on the "hero" would silently follow the
   invisible first slide's link. Re-enabled once the hero has dissolved. */
html.js body.home:not(.hero-done) .work-carousel { pointer-events: none; }
/* With the header out of flow the carousel starts at y=0, so this label
   would otherwise sit underneath it — push it clear of the header bar. */
html.js body.home .wc__eyebrow { top: calc(68px + clamp(20px, 2vw, 30px)); }
html.js body.home .hero__stage {
  position: fixed;
  inset: 0;
  z-index: 40;
  min-height: 0;
  padding: 0 var(--pad);
  pointer-events: none;
  background: var(--bg);
  opacity: calc(1 - var(--hero-p, 0));
}
/* Content runs on --hero-q (double speed): it has flown to the corner and
   gone by the halfway point, so it never ghosts over the project image
   while the slower curtain finishes lifting. */
html.js body.home .hero__markwrap {
  transform:
    translate(calc(var(--hero-q, 0) * (-50vw + 74px)), calc(var(--hero-q, 0) * (-50vh + 58px)))
    scale(calc(1 - var(--hero-q, 0) * 0.8))
    translateX(-2.43%);
  opacity: calc(1 - var(--hero-q, 0));
  /* The stage above is pointer-events:none so the page still scrolls; the
     mark opts back in so it can be hovered. */
  pointer-events: auto;
}
/* Once the hero has essentially faded, stop the invisible mark swallowing
   the pointer (opacity alone does not disable hit-testing). */
html.js body.home.hero-faded .hero__markwrap { pointer-events: none; }
html.js body.home .hero__tag {
  transform: translateY(calc(var(--hero-q, 0) * -24px));
  opacity: calc(1 - var(--hero-q, 0) * 1.5);
}
html.js body.home .hero__cue { opacity: calc(1 - var(--hero-q, 0) * 4); }

/* ------------------------------------------------- home closing columns */
/* About, Services and Get in touch sit side by side rather than stacked, so
   the full width of the screen carries them instead of one narrow ribbon of
   text down the middle. Below 900px they fall back to one column, because
   three columns of prose on a phone is unreadable. */
/* The columns share two lines: the label/heading/body run naturally from the
   top (About and Services both have a one-line heading, so their body text
   starts level), and the closing link is pushed to the foot of the column so
   all three sit on one baseline however long the column above it ran. That
   last part is what margins can't do on their own — the contact heading wraps
   to three lines and has no body beneath it. */
.home-close {
  border-top: 1px solid var(--line);
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 0;
  align-items: stretch;
}
.home-col {
  padding-inline: clamp(20px, 3vw, 44px);
  display: flex;
  flex-direction: column;
}
/* Hairline rules between the columns — the dividers in the sketch. */
.home-col + .home-col { border-left: 1px solid var(--line); }
.home-col:first-child { padding-inline-start: 0; }
.home-col:last-child  { padding-inline-end: 0; }
/* Breathing room between the small section label and the heading under it. */
.home-col > .eyebrow { margin-bottom: clamp(14px, 2vw, 22px); }
.home-col .display--sm { font-size: clamp(24px, 2.4vw, 34px); }
.home-col .prose p { font-size: 15px; }
.home-services__list { margin-top: 28px; }
/* One item per line here: the columns are too narrow for the wrapping
   multi-column list used on the services page. */
.home-col .home-services__list { display: flex; flex-direction: column; gap: 10px; }
/* Same opening gap in both columns that have body text, so About's paragraph
   and the Services list start on one line. */
.home-col > .prose,
.home-col > .home-services__list { margin-top: clamp(16px, 2vw, 24px); }
/* auto pushes the link to the foot of the column; the columns are stretched
   to equal height by the grid, so the three links land on one line. */
.home-col > .cta,
.home-col > .contact__mail {
  margin-top: auto;
  padding-top: clamp(28px, 4vw, 44px);
  align-self: start;
  /* Flex so the paragraph is exactly as tall as the link inside it. The three
     paragraphs bottom-align, and with no leftover line-box leading the links'
     underlines land on one line despite the email being 17px against the
     ghost links' 12.5px. */
  display: flex;
  align-items: flex-end;
}
/* .contact is centred and top-padded for the standalone contact page; in a
   column it has to sit flush with its two neighbours. */
.home-col.contact { text-align: left; padding-top: 0; }
/* .contact .eyebrow sets its own 24px further down the sheet and would win on
   source order, knocking this column's heading 2px out of line with the other
   two. Match the column rule explicitly. */
.home-col.contact > .eyebrow { margin-bottom: clamp(14px, 2vw, 22px); }
.home-col.contact .display--sm { margin-inline: 0; max-width: none; }
.home-col .contact__mail { font-size: 17px; }

@media (max-width: 900px) {
  /* Stacked, there is no shared baseline to hold — back to normal flow. */
  .home-close {
    grid-template-columns: 1fr;
    gap: clamp(44px, 7vw, 64px);
  }
  .home-col {
    display: block;
    padding-inline: 0;
  }
  .home-col > .cta,
  .home-col > .contact__mail { padding-top: 0; margin-top: clamp(22px, 3vw, 32px); }
  .home-col + .home-col {
    border-left: 0;
    border-top: 1px solid var(--line);
    padding-top: clamp(40px, 6vw, 60px);
  }
}

/* -------------------------------------------------- home work carousel */
/* Full-bleed, autoplaying carousel of project hero shots — see main.js for
   the slide/dot/arrow logic. Falls back to a plain first-slide view with no
   JS: only .is-active is ever shown, arrows/dots are inert without their
   click handlers, which is a reasonable static result. */

.work-carousel {
  position: relative;
  height: 100svh;
  overflow: hidden;
  background: var(--bg-raise);
}
.wc__eyebrow {
  position: absolute;
  left: var(--pad); top: clamp(24px, 4vw, 40px);
  z-index: 6;
  font-size: 12px;
  font-weight: 400;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: oklch(0.97 0.01 60);
  /* Sits directly on the photo, so it needs its own contrast guard — some
     project images are bright enough to swallow plain grey text. */
  text-shadow: 0 1px 6px oklch(0.1 0.006 55 / 0.85);
}
.wc__track { position: absolute; inset: 0; }
.wc__slide {
  position: absolute;
  inset: 0;
  display: block;
  opacity: 0;
  visibility: hidden;
  transition: opacity 1.1s ease;
}
.wc__slide.is-active { opacity: 1; visibility: visible; z-index: 1; }
.media.wc__media { position: absolute; inset: 0; aspect-ratio: auto; }
.wc__media img { width: 100%; height: 100%; object-fit: cover; }
.wc__slide::after {
  content: '';
  position: absolute; inset: 0;
  background: linear-gradient(180deg,
    oklch(0.10 0.006 55 / 0.1) 0%,
    oklch(0.10 0.006 55 / 0.05) 45%,
    oklch(0.10 0.006 55 / 0.9) 100%);
}
.wc__caption {
  position: absolute;
  left: var(--pad); right: var(--pad); bottom: clamp(88px, 12vw, 130px);
  z-index: 2;
  max-width: 720px;
}
.wc__title {
  font-size: clamp(30px, 5vw, 58px);
  font-weight: 300;
  line-height: 1.08;
  margin-top: 10px;
}
.wc__client { font-size: 13px; color: var(--text-mute); margin-top: 12px; }

.wc__arrow {
  position: absolute;
  top: 50%; transform: translateY(-50%);
  z-index: 6;
  width: 44px; height: 44px;
  display: flex; align-items: center; justify-content: center;
  border: 1px solid oklch(1 0 0 / 0.28);
  border-radius: 50%;
  background: oklch(0.10 0.006 55 / 0.35);
  color: var(--text);
  font-size: 22px;
  line-height: 1;
  cursor: pointer;
  transition: border-color .18s, background .18s;
}
.wc__arrow:hover { border-color: var(--accent); background: oklch(0.10 0.006 55 / 0.6); }
.wc__arrow--prev { left: clamp(14px, 3vw, 32px); }
.wc__arrow--next { right: clamp(14px, 3vw, 32px); }

.wc__meta {
  position: absolute;
  right: var(--pad); bottom: clamp(28px, 5vw, 48px);
  z-index: 6;
  display: flex;
  align-items: center;
  gap: 20px;
}
/* On a phone there are no arrows flanking the slide, so dots pinned to the
   right edge read as stranded. Centre them under the caption instead. */
@media (max-width: 700px) {
  .wc__meta {
    left: var(--pad); right: var(--pad);
    justify-content: center;
  }
}
.wc__count {
  font-size: 12px;
  letter-spacing: 0.08em;
  color: var(--text-mute);
  font-variant-numeric: tabular-nums;
}
.wc__dots { display: flex; gap: 10px; }
.wc__dot {
  width: 8px; height: 8px;
  border-radius: 50%;
  background: oklch(1 0 0 / 0.3);
  border: 0;
  padding: 0;
  cursor: pointer;
  transition: background .18s, transform .18s;
}
.wc__dot.is-active { background: var(--accent); transform: scale(1.35); }

.wc__cta { text-align: center; padding: clamp(48px, 8vw, 72px) var(--pad); }

@media (max-width: 700px) {
  .wc__arrow { display: none; }
  .wc__caption { bottom: clamp(72px, 18vw, 100px); }
}

@keyframes rise { from { opacity: 0; transform: translateY(14px) translateX(-2.43%); } to { opacity: 1; transform: translateX(-2.43%); } }

/* ------------------------------------------------------- media / picture */

.media {
  position: relative;
  aspect-ratio: var(--ratio, 4 / 5);
  overflow: hidden;
  background:
    repeating-linear-gradient(45deg,
      var(--bg-raise), var(--bg-raise) 10px,
      var(--bg-raise2) 10px, var(--bg-raise2) 20px);
}
.media img {
  width: 100%; height: 100%;
  object-fit: cover;
}
.media__ph {
  position: absolute;
  top: 14px; left: 14px;
  font-size: 10px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-dim);
}
/* Self-hosted looping clip inside a .media frame (project galleries).
   The clip plays silently in place while it is on screen and pauses the
   moment it scrolls away — see the gallery-video observer in main.js. A page
   with twenty-five clips must never have twenty-five of them decoding at
   once. The poster frame covers the gap before playback starts. */
.media--video video {
  position: absolute;
  inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  display: block;
}

/* Responsive YouTube/Vimeo embed — 16:9 box, no fixed pixel sizes. */
.video-embed {
  aspect-ratio: 16 / 9;
  width: 100%;
  margin-top: clamp(28px, 4vw, 44px);
  background: var(--bg-raise);
}
.video-embed iframe { width: 100%; height: 100%; border: 0; display: block; }
/* .project__body is a 220px | 1fr grid; without this the embed auto-flows
   into the narrow facts column instead of sitting under the description. */
.project__body > .video-embed { grid-column: 2; }
@media (max-width: 760px) { .project__body > .video-embed { grid-column: 1; } }
/* The trailer belongs to the gallery, not the text column: it sits in its own
   .wrap so it spans the same width as two gallery photos side by side. */
.project__embed {
  padding-top: 0;
  padding-bottom: 4px;
}
.project__embed .video-embed { margin-top: 0; }
@media (max-width: 560px) { .project__embed { padding-bottom: 8px; } }

/* JS adds .has-image once the file actually loads.
   Hide the pending image with opacity, NOT display:none. A display:none
   image has no box, so the browser never considers it "near the viewport"
   and a loading="lazy" image therefore never begins loading — it stays
   hidden forever waiting for a load event that cannot fire, and every
   lazy photo on the site silently shows the placeholder instead. Opacity
   keeps the box (and the lazy-loading behaviour) intact. */
.media.has-image .media__ph { display: none; }
.media img { opacity: 0; transition: opacity .45s ease; }
.media.has-image img { opacity: 1; }

/* -------------------------------------------------------------- lightbox */
/* Overlay is injected by js/main.js on project pages; see the lightbox
   block there. Without JS nothing is added and the gallery stays a plain
   grid of photos, which is a perfectly good fallback. */

.media.is-zoomable { cursor: zoom-in; }
.media.is-zoomable::after {
  content: '';
  position: absolute; inset: 0;
  box-shadow: inset 0 0 0 0 var(--accent);
  transition: box-shadow .18s;
  pointer-events: none;
}
.media.is-zoomable:hover::after,
.media.is-zoomable:focus-visible::after { box-shadow: inset 0 0 0 2px var(--accent); }

body.lb-open { overflow: hidden; }

.lb {
  position: fixed;
  inset: 0;
  z-index: 300;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: clamp(52px, 8vw, 96px) clamp(16px, 9vw, 110px);
  background: oklch(0.08 0.006 55 / 0.97);
  animation: lb-in .2s ease;
}
.lb[hidden] { display: none; }
@keyframes lb-in { from { opacity: 0; } to { opacity: 1; } }

.lb__img, .lb__video {
  max-width: 100%;
  max-height: 100%;
  width: auto;
  height: auto;
  object-fit: contain;
}
/* A video whose file never loads has no intrinsic size, so `width:auto` would
   collapse it to the 300x150 default box — a tiny grey stub floating in the
   middle of the overlay. Give it a real frame to sit in either way. */
.lb__video { width: min(100%, 1280px); aspect-ratio: 16 / 9; background: #000; }
.lb__img[hidden], .lb__video[hidden], .lb__fail[hidden] { display: none; }
/* Shown when a clip 404s or the browser can't decode it. Silence here just
   looks like a broken site; saying so out loud is more use to everyone. */
.lb__fail {
  position: absolute;
  left: 50%; top: 50%;
  translate: -50% -50%;
  max-width: 34ch;
  text-align: center;
  font-size: 14px;
  line-height: 1.6;
  color: var(--text-soft);
}
.lb__fail code {
  display: block;
  margin-top: 10px;
  font-size: 12px;
  color: var(--text-dim);
  word-break: break-all;
}

/* A clip in the grid plays silently on loop; the badge marks it as a video
   and signals that clicking opens it with sound and controls. It fades back
   once playback has actually begun so it doesn't sit over moving footage. */
.media[data-video].is-playing .media__play { opacity: 0.28; }
.media[data-video]:hover .media__play { opacity: 1; }
.media[data-video] .media__play::before { content: '\25B6'; }
/* Tucked into the bottom-left corner and kept small: it is a marker saying
   "this one moves", not a call to action. Centred and large it sat on top of
   the shot, which is the thing people actually came to look at. */
.media[data-video] .media__play {
  position: absolute;
  left: 12px; bottom: 12px;
  width: 26px; height: 26px;
  display: flex; align-items: center; justify-content: center;
  border: 1px solid oklch(1 0 0 / 0.45);
  border-radius: 50%;
  background: oklch(0.10 0.006 55 / 0.4);
  color: var(--text);
  font-size: 9px;
  padding-left: 2px;
  pointer-events: none;
  transition: border-color .18s, background .18s, opacity .3s;
}
@media (max-width: 560px) {
  .media[data-video] .media__play { left: 9px; bottom: 9px; width: 23px; height: 23px; font-size: 8px; }
}
.media[data-video]:hover .media__play,
.media[data-video]:focus-visible .media__play {
  border-color: var(--accent);
  background: oklch(0.10 0.006 55 / 0.7);
}

.lb__btn {
  position: absolute;
  display: flex; align-items: center; justify-content: center;
  width: 46px; height: 46px;
  border: 1px solid oklch(1 0 0 / 0.28);
  border-radius: 50%;
  background: oklch(0.10 0.006 55 / 0.5);
  color: var(--text);
  font: inherit;
  font-size: 22px;
  line-height: 1;
  cursor: pointer;
  transition: border-color .18s, background .18s;
}
.lb__btn:hover { border-color: var(--accent); background: oklch(0.10 0.006 55 / 0.85); }
.lb__close { top: clamp(14px, 3vw, 28px); right: clamp(14px, 3vw, 28px); font-size: 16px; }
.lb__nav--prev { left: clamp(10px, 2.5vw, 30px); top: 50%; transform: translateY(-50%); }
.lb__nav--next { right: clamp(10px, 2.5vw, 30px); top: 50%; transform: translateY(-50%); }

.lb__count {
  position: absolute;
  bottom: clamp(16px, 3vw, 30px);
  left: 50%;
  transform: translateX(-50%);
  font-size: 12px;
  letter-spacing: 0.08em;
  color: var(--text-mute);
  font-variant-numeric: tabular-nums;
}

@media (max-width: 620px) {
  /* No room for side arrows on a phone — swipe instead (handled in JS). */
  .lb__nav { display: none; }
  .lb { padding: 64px 12px 52px; }
}

/* --------------------------------------------------------------- gallery */

.filters {
  display: flex;
  flex-wrap: wrap;
  gap: 14px 28px;
  margin-bottom: clamp(28px, 5vw, 48px);
}
.filter {
  background: none;
  border: 0;
  border-bottom: 1px solid transparent;
  color: var(--text-mute);
  font: inherit;
  font-size: 12.5px;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  padding: 0 0 4px;
  cursor: pointer;
  transition: color .18s, border-color .18s;
}
.filter:hover { color: var(--text); }
.filter.is-active { color: var(--accent); border-bottom-color: var(--accent); }

.gallery {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 4px;
}
@media (max-width: 900px) { .gallery { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 560px) { .gallery { grid-template-columns: 1fr; gap: 8px; } }

.card { position: relative; display: block; }
.card .media { transition: transform .5s ease; }
.card:hover .media { transform: scale(1.03); }
.card__body {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  padding: 22px;
  background: linear-gradient(180deg, transparent 45%, oklch(0.10 0.006 55 / 0.92));
  pointer-events: none;
}
.card__title {
  font-size: clamp(19px, 2.2vw, 24px);
  font-weight: 300;
  margin-top: 6px;
}
.card__client { font-size: 13px; color: var(--text-mute); margin-top: 4px; }
.card[hidden] { display: none; }

.gallery__empty { color: var(--text-mute); font-size: 15px; margin-top: 32px; }

/* One-line caption above a gallery, for when the images need context
   (e.g. renders of a space that has not been built). */
.medianote {
  max-width: 1120px;
  margin-inline: auto;
  padding: 0 var(--pad) 14px;
  font-size: 13px;
  color: var(--text-mute);
}
.medianote b { font-weight: 400; color: var(--text-soft); }

/* ------------------------------------------------- concept / development */
/* Kept visually distinct from the gallery above it on purpose: these are
   renders and simulations, not documentation of the built piece, and a
   viewer should never have to guess which they are looking at. */
.concept {
  padding-top: clamp(40px, 6vw, 64px);
  border-top: 1px solid var(--line);
}
.concept__head { max-width: 62ch; margin-bottom: clamp(24px, 4vw, 36px); }
.concept__note {
  margin-top: 10px;
  font-size: 14.5px;
  line-height: 1.7;
  color: var(--text-mute);
}
.concept__grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 4px;
}
@media (max-width: 560px) { .concept__grid { grid-template-columns: 1fr; gap: 8px; } }

/* Downloadable documents attached to a project. */
.docs { margin-top: clamp(30px, 4vw, 44px); }
.docs__list { display: flex; flex-direction: column; gap: 10px; margin-top: 14px; }
.doc {
  display: flex;
  align-items: baseline;
  gap: 14px;
  padding: 14px 16px;
  border: 1px solid var(--line);
  font-size: 14px;
  color: var(--text-soft);
  transition: border-color .18s, color .18s;
}
.doc:hover { border-color: var(--accent); color: var(--text); }
.doc__meta {
  margin-left: auto;
  font-size: 11.5px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-dim);
  white-space: nowrap;
}
.project__body > .docs { grid-column: 2; }
@media (max-width: 760px) { .project__body > .docs { grid-column: 1; } }

/* --------------------------------------------------------- project page */

.hero { position: relative; }
.media--hero {
  width: 100%;
  aspect-ratio: 16 / 9;
  max-height: 78svh;
}
/* min-height + a fixed aspect-ratio sets an implied WIDTH (340px × 16/9 =
   604px), which overflowed the viewport and gave every project page a
   horizontal scrollbar on phones. Only apply it once the screen is wide
   enough to contain that width. */
@media (min-width: 620px) {
  .media--hero { min-height: 340px; }
}
.media--hero::after {
  content: '';
  position: absolute; inset: 0;
  background: linear-gradient(180deg,
    oklch(0.15 0.006 55 / 0.15), oklch(0.15 0.006 55 / 0.92));
}
.hero__text {
  position: absolute;
  left: var(--pad); right: var(--pad); bottom: clamp(28px, 5vw, 56px);
  z-index: 2;
  max-width: 900px;
}
.hero__text .display { margin-top: 14px; }

.project__body {
  display: grid;
  grid-template-columns: 220px minmax(0, 1fr);
  gap: clamp(28px, 5vw, 64px);
  padding-bottom: clamp(40px, 6vw, 60px);
}
@media (max-width: 760px) {
  .project__body { grid-template-columns: 1fr; }
}

.facts {
  display: flex;
  flex-direction: column;
  gap: 20px;
  font-size: 12.5px;
}
.facts dt {
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-dim);
  margin-bottom: 4px;
}
.facts dd { color: oklch(0.90 0.004 60); }

@media (max-width: 760px) {
  .facts { flex-direction: row; flex-wrap: wrap; gap: 20px 36px; }
}

.pull {
  border-left: 1px solid var(--accent);
  padding-left: 24px;
  margin-top: 24px;
  font-style: italic;
  font-size: clamp(17px, 2vw, 19px);
  color: oklch(0.80 0.006 60);
  line-height: 1.6;
}
.pull::before { content: '“'; }
.pull::after  { content: '”'; }

.project__gallery {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 4px;
  padding-top: 0;
  padding-bottom: clamp(60px, 9vw, 100px);
}
@media (max-width: 560px) { .project__gallery { grid-template-columns: 1fr; gap: 8px; } }

.project__nav {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 20px;
  padding-top: 0;
  padding-bottom: clamp(70px, 10vw, 120px);
  border-top: 1px solid var(--line);
  margin-top: 0;
}
.project__nav .ghost { border-bottom-color: transparent; padding-top: 28px; }
.project__nav .ghost:hover { border-bottom-color: var(--accent); }
/* Narrow screens: previous hard left, next hard right, "All work" centred on
   its own row underneath. Left to flex-wrap the three links reflowed into a
   scrambled order — previous and All work sharing the first line, next
   dropping below on the left — which read as an accident. A two-column grid
   with explicit placement holds the same shape at every width. */
@media (max-width: 700px) {
  .project__nav {
    display: grid;
    grid-template-columns: 1fr 1fr;
    column-gap: 16px;
    row-gap: 0;
  }
  .project__nav .ghost:nth-of-type(1) { grid-row: 1; grid-column: 1; justify-self: start; }
  .project__nav .ghost:nth-of-type(3) { grid-row: 1; grid-column: 2; justify-self: end; text-align: right; }
  .project__nav .ghost:nth-of-type(2) {
    grid-row: 2; grid-column: 1 / -1;
    justify-self: center;
    padding-top: 22px;
  }
}

/* ----------------------------------------------------------------- about */

.about {
  display: grid;
  grid-template-columns: minmax(200px, 300px) minmax(0, 1fr);
  gap: clamp(28px, 5vw, 56px);
  align-items: start;
}
.about__portrait { width: 100%; height: auto; object-fit: cover; }
@media (max-width: 760px) {
  .about { grid-template-columns: 1fr; }
  .about__portrait { max-width: 320px; }
}

.about .display--sm { margin-bottom: 20px; }

.clients { margin-top: 44px; }
.clients .eyebrow { margin-bottom: 16px; }
.clients__list {
  display: flex;
  flex-wrap: wrap;
  gap: 12px 32px;
  font-size: 15px;
  color: oklch(0.75 0.006 60);
}

/* ------------------------------------------------------ services / press */

.rows { margin-top: clamp(36px, 6vw, 56px); counter-reset: row; }

.row {
  display: grid;
  grid-template-columns: 60px minmax(0, 1fr);
  /* On Services the counter, title and body are three separate grid items,
     so without this the body auto-flows onto a second grid row and lands in
     the 60px number column — squeezing the paragraph to a few characters
     wide. Pinning both to column 2 stacks them under each other correctly.
     (Press is unaffected: there the title and body share a wrapper div.) */
  gap: clamp(16px, 3vw, 32px);
  padding: clamp(26px, 4vw, 36px) 0;
  border-top: 1px solid var(--line);
}
.row::before {
  counter-increment: row;
  content: counter(row, decimal-leading-zero);
  font-size: 13px;
  color: var(--accent);
}
.row > .row__title,
.row > .row__body { grid-column: 2; }

/* Services runs its list tighter than Press does. Three things buy the height:
   less vertical padding, a slightly smaller title, and — the big one — the
   32px grid row-gap that was sitting between each title and its own
   description. That gap is a column gap worth keeping and a row gap worth
   closing, so it's split here. At 108px a row the four disciplines all sit
   above the fold on a laptop, and the title binds to its own line of copy
   instead of floating between two of them. */
.rows--tight { margin-top: clamp(24px, 3vw, 32px); }
.rows--tight .row { padding: clamp(16px, 2.4vw, 20px) 0; gap: 10px clamp(16px, 3vw, 32px); }
.rows--tight .row__title { font-size: clamp(20px, 2.6vw, 24px); margin-bottom: 0; }

.rows--press .row::before { content: none; }
.rows--press .row { grid-template-columns: 120px minmax(0, 1fr); }
.row__meta { font-size: 12.5px; letter-spacing: 0.05em; color: var(--text-mute); }


@media (max-width: 620px) {
  .row, .rows--press .row { grid-template-columns: 1fr; gap: 10px; }
}

.row__title { font-size: clamp(22px, 3vw, 28px); font-weight: 300; margin-bottom: 10px; }
.row__title--sm { font-size: clamp(19px, 2.4vw, 24px); }
.row__body {
  font-size: clamp(15px, 1.6vw, 17px);
  line-height: 1.7;
  color: var(--text-soft);
  max-width: 62ch;
}

/* --------------------------------------------------------------- contact */

/* Left-aligned and top-padded like every other page, so its heading lands on
   the same line as the rest. It used to be centred with a much deeper top
   padding, which put its title in a third position of its own. */
.contact .eyebrow { margin-bottom: 24px; }
.contact .display--sm { max-width: 26ch; }
.contact__mail {
  margin-top: clamp(32px, 6vw, 48px);
  font-size: clamp(17px, 2.6vw, 22px);
  word-break: break-word;
}
.contact__mail a {
  border-bottom: 1px solid var(--accent);
  padding-bottom: 8px;
}
.contact__links {
  margin-top: 56px;
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-start;
  gap: 16px 32px;
  font-size: 13px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-mute);
}
.contact__links a:hover { color: var(--accent); }

/* ---------------------------------------------------------------- footer */

.site-footer {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 8px 24px;
  padding: 28px var(--pad);
  border-top: 1px solid var(--line);
  font-size: 11.5px;
  letter-spacing: 0.04em;
  color: var(--text-dim);
}

/* ------------------------------------------------------------ preferences */

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
}

@media print {
  .site-header, .site-footer, .filters, .project__nav, .skip { display: none; }
  body { background: #fff; color: #000; }
}
