/* ============================================================
   Formix
   The hero is a single canvas. Everything about the giant headline
   there is drawn in JS, because the headline is a mask, not text.
   This file styles the page around it.
   ============================================================ */

:root {
  --ink: #0B0908;          /* deep background */
  --ink-2: #131010;
  --cream: #F4EDE4;        /* primary typography */
  --cream-dim: #A79E94;
  --burnt: #E5511F;        /* emphasis */
  --burnt-2: #FF7A3C;
  --ember: #7A1F0A;        /* atmospheric accent */
  --line: rgba(244, 237, 228, .13);

  --gutter: clamp(18px, 4vw, 72px);
  --display: "Anton", "Arial Narrow", Impact, sans-serif;
  --ui: "Inter", system-ui, -apple-system, sans-serif;
}

* { box-sizing: border-box; }

html {
  background: var(--ink);
  scroll-behavior: smooth;
  scroll-padding-top: 96px;
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  font-family: var(--ui);
  color: var(--cream);
  overflow-x: clip;
  /* the canvas paints its own ground; this is what shows outside the letters */
  background: var(--ink);
}

body.is-loading { overflow: hidden; height: 100vh; }

img { display: block; max-width: 100%; }
a { color: inherit; text-decoration: none; }

::selection { background: var(--burnt); color: #fff; }

/* ============================================================
   Loader
   ============================================================ */
.loader {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: grid;
  place-content: center;
  gap: 18px;
  background: var(--ink);
  transition: opacity .7s ease .1s, visibility .7s ease .1s;
}
.loader.is-done { opacity: 0; visibility: hidden; }
.loader__mark {
  font-family: var(--display);
  font-size: clamp(34px, 7vw, 72px);
  letter-spacing: .04em;
  text-align: center;
  color: var(--cream);
}
.loader__bar {
  width: min(240px, 60vw);
  height: 2px;
  background: var(--line);
  overflow: hidden;
}
.loader__bar i { display: block; height: 100%; width: 0; background: var(--burnt); }
.loader__pct {
  font-size: 11px;
  letter-spacing: .22em;
  text-transform: uppercase;
  color: var(--cream-dim);
  text-align: center;
}

/* ============================================================
   The stage. Fixed behind everything, painted by main.js.
   ============================================================ */
.stage {
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
}
/* atmosphere outside the letterforms, so the dark is not dead flat.
   Costs nothing per frame: it never repaints. */
.stage__glow {
  position: absolute;
  inset: 0;
  background:
    radial-gradient(120% 90% at 62% 42%, rgba(122, 31, 10, .50) 0%, rgba(122, 31, 10, 0) 62%),
    radial-gradient(70% 60% at 22% 88%, rgba(229, 81, 31, .16) 0%, rgba(229, 81, 31, 0) 70%);
  opacity: 0;
  transition: opacity .6s ease;
}
.stage.is-lit .stage__glow { opacity: 1; }
.stage__canvas { position: absolute; inset: 0; width: 100%; height: 100%; }

/* film grain. An SVG turbulence tile, animated by steps so it never
   interpolates: a smooth grain reads as a gradient, not as film. */
.stage__grain {
  position: absolute;
  inset: -50%;
  opacity: .16;
  mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)'/%3E%3C/svg%3E");
  animation: grain 1.1s steps(4) infinite;
  will-change: transform;
}
@keyframes grain {
  0%   { transform: translate(0, 0); }
  25%  { transform: translate(-2%, 1.5%); }
  50%  { transform: translate(1.5%, -1%); }
  75%  { transform: translate(-1%, -2%); }
  100% { transform: translate(0, 0); }
}

/* ============================================================
   Nav
   ============================================================ */
.nav {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 100;
  display: flex;
  align-items: center;
  gap: 24px;
  padding: 20px var(--gutter);
  transition: background .4s ease, backdrop-filter .4s ease, border-color .4s ease;
  border-bottom: 1px solid transparent;
}
.nav.is-stuck {
  background: rgba(11, 9, 8, .72);
  backdrop-filter: blur(14px);
  border-bottom-color: var(--line);
}
.nav__logo {
  font-family: var(--display);
  font-size: 22px;
  letter-spacing: .04em;
  margin-right: auto;
}
.nav__logo span { opacity: .55; }
.nav__links { display: flex; gap: 26px; }
.nav__links a {
  font-size: 13px;
  font-weight: 500;
  color: var(--cream-dim);
  transition: color .25s ease;
}
.nav__links a:hover, .nav__links a.is-active { color: var(--cream); }
.nav__cta {
  font-size: 13px;
  font-weight: 600;
  padding: 10px 18px;
  border: 1px solid var(--line);
  border-radius: 999px;
  transition: background .25s ease, color .25s ease, border-color .25s ease;
}
.nav__cta i { font-style: normal; }
.nav__cta:hover { background: var(--burnt); border-color: var(--burnt); color: #fff; }

/* ============================================================
   Hero. 400vh of scroll, a 100vh sticky window.
   The headline lives in the canvas; these are the small parts.
   Every element here is driven from the same scroll progress in
   main.js, so nothing can drift out of step with the frames.
   ============================================================ */
.hero { position: relative; height: 400vh; z-index: 1; }
.hero__pin {
  position: sticky;
  top: 0;
  height: 100vh;
  overflow: hidden;
}
.hero__sr {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* The headline is fitted to fill most of the viewport, so it reaches into the
   gutters where this furniture lives and the two will overlap. A shadow keeps
   the small type readable across a letterform without needing a panel behind
   it, which would read as UI chrome and break the editorial feel. */
.hero__pin [data-hud], .hero__pin .ann {
  will-change: opacity, transform;
  text-shadow: 0 1px 2px rgba(11, 9, 8, .95), 0 0 16px rgba(11, 9, 8, .8);
}

.hero__eyebrow {
  position: absolute;
  top: 96px; left: var(--gutter);
  margin: 0;
  display: flex; align-items: center; gap: 9px;
  font-size: 11px; font-weight: 600;
  letter-spacing: .26em; text-transform: uppercase;
  color: var(--cream);
}
.dot {
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--burnt); flex: none;
}
.hero__disciplines {
  position: absolute;
  top: 96px; right: var(--gutter);
  margin: 0;
  font-size: 11px; font-weight: 500;
  letter-spacing: .18em; text-transform: uppercase;
  color: var(--cream-dim);
}
.hero__disciplines b { color: var(--burnt); margin: 0 4px; }

.hero__scroll {
  position: absolute;
  bottom: 34px; left: var(--gutter);
  margin: 0;
  font-size: 11px; font-weight: 500;
  letter-spacing: .22em; text-transform: uppercase;
  color: var(--cream-dim);
}
.hero__scroll i { font-style: normal; display: inline-block; animation: bob 1.8s ease-in-out infinite; }
@keyframes bob { 0%,100% { transform: translateY(0); } 50% { transform: translateY(5px); } }

.hero__caption {
  position: absolute;
  bottom: 34px; right: var(--gutter);
  margin: 0;
  max-width: 30ch;
  font-size: 13px; line-height: 1.55;
  color: var(--cream-dim);
  text-align: right;
}
.hero__caption b { color: var(--cream); font-weight: 600; }

/* editorial annotations, not dashboard cards: a rule, a number, a label */
.ann {
  position: absolute;
  padding-left: 14px;
  border-left: 1px solid var(--burnt);
}
.ann__n {
  display: block;
  font-family: var(--display);
  font-size: clamp(26px, 3.1vw, 44px);
  line-height: .95;
  letter-spacing: .01em;
}
.ann__l {
  display: block;
  margin-top: 5px;
  font-size: 10px; font-weight: 500;
  letter-spacing: .2em; text-transform: uppercase;
  color: var(--cream-dim);
}
.ann--a { top: 30%; left: var(--gutter); }
.ann--b { bottom: 25%; left: calc(var(--gutter) + 4vw); }
.ann--c { top: 40%; right: var(--gutter); text-align: left; }

/* ============================================================
   Giant typography, shared by every section below the hero
   ============================================================ */
.giant {
  font-family: var(--display);
  font-weight: 400;
  font-size: clamp(52px, 13.5vw, 260px);
  line-height: .84;
  letter-spacing: -.018em;
  text-transform: uppercase;
  margin: 0;

  /* A hairline outline on every giant word. On the flat cream state it is
     invisible, but once a word is filled with the portrait the letterforms
     lose their edges against the dark ground and the word stops reading as
     a word. The outline is what keeps it legible in that state.

     Sized in em so it tracks the font: a fixed 1px disappears on a 340px
     numeral and looks heavy on a 52px phone heading. */
  -webkit-text-stroke: .008em rgba(244, 237, 228, .55);
}
.giant--stack { display: block; }
.giant__line { display: block; overflow: hidden; }
.giant__line > span { display: block; }

/* Image inside the letters, cheap version: one still frame as a
   background, clipped to the glyphs. The hero needs a canvas because
   it animates 240 frames; a section heading only needs one, so this
   costs a single decoded image and no per-frame work. */
.giant__line--fill > span,
.giant--fill {
  color: transparent;
  background-image: var(--fill);
  background-size: cover;
  background-position: center 38%;
  -webkit-background-clip: text;
  background-clip: text;
}
/* a readable floor: pure image-in-text can go too dark to read against
   the page, so a faint cream underlay sits behind the glyphs */
.giant__line--fill { position: relative; }

/* Scroll reveals. Staggered clip + rise, not a blanket fade.

   Scoped to .js, which main.js sets on the root element. The hidden state
   hides real content: a heading line is shifted a full line down inside an
   overflow:hidden box, so if the observer never runs the section headings are
   invisible with no way back. Gating it means the page renders complete
   without script and only opts into animating when something is there to
   animate it. */
.js .reveal {
  opacity: 0;
  transform: translateY(26px);
  transition: opacity .8s cubic-bezier(.22,.61,.36,1),
              transform .8s cubic-bezier(.22,.61,.36,1);
}
.js .reveal.is-in { opacity: 1; transform: none; }
.js .giant__line.reveal { opacity: 1; transform: none; }
.js .giant__line.reveal > span {
  transform: translateY(105%);
  transition: transform 1s cubic-bezier(.16,.84,.28,1);
}
.js .giant__line.reveal.is-in > span { transform: none; }

/* ============================================================
   Statement slab
   ============================================================ */
.slab { position: relative; z-index: 1; padding: 22vh var(--gutter) 14vh; background: var(--ink); }
.slab__inner { max-width: 1500px; margin: 0 auto; }
.slab__foot {
  margin-top: 7vh;
  display: flex; flex-wrap: wrap; gap: 32px;
  align-items: flex-end; justify-content: space-between;
}
.slab__foot p { max-width: 46ch; margin: 0; font-size: 15px; line-height: 1.65; color: var(--cream-dim); }

.btn {
  display: inline-flex; align-items: center; gap: 9px;
  font-size: 13px; font-weight: 600;
  padding: 13px 22px; border-radius: 999px;
  white-space: nowrap;
  transition: background .25s ease, color .25s ease, border-color .25s ease, transform .25s ease;
}
.btn i { font-style: normal; }
.btn:hover { transform: translateY(-2px); }
.btn--ghost { border: 1px solid var(--line); color: var(--cream); }
.btn--ghost:hover { background: var(--cream); color: var(--ink); border-color: var(--cream); }
.btn--solid { background: var(--burnt); color: #fff; padding: 16px 28px; font-size: 14px; }
.btn--solid:hover { background: var(--burnt-2); }

.tag {
  display: flex; align-items: center; gap: 9px;
  margin: 0 0 26px;
  font-size: 11px; font-weight: 600;
  letter-spacing: .26em; text-transform: uppercase;
  color: var(--cream-dim);
}

/* ============================================================
   About
   ============================================================ */
.about { position: relative; z-index: 1; padding: 14vh var(--gutter) 16vh; background: var(--ink); }
.about > .giant, .about > .tag { max-width: 1500px; margin-left: auto; margin-right: auto; }
.about__grid {
  max-width: 1500px;
  margin: 9vh auto 0;
  display: grid;
  grid-template-columns: minmax(0, .82fr) minmax(0, 1fr);
  gap: clamp(28px, 5vw, 84px);
  align-items: start;
}
.about__shot { margin: 0; }
.about__shotInner {
  overflow: hidden;
  border-radius: 4px;
  aspect-ratio: 4 / 5;
  background: var(--ink-2);
}
.about__shotInner img {
  width: 100%; height: 100%;
  object-fit: cover;
  object-position: 62% 30%;
  transform: scale(1.06);
  transition: transform 1.4s cubic-bezier(.22,.61,.36,1);
}
.about__shot.is-in .about__shotInner img { transform: none; }
.about__shot figcaption {
  margin-top: 12px;
  font-size: 10px; letter-spacing: .2em; text-transform: uppercase;
  color: var(--cream-dim);
}
.lede { margin: 0 0 20px; font-size: clamp(19px, 2vw, 27px); line-height: 1.32; letter-spacing: -.01em; }
.about__body p:not(.lede) { margin: 0; font-size: 15px; line-height: 1.7; color: var(--cream-dim); max-width: 52ch; }
.about__stats {
  margin: 44px 0 0;
  display: flex; flex-wrap: wrap; gap: 40px;
}
.about__stats div { border-top: 1px solid var(--line); padding-top: 12px; }
.about__stats dt { font-family: var(--display); font-size: clamp(30px, 3.4vw, 52px); line-height: 1; }
.about__stats dd {
  margin: 6px 0 0;
  font-size: 10px; letter-spacing: .2em; text-transform: uppercase;
  color: var(--cream-dim);
}

/* ============================================================
   Services. Typographic rows; the image arrives inside the word.
   ============================================================ */
.services { position: relative; z-index: 1; padding: 14vh var(--gutter) 12vh; background: var(--ink); }
.services > .tag, .services > .giant, .svc { max-width: 1500px; margin-left: auto; margin-right: auto; }
.services > .giant { margin-bottom: 6vh; }

/* `margin: 0` here used to clobber the auto margins from the shared container
   rule above: same specificity, later in the file, so it won. The list then sat
   flush against the gutter while every other section centred, which only became
   visible past ~1630px where the 1500px max-width actually binds. */
.svc { list-style: none; padding: 0; margin: 0 auto; }
.svc__row {
  position: relative;
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) minmax(0, 26ch) auto;
  gap: clamp(16px, 3vw, 56px);
  align-items: center;
  padding: clamp(22px, 3.4vh, 44px) 0;
  border-top: 1px solid var(--line);
  cursor: pointer;
}
.svc__row:last-child { border-bottom: 1px solid var(--line); }
.svc__n {
  font-size: 11px; font-weight: 600;
  letter-spacing: .2em;
  color: var(--burnt);
  align-self: start;
  padding-top: .7em;
}
.svc__t {
  font-family: var(--display);
  font-weight: 400;
  font-size: clamp(40px, 7.6vw, 132px);
  line-height: .9;
  letter-spacing: -.02em;
  text-transform: uppercase;
  margin: 0;
  -webkit-text-stroke: .008em rgba(244, 237, 228, .55);

  /* The still frame sits behind the glyphs the whole time and a flat cream
     layer covers it. Hovering wipes that cover away, so there is nothing to
     fetch or lay out at interaction time.

     The cover layer is anchored right and its WIDTH is what animates, from
     100% to 0%, so the image is uncovered left to right like a reveal wipe.
     The first version went from `cover` to `0 0`, which does not animate at
     all: `cover` is a keyword with no numeric value to interpolate from, so
     the browser had no choice but to snap between the two states.

     no-repeat matters as much as the sizes. The default is repeat, so a cover
     layer at 50% width tiles twice across the word instead of uncovering half
     of it, and the wipe never reads as a wipe. */
  color: transparent;
  background-image: linear-gradient(var(--cream), var(--cream)), var(--fill);
  background-repeat: no-repeat, no-repeat;
  background-size: 100% 100%, cover;
  background-position: right center, center 34%;
  -webkit-background-clip: text;
  background-clip: text;
  /* Near-even travel with soft ends, not the ease-out used elsewhere on the
     page. A wipe wants its leading edge moving at a steady, confident pace;
     an ease-out puts nearly half the reveal in the first sixth of the time,
     which still reads as a snap with a slow tail after it. */
  transition: background-size 820ms cubic-bezier(.5, .12, .34, .94);
}
.svc__row:hover .svc__t, .svc__row.is-open .svc__t {
  background-size: 0% 100%, cover;
}
.svc__d {
  margin: 0;
  font-size: 14px; line-height: 1.62;
  color: var(--cream-dim);
  transition: color .4s ease;
}
.svc__row:hover .svc__d, .svc__row.is-open .svc__d { color: var(--cream); }
.svc__arrow {
  font-style: normal;
  font-size: 20px;
  color: var(--cream-dim);
  transition: transform .45s cubic-bezier(.22,.61,.36,1), color .45s ease;
}
.svc__row:hover .svc__arrow, .svc__row.is-open .svc__arrow {
  transform: translate(6px, -6px);
  color: var(--burnt);
}

/* ============================================================
   Work
   ============================================================ */
.work { position: relative; z-index: 1; padding: 12vh var(--gutter) 14vh; background: var(--ink); }
.work > .tag, .proj { max-width: 1500px; margin-left: auto; margin-right: auto; }
.proj { padding: clamp(24px, 5vh, 70px) 0; border-top: 1px solid var(--line); }
.proj:last-of-type { border-bottom: 1px solid var(--line); }
.proj__t {
  display: flex;
  align-items: baseline;
  gap: clamp(12px, 2.4vw, 40px);
  font-size: clamp(64px, 19vw, 340px);
  line-height: .82;
}
.proj__label {
  font-family: var(--ui);
  font-size: 11px; font-weight: 600;
  letter-spacing: .26em; text-transform: uppercase;
  color: var(--cream-dim);
  -webkit-text-fill-color: var(--cream-dim);
  /* small label inside a .giant heading: the inherited outline would smear it */
  -webkit-text-stroke: 0;
  flex: none;
}
.proj__meta {
  margin-top: 18px;
  display: flex; flex-wrap: wrap; gap: 28px;
  font-size: 12px; letter-spacing: .14em; text-transform: uppercase;
  color: var(--cream-dim);
}

/* ============================================================
   Final
   ============================================================ */
.final { position: relative; z-index: 1; padding: 18vh var(--gutter) 14vh; background: var(--ink); }
.final .giant, .final__cta { max-width: 1500px; margin-left: auto; margin-right: auto; }
.final__cta {
  margin-top: 7vh;
  display: flex; flex-wrap: wrap; gap: 26px; align-items: center;
}
.final__mail { font-size: 14px; color: var(--cream-dim); border-bottom: 1px solid var(--line); padding-bottom: 2px; }
.final__mail:hover { color: var(--cream); border-bottom-color: var(--burnt); }

.foot {
  position: relative; z-index: 1;
  display: flex; flex-wrap: wrap; gap: 16px;
  justify-content: space-between;
  padding: 26px var(--gutter);
  border-top: 1px solid var(--line);
  background: var(--ink);
  font-size: 11px; letter-spacing: .16em; text-transform: uppercase;
  color: var(--cream-dim);
}

/* ============================================================
   Narrow screens. The hero canvas handles its own type sizing in
   JS; what changes here is the furniture around it.
   ============================================================ */
@media (max-width: 900px) {
  .about__grid { grid-template-columns: 1fr; }
  .svc__row { grid-template-columns: auto minmax(0, 1fr) auto; }
  .svc__d { display: none; }
  .hero__caption, .hero__disciplines { display: none; }
  .ann--c { display: none; }
  .ann--a { top: 24%; }
  .ann--b { bottom: 22%; left: var(--gutter); }
  .proj__t { flex-direction: column; align-items: flex-start; gap: 6px; }
}
@media (max-width: 560px) {
  .nav__links { display: none; }
  .about__stats { gap: 26px; }
  .foot__mid { display: none; }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .stage__grain { animation: none; }
  .hero__scroll i { animation: none; }
  .reveal { transition-duration: .01ms; }
  .giant__line.reveal > span { transition-duration: .01ms; }
}
