/* ============================================================
   GABEZACK.COM — v2
   One typeface, white paper, black ink, no accent colour.
   System Helvetica, zero webfonts. Metadata is the ornament.
   Breakpoints: 810 / 1200 · page padding 12px · 10-col/12px grid
   ============================================================ */

:root {
  --ink: #000;
  --paper: #fff;
  --grey: #767676;
  --frame: #f2f2f2;      /* media placeholder before the film paints */
  --fill: #000;          /* row hover fill */
  --on-fill: #fff;
  --hairline: rgba(0, 0, 0, 0.14);
  --font: "Helvetica Neue", Helvetica, Arial, sans-serif;
  --ease: cubic-bezier(0.12, 0.23, 0.5, 1);
  --pad: 12px;
  --rule: rgba(255, 255, 255, 0.42);   /* nav hairline, under difference */
}

* { margin: 0; padding: 0; box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }
html.lenis, html.lenis body { height: auto; }
.lenis.lenis-smooth { scroll-behavior: auto !important; }

body {
  font-family: var(--font);
  font-size: 15px;
  line-height: 1.45;
  font-weight: 400;
  color: var(--ink);
  background: var(--paper);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

a { color: inherit; text-decoration: none; }
p { margin: 0; }
ul { list-style: none; }
button { font-family: var(--font); }
address { font-style: normal; }

::selection { background: var(--ink); color: var(--paper); }

/* ---- type roles --------------------------------------------
   Scaled to the references: Gregory's H1 is 54px and his lead
   is 28px. Display is a document heading, not a billboard.
   ------------------------------------------------------------ */
.t-display {
  font-size: clamp(22px, 2.1vw, 28px);
  line-height: 1.04;
  font-weight: 400;
  letter-spacing: -0.03em;
}
.t-lead {
  font-size: clamp(17px, 1.5vw, 20px);
  line-height: 1.2;
  font-weight: 400;
  letter-spacing: -0.02em;
}
.t-ui   { font-size: 13px; font-weight: 500; letter-spacing: 0; line-height: 1.35; }
.t-meta { font-size: 12.5px; font-weight: 500; letter-spacing: 0.012em; text-transform: uppercase; line-height: 1.35; }
.t-grey { color: var(--grey); }


/* ============================================================
   NAV — one justified row. Each label carries its own bracketed
   hairline: a 1px rule with short downward ticks at both ends,
   lifted from S-1's footer rule. The rules are nav cells, so they
   align with the words by construction, not by measurement.
   White + difference: black on paper, inverts over film.
   ============================================================ */
.nav {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 110;
  display: flex;
  justify-content: space-between;
  gap: 12px;
  padding: 0 var(--pad);
  color: #fff;
  mix-blend-mode: difference;
}
.nav__cell {
  position: relative;
  display: block;
  padding-top: 11px;
  flex: 0 1 auto;
}
/* Desktop has no rules over the nav words — the row reads on its own.
   They come back on phone, where the nav wraps to a grid and the rules
   are what separate one cell from the next. */
.nav__cell::before {
  content: "";
  display: none;
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 1px;
  background: var(--rule);
  transform-origin: left center;
}
.nav__cell--clock { text-align: right; font-variant-numeric: tabular-nums; }
/* Menu is a phone affordance only — desktop shows every destination in the row. */
.nav__cell--menu { display: none; }
.nav__btn {
  all: unset;
  cursor: pointer;
  font-family: var(--font);
  font-size: 13px;
  font-weight: 500;
}
/* The rules sweep in on load and redraw on every view change.
   Deliberately an animation with no fill mode, not a transition: the
   resting state is fully drawn, so if the animation never runs the
   rules are simply there rather than stuck at zero width. The stagger
   rides on per-cell duration for the same reason. */
@keyframes rule-draw { from { transform: scaleX(0); } to { transform: scaleX(1); } }
.nav[data-draw] .nav__cell::before {
  animation: rule-draw var(--dur, 0.5s) var(--ease);
}
@media (prefers-reduced-motion: reduce) {
  .nav[data-draw] .nav__cell::before { animation: none; }
}

/* Hover is one gesture across the site: the thing you are on goes
   grey. Rows that fill black are the exception — they invert instead,
   so they opt out below. */
a { transition: color 0.2s var(--ease); }
a:hover { color: var(--grey); }
.ix-row:hover, .ix-row:hover *,
a.ixo__row:hover, a.ixo__row:hover * { color: inherit; }

/* Links worth pressing pick up a rule on hover rather than just fading:
   the contact addresses, and Watch on Vimeo. */
.link-u, a.u {
  display: inline-block;
  font-size: 13px;
  padding-bottom: 3px;
  border-bottom: 1px solid currentColor;
  transition: color 0.2s var(--ease), border-color 0.2s var(--ease);
}
.link-u:hover, a.u:hover { color: var(--grey); }

/* ---- page-load appear (delay .3s · dur .5s) ----------------- */
.appear { opacity: 0.001; }
.js-ready .appear { animation: appear-fade 0.5s var(--ease) 0.3s forwards; }
@keyframes appear-fade { to { opacity: 1; } }

/* ---- scroll reveals ---------------------------------------- */
.reveal { opacity: 0; transform: translateY(14px);
  transition: opacity 0.7s var(--ease), transform 0.7s var(--ease); }
.reveal.is-in { opacity: 1; transform: none; }

/* rows inside a [data-stagger] cascade in. The delay lives on a custom
   property set from JS and is attached ONLY to the reveal pair — a bare
   transition-delay would also lag the hover colour on every row. */
.reveal [data-stagger] > * {
  opacity: 0;
  transform: translateY(8px);
  transition: opacity 0.6s var(--ease) var(--stag, 0ms),
              transform 0.6s var(--ease) var(--stag, 0ms),
              color 0.2s var(--ease);
}
.reveal.is-in [data-stagger] > * { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  .reveal, .reveal [data-stagger] > *, .chars .ch {
    opacity: 1; transform: none; transition: none;
  }
}

.chars .word { white-space: nowrap; display: inline-block; }
.chars .ch { display: inline-block; opacity: 0.001;
  transform: translateY(0.35em);
  transition: opacity 0.5s var(--ease), transform 0.5s var(--ease); }
.chars.is-in .ch { opacity: 1; transform: none; }

/* ============================================================
   META ROW — the ruled-document primitive.
   `01 · CAMPAIGN · CLIENT · 2025` — number left, year right,
   12.5px uppercase, hairline above and below.
   ============================================================ */
.m-row {
  display: flex;
  align-items: baseline;
  gap: 16px;
  padding: 9px 0;
  border-top: 1px solid var(--hairline);
  border-bottom: 1px solid var(--hairline);
}
.m-row__num { min-width: 28px; }
.m-row__mid { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.m-row__end { margin-left: auto; white-space: nowrap; }
/* ventures + studios read as one ruled column: single rules between entries */
.ventures .m-row, .studios .m-row { border-bottom: 0; }

/* ---- label/value stack ------------------------------------- */
/* Visually hidden, still read by search engines and screen readers. Used
   for the page h1 on the four pages whose design has no visible title. */
.vh {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.stack { border-top: 1px solid var(--hairline); padding-top: 11px; }
.stack > .t-meta { margin-bottom: 14px; }
.stack__value { display: block; margin-bottom: 4px; }
/* a link value keeps the stacking but shrinks to its text, so the
   underline ends where the words do */
a.stack__value { width: fit-content; }

/* ============================================================
   BANNER — a still across the top of /work, cropped to a band.
   Sits inside the page padding on desktop like the rest of the
   media, full-bleed on phone.
   ============================================================ */
.banner { margin: 0 var(--pad) 72px; }
.banner__frame {
  display: block;
  height: min(62vh, 620px);
  background: var(--frame);
  overflow: hidden;
}
.banner__frame img {
  width: 100%; height: 100%;
  object-fit: cover;
  display: block;
}
.banner__cap { margin-top: 0; border-top: 0; }

/* ============================================================
   SECTIONS — Gregory's fact-section system. Every section takes a
   full line across the viewport (the divider), generous block
   padding, and opens with a numbered head: the number in its own
   narrow column, the caps label beside it. One section per page may
   flip black — a banner moment, smaller than a screen. Tokens are
   redefined on the band (footer pattern), so everything inside
   inverts for free.
   ============================================================ */
.sect {
  /* pull past the page padding so the rule runs edge to edge,
     then re-inset the content */
  margin-inline: calc(-1 * var(--pad));
  padding: clamp(48px, 5.5vw, 76px) var(--pad) clamp(60px, 7vw, 104px);
  border-top: 1px solid var(--hairline);
}
.s-head {
  display: grid;
  grid-template-columns: 44px 1fr;
  margin-bottom: clamp(28px, 3.5vw, 44px);
}
.sect--dark {
  /* literal, not var(--ink) — the token is redefined on this same
     element, so a var() here would resolve to the new value and
     paint the band white (see .footer__content) */
  background: #000;
  color: #fff;
  border-top: 0;                     /* the edge is the divider */
  --hairline: rgba(255, 255, 255, 0.22);
  --grey: rgba(255, 255, 255, 0.5);
  --ink: #fff;
  --paper: #000;
}
/* a white section straight after the dark band needs no rule —
   the band's edge already divides them */
.sect--dark + .sect { border-top: 0; }

/* ============================================================
   SELECTED WORK — the compact ruled list, folded into the home
   page between the stats and the clients. List type; the
   hairlines carry the structure.
   ============================================================ */
.ix-row {
  display: grid;
  grid-template-columns: 40px 1fr 240px 44px;
  gap: 12px;
  align-items: baseline;
  padding: 11px 0;
  border-top: 1px solid var(--hairline);
}
.ix-row:last-child { border-bottom: 1px solid var(--hairline); }
.ix-row__year { text-align: right; }
/* The row you are on fills solid black and inverts, the way the
   recognition table on gregoryconstantine.com does it. The fill bleeds
   past the text on both sides so it reads as a bar, not a highlight. */
.ix-row { position: relative; z-index: 0; transition: color 0.2s var(--ease); }
.ix-row::before {
  content: "";
  position: absolute;
  left: -10px; right: -10px; top: -1px; bottom: 0;
  z-index: -1;
  background: var(--ink);
  opacity: 0;
  transition: opacity 0.2s var(--ease);
}
.ix-row:hover::before { opacity: 1; }
.ix-row:hover, .ix-row:hover .t-meta { color: var(--paper); }
/* The divider cube was removed from Selected Work on request: six of them
   stacked down the centre of the list read as debris rather than as the
   house mark. It is now gone from the /work list and the index table too,
   so no row in any list carries one. The 5px mark survives only on the
   active view toggle; the player's 7px scrub handle is a control, not the
   same device. */
/* THE HOUSE LINK — caps, a snug rule 3px under the text, and a plus.
   Taken from Gregory's textLink (caps · border-bottom · 3px gap ·
   mark at the end), with his arrow swapped for a plus sign on
   request. currentColor everywhere, so it inverts on the black
   moments for free. */
.link-mini {
  display: inline-flex;
  align-items: baseline;
  gap: 8px;
  /* 10px: Gregory's actual textLink size, set on Gabe's explicit ask —
     the ONE exception to the 12.5px type floor. Weight 500 caps keeps
     it solid at this size. */
  font-size: 10px;
  font-weight: 500;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  padding-bottom: 3px;
  border-bottom: 1px solid currentColor;
  transition: color 0.2s var(--ease), opacity 0.2s var(--ease);
}
.link-mini::after { content: "+"; }
.link-mini:hover { color: var(--grey); }
.ix-all { margin-top: 24px; }

/* ============================================================
   VENTURES — the lead-out block (foot of / and /about)
   ============================================================ */
.vent { display: block; }
.vent__name { margin: 26px 0 0; transition: color 0.25s var(--ease); }
/* the descriptive line sits under the wordmark, with air above the rule */
.vent__line { margin: 12px 0 26px; }
/* .vent__desc removed in the furniture pass: the descriptor moved into
   .vent__line, so the wordmark carries no label of its own. */
.vent:hover .vent__name { color: var(--grey); }
.vent__row {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  padding: 9px 0;
  border-top: 1px solid var(--hairline);
}
/* Entries were flush at 0 while each carried 26px of internal air, so a
   record held more space inside it than there was between two companies
   and the two ran together. 44px is the site's existing block-separation
   number (.s-head's margin-bottom), so the gap between entries now
   matches the gap from the section head down to the first one. */
.vent + .vent { margin-top: 44px; }
/* Every venture closes with a rule, so both records are the same shape.
   It used to be a :last-child special case, which left the first entry
   open at the foot and the last one closed. On /companies the entries are
   each their own .sect with a section rule already bounding them, so the
   special case still applies there. */
.ventures .vent .vent__row { border-bottom: 1px solid var(--hairline); }
.studios .studio:last-child .vent__row { border-bottom: 1px solid var(--hairline); }

/* ============================================================
   PLAYER CHROME — Renell's text-only controls over the film.
   No icons, no player UI. White under mix-blend difference, so
   it inverts against whatever frame is behind it.
   ============================================================ */
.player { position: relative; display: block; }

/* The chrome is difference-blended white, and difference has ZERO contrast when the
   backdrop sits at mid grey — white against 128 returns 127. Dark frames read clean,
   the bright B&O elevator does not, and a light scrim lands it exactly in the dead
   zone and makes it worse. So the scrim has to be strong enough to carry the bottom
   strip near black, where difference returns near white again. Gradient, so the top
   edge is invisible and only the last few centimetres of frame are touched. */
.player::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 88px;
  z-index: 3;                       /* under .player__chrome, which is 4 */
  pointer-events: none;
  /* Held FLAT to 34px, then faded. The chrome's text row sits 10-23px off the
     bottom, and difference only reads when the backdrop is under ~70 or over
     ~185. A gradient that is still fading across the text row lands it in the
     128 dead zone, which is worse than no scrim. .82 over a white frame gives
     44, so the row is always in the readable band. */
  background: linear-gradient(to top,
              rgba(0,0,0,.82) 0px, rgba(0,0,0,.82) 34px, rgba(0,0,0,0) 100%);
}
.player__chrome {
  position: absolute;
  left: 12px; right: 12px; bottom: 10px;
  font-size: 12.5px;
  z-index: 4;                       /* stays usable over the description panel */
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  color: #fff;
  mix-blend-mode: difference;
}
.player__btns { display: flex; align-items: center; gap: 18px; }
.player__chrome button {
  all: unset;
  cursor: pointer;
  font-family: var(--font);
  font-size: 12.5px;
  font-weight: 500;
  min-width: 4.6em;                 /* Pause/Play + Unmute/Mute must not shift the row */
}
.player__chrome button[data-fs] { min-width: 0; }
.player__time { font-variant-numeric: tabular-nums; }
.player__bar {
  position: relative;
  width: min(32%, 400px);
  height: 12px;
  cursor: pointer;
  flex: none;
  touch-action: none;   /* the bar drags — don't let touch scroll eat it */
}
.player__bar::before {                /* the track */
  content: "";
  position: absolute; left: 0; right: 0; top: 50%;
  height: 1px; margin-top: -0.5px;
  background: rgba(255, 255, 255, 0.45);
}
.player__fill {                       /* elapsed */
  position: absolute; left: 0; top: 50%;
  width: 100%; height: 1px; margin-top: -0.5px;
  background: #fff;
  transform-origin: left center;
  transform: scaleX(0);
}
.player__bar::after {                 /* the handle */
  content: "";
  position: absolute; top: 50%;
  left: calc(var(--p, 0) * 100%);
  width: 7px; height: 7px;
  background: #fff;
  transform: translate(-50%, -50%);
}

/* ============================================================
   WORK — list (default) / screens / grid
   ============================================================ */
.works-main { padding-top: 96px; padding-bottom: 160px; }
.works-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  padding: 0 var(--pad);
  margin-bottom: 24px;
}
.works-head__left { display: flex; gap: 16px; }
.view-toggle { display: flex; gap: 20px; }
.view-toggle button {
  all: unset;
  cursor: pointer;
  font-family: var(--font);
  font-size: 13px; font-weight: 500;
  color: var(--grey);
}
.view-toggle button.is-active { color: var(--ink); }
.view-toggle button.is-active::before {
  content: "";
  display: inline-block;
  width: 5px; height: 5px;
  margin-right: 7px;
  vertical-align: middle;
  background: var(--ink);
}

.works-view { display: none; }
.works-view.is-active { display: block; }

/* screens — one work per viewport, corner-pinned metadata */
.works-screens .ws {
  position: relative;
  min-height: 100vh;                /* grows when a description opens */
  padding: 40px 0;
  display: flex;
  align-items: center;
  justify-content: center;
}
.ws__corner { position: absolute; }
.ws__corner--tl { top: 20px; left: var(--pad); }
.ws__corner--tr { top: 20px; right: var(--pad); }
/* title, the description toggle and the year sit directly under the
   film at its own width — the toggle next to what it opens, not
   stranded in a corner at the other end of the screen. */
.ws__row {
  display: flex;
  align-items: baseline;
  gap: 20px;
  padding-top: 12px;
}
.ws__link { flex: 1 1 auto; transition: color 0.25s var(--ease); }
.ws__link:hover { color: var(--grey); }
.ws__year { flex: none; }
.ws__media {
  display: block;
  width: 100%;
  aspect-ratio: 16 / 9;
  background: var(--frame);
  overflow: hidden;
  position: relative;
}
.ws__media video, .vimeo-slot iframe {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover; border: 0;
  pointer-events: none;
}
.vimeo-slot { position: relative; background: var(--frame); }
.vimeo-slot iframe { width: 100%; }
/* Grid cards keep a fixed 16:9 card, so the film overscans to fill it
   rather than pillarboxing — cropping reads better than grey bars on a
   thumbnail. The screens do the opposite: the frame takes the film's
   own shape, so nothing is cropped where the work is actually shown. */
.w-card__media.vimeo-slot iframe {
  width: 177.78%;
  left: 50%;
  transform: translateX(-50%);
}

/* list */
.works-list { padding: 0 var(--pad); }
.works-list .w-row {
  display: grid;
  grid-template-columns: 40px 1fr 110px 220px 44px;
  gap: 12px;
  align-items: baseline;
  padding: 11px 0;
  border-top: 1px solid var(--hairline);
}
.works-list .w-row:last-child { border-bottom: 1px solid var(--hairline); }
.w-row__title { font-size: 13px; font-weight: 500; transition: color 0.25s var(--ease); }
.w-row:hover .w-row__title { color: var(--grey); }
.w-row__year { text-align: right; }

/* grid */
.works-grid { padding: 0 var(--pad); }
.works-grid { grid-template-columns: repeat(2, 1fr); gap: 12px; }
.works-view.works-grid.is-active { display: grid; }
.w-card { display: block; margin-bottom: 28px; }
.w-card__media {
  display: block;
  aspect-ratio: 16 / 9;
  background: var(--frame);
  overflow: hidden;
  position: relative;
}
.w-card__media video {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  pointer-events: none;
}
.w-card__label { display: flex; justify-content: space-between; align-items: baseline; padding-top: 8px; }

/* ============================================================
   WORK PAGE — the ruled document
   ============================================================ */
.doc { padding: 110px var(--pad) 120px; }
.m-row--page { margin-bottom: 32px; }
.doc__media { margin: 0 auto; width: min(100%, calc(80vh * 16 / 9)); }
.media-frame {
  display: block;
  aspect-ratio: 16 / 9;
  background: var(--frame);
  overflow: hidden;
  position: relative;
}
.media-frame iframe {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  border: 0;
}
/* the one place a title runs large — Gregory's tight work-page heading */
.doc__title {
  font-size: clamp(26px, 2.7vw, 34px);
  line-height: 1.0;
  letter-spacing: -0.035em;
  margin: 32px 0 48px;
}
.doc__label { margin-bottom: 18px; }
.doc__section { border-top: 1px solid var(--hairline); padding: 11px 0 48px; }
.doc__lead { max-width: 46rem; }
.doc__stacks {
  display: grid;
  grid-template-columns: 3fr 4fr 3fr;
  gap: 12px;
}
.doc__others .o-row {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 16px;
  padding: 11px 0;
  border-top: 1px solid var(--hairline);
  transition: color 0.25s var(--ease);
}
.doc__others .o-row:last-child { border-bottom: 1px solid var(--hairline); }
.doc__others .o-row:hover .o-row__title { color: var(--grey); }
.o-row__title { transition: color 0.25s var(--ease); }

/* ============================================================
   ABOUT — enormous first paragraph, stacks, ventures
   ============================================================ */
.about { padding: 130px var(--pad) 0; }
.about-bio { padding-bottom: clamp(72px, 8vw, 120px); }
/* the bio set larger than body — the page's one piece of scale */
.about-bio__lead {
  display: block;
  font-size: clamp(22px, 2.3vw, 30px);
  line-height: 1.14;
  letter-spacing: -0.03em;
  max-width: 34ch;
}
/* Expands rather than appears. display:none cannot be transitioned, so
   the height comes from a grid row animating 0fr -> 1fr, which gets a
   real auto-height expansion without measuring anything in JS. The
   inner wrapper is what does the clipping. */
.about-rest {
  display: grid;
  grid-template-rows: 0fr;
  max-width: 46rem;
  transition: grid-template-rows 0.55s var(--ease);
}
.about-rest.is-open { grid-template-rows: 1fr; }
.about-rest__inner {
  overflow: hidden;
  opacity: 0;
  transition: opacity 0.3s var(--ease);
}
.about-rest.is-open .about-rest__inner {
  opacity: 1;
  transition-delay: 0.12s;
}
.about-rest__inner > :first-child { padding-top: 40px; }

@media (prefers-reduced-motion: reduce) {
  .about-rest, .about-rest__inner { transition: none; }
}
.about-rest__p { margin-bottom: 16px; }
.more {
  all: unset;
  cursor: pointer;
  display: block;
  margin-top: 28px;
  font-family: var(--font);
  font-size: 13px; font-weight: 500;
  color: var(--grey);
  transition: color 0.25s var(--ease);
}
.more:hover { color: var(--ink); }
.about-stats {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 12px;
}
.clients-list li { padding: 2px 0; transition: color 0.25s var(--ease); }
.clients-list:hover li { color: var(--grey); }
.clients-list li:hover { color: var(--ink); }

/* ============================================================
   STUDIOS — the lead-out page. Two ruled entries.
   ============================================================ */
.studios { padding: 130px var(--pad) 0; }
.studio__name { margin: 26px 0 0; }
.studio__about { max-width: 46rem; margin-bottom: 40px; }
.studio__relation { margin: -28px 0 40px; }

/* ============================================================
   CONTACT
   ============================================================ */
.contact { min-height: 100vh; padding: 0 var(--pad) 140px; }
.contact-grid {
  padding-top: 55vh;
  display: grid;
  grid-template-columns: repeat(3, 1fr);   /* three stacks since the second address went */
  gap: 12px;
}
.contact .stack__value { max-width: 242px; }

/* ============================================================
   PRIVACY
   ============================================================ */
.privacy { padding: 130px var(--pad) 140px; }
.privacy-title { display: block; border-bottom: 1px solid var(--hairline); padding-bottom: 9px; margin-bottom: 48px; }
.privacy-body { max-width: 560px; }
.privacy-body h2 { margin: 44px 0 14px; }
.privacy-body p { margin-bottom: 14px; }

/* ============================================================
   404
   ============================================================ */
.nf { min-height: 100vh; padding: 130px var(--pad) 0; }
.nf-code { margin: 10px 0 48px; }

/* ============================================================
   FOOTER — sticky video panel (index only), ruled columns
   ============================================================ */
.footer { position: relative; }
.footer__video-wrap {
  position: sticky;
  top: 0;
  height: 100vh;
  z-index: 1;
  overflow: hidden;
  background: var(--paper);
}
/* the banner is the idle state; the hover preview fades in over it,
   so both fill the panel and the video is stacked on top */
.footer__banner,
.footer__video-wrap video {
  position: absolute;
  inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  display: block;
}
.footer__banner { z-index: 1; }
.footer__video-wrap video {
  z-index: 2;
  opacity: 0;
  transition: opacity 0.5s var(--ease);
}
.footer__video-wrap video.is-on { opacity: 1; }
/* The footer is the site's other black moment. Everything inside it
   flips, so the hairlines and greys come from tokens rather than a
   parallel set of rules. */
.footer__content {
  position: relative;
  z-index: 3;
  /* literal, not var(--ink): the token is redefined below for
     descendants, and a var() on this element would resolve to the new
     value and paint the footer white. */
  background: #000;
  color: #fff;
  padding: 56px var(--pad) 20px;
  --hairline: rgba(255, 255, 255, 0.22);
  --grey: rgba(255, 255, 255, 0.5);
  --ink: #fff;
  --paper: #000;
}
.footer__grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 12px;
}
.footer__col { border-top: 1px solid var(--hairline); padding-top: 11px; }
.footer__col > .t-meta { margin-bottom: 14px; }
.footer__col ul li { margin-bottom: 2px; }
.footer__col a, .footer__col address, .footer__col p { font-size: 13px; font-weight: 400; }
.footer__bottom a, .footer__bottom p { font-size: 12.5px; font-weight: 500;
  letter-spacing: 0.012em; text-transform: uppercase; }
.footer__col address { max-width: 200px; }
.footer__col [data-clock-short] { margin-top: 7px; }
/* name left, socials centred, copyright right — with a long stretch of
   black above it, which is what makes it read as a moment rather than
   a strip */
.footer__bottom {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: baseline;
  gap: 16px;
  margin-top: 180px;
}
.footer__bottom .socials { display: flex; gap: 28px; justify-content: center; }
.footer__bottom .socials a,
.footer__mark { transition: color 0.2s var(--ease); }
.footer__bottom .socials a:hover, .footer__mark:hover { color: rgba(255, 255, 255, 0.55); }
.footer__copy { text-align: right; color: rgba(255, 255, 255, 0.5); }

/* ============================================================
   RESPONSIVE — 810 / 1200. The same layout at a different
   width: corners stay corners, media goes full-bleed on phone.
   ============================================================ */
@media (min-width: 1200px) {
  .works-grid { grid-template-columns: repeat(3, 1fr); }
}

@media (max-width: 809px) {
  /* nav wraps to two rows but keeps its corners */
  .nav { flex-wrap: wrap; row-gap: 6px; }
  .nav__logo { order: 0; flex: 0 0 50%; }
  .nav__clock { order: 1; flex: 0 0 50%; text-align: right; }
  .nav__item { order: 2; }

  .banner { margin: 0 calc(-1 * var(--pad)) 64px; }
  .banner__frame { height: 46vh; }
  .ix-row { grid-template-columns: 34px 1fr 44px; }
  .ix-row__client { display: none; }

  /* work screens: full-bleed media, title/year pair underneath */
  .works-screens .ws {
    height: auto;
    display: grid;
    grid-template-columns: 1fr auto;
    gap: 10px 16px;
    padding: 36px 0;
  }
  .ws__corner { position: static; inset: auto; }
  .ws__corner--tl { grid-row: 1; grid-column: 1; padding-left: var(--pad); }
  .ws__corner--tr { grid-row: 1; grid-column: 2; padding-right: var(--pad); text-align: right; }
  .ws__stage { grid-row: 2; grid-column: 1 / -1; width: 100%; }
  .ws__media { width: 100%; }
  /* the row is inside the stage now, so it needs the page padding back */
  .ws__row { padding: 12px var(--pad) 0; gap: 14px; }
  .ws__link { position: relative; }
  /* not enough width for one row: buttons, then the bar beneath */
  .player__chrome { left: var(--pad); right: var(--pad); bottom: 9px; flex-wrap: wrap; gap: 8px; }
  .player__btns { flex: 1 1 100%; justify-content: space-between; gap: 10px; }
  .player__chrome button { min-width: 0; }
  .player__bar { flex: 1 1 100%; width: auto; height: 10px; }

  .works-list .w-row { grid-template-columns: 40px 1fr 44px; }
  .w-row__campaign, .w-row__client { display: none; }
  .works-grid { grid-template-columns: 1fr; }

  /* work page: full-bleed film */
  .doc__media { width: auto; margin: 0 calc(-1 * var(--pad)); }
  .doc__stacks { grid-template-columns: 1fr; gap: 0; }
  .doc__title { margin: 32px 0 48px; }

  .about-bio__lead { max-width: none; }   /* edge to edge on phone, as Renell */
  .about-stats { grid-template-columns: 1fr 1fr; row-gap: 32px; }

  .contact-grid { padding-top: 40vh; grid-template-columns: 1fr 1fr; row-gap: 44px; }

  .ventures { margin-top: 110px; padding-bottom: 110px; }

  .footer__grid { grid-template-columns: 1fr 1fr; row-gap: 28px; }
  .footer__bottom {
    grid-template-columns: 1fr;
    justify-items: start;
    gap: 14px;
    margin-top: 96px;
  }
  .footer__bottom .socials { justify-content: flex-start; gap: 20px; }
  .footer__copy { text-align: left; }
}

/* ============================================================
   INDEX OVERLAY — the whole catalogue as one ruled table.
   The site is white; the index is its inverse. Same document,
   two states.
   ============================================================ */
.ixo {
  position: fixed;
  inset: 0;
  z-index: 200;
  background: var(--ink);
  color: var(--paper);
  display: flex;
  flex-direction: column;
  /* inside the black, the fill flips: rows go white and invert */
  --fill: #fff;
  --on-fill: #000;
  --rule: rgba(255, 255, 255, 0.4);
}
.ixo[hidden] { display: none; }
/* the screen fades up rather than slamming: [hidden] comes off first,
   then .is-open lands a frame later and the transition runs. Closing
   reverses it — the class comes off, and [hidden] returns on
   transitionend (see site.js). */
.ixo {
  opacity: 0;
  transition: opacity 0.35s var(--ease);
}

/* ============================================================
   MENU OVERLAY — phone navigation (2026-08-24)

   Built first at display scale, which Gabe rejected: it did not read as
   this site. It is the same object as the index — a bar, then a numbered
   ruled list you tap — so it takes the index's treatment exactly: 15px
   rows, 46px number column, 10px padding, the 0.18 white rule, and the
   same fill-behind-the-row device. Anything larger is a different site.
   ============================================================ */
.mno {
  position: fixed;
  inset: 0;
  z-index: 210;
  background: var(--ink);
  color: var(--paper);
  display: flex;
  flex-direction: column;
  /* inside the black the fill flips, exactly as .ixo does */
  --fill: #fff;
  --on-fill: #000;
  --rule: rgba(255, 255, 255, 0.4);
  opacity: 0;
  transition: opacity 0.3s var(--ease);
}
.mno.is-open { opacity: 1; }
.mno[hidden] { display: none; }
.mno__bar {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  padding: 11px var(--pad) 0;
  flex: none;
  color: rgba(255, 255, 255, 0.55);
}
.mno__bar .nav__btn { color: rgba(255, 255, 255, 0.55); font-size: 12.5px; }
.mno__list {
  display: flex;
  flex-direction: column;
  padding: 40px var(--pad) 0;
}
.mno__row {
  all: unset;
  cursor: pointer;
  display: grid;
  grid-template-columns: 46px minmax(0, 1fr);
  gap: 16px;
  align-items: baseline;
  padding: 10px 0;
  border-top: 1px solid rgba(255, 255, 255, 0.18);
  color: var(--paper);
  /* the fill sits behind the row, so the row needs its own context */
  position: relative;
  z-index: 0;
  transition: color 0.2s var(--ease);
}
.mno__row:last-child { border-bottom: 1px solid rgba(255, 255, 255, 0.18); }
.mno__row::before {
  content: "";
  position: absolute;
  left: -10px; right: -10px; top: -1px; bottom: 0;
  z-index: -1;
  background: var(--fill);
  opacity: 0;
  transition: opacity 0.2s var(--ease);
}
.mno__row:active { color: var(--on-fill); }
.mno__row:active::before { opacity: 1; }
.mno__t { font-size: 15px; font-weight: 500; letter-spacing: 0; line-height: 1.35; }
body.mno-open { overflow: hidden; }

/* index open: lock the page behind it */
body.ixo-open { overflow: hidden; }

/* ============================================================
   WORK DESCRIPTION — a band under the film, not a panel over it.
   Always open on every screen; there is no toggle, so no grid
   expansion and nothing for JS to measure.
   ============================================================ */
.ws__stage {
  position: relative;
  display: block;
  width: min(64vw, calc(70vh * 16 / 9));   /* the band matches the film */
}
.wd { display: block; }
.wd__clip { overflow: hidden; }
.wd__inner {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) minmax(0, 1.2fr) minmax(0, 1.6fr);
  gap: 24px;
  padding-top: 13px;
  margin-top: 12px;
  border-top: 1px solid var(--hairline);
}
.wd__col { display: block; }
.wd__k { display: block; margin-bottom: 8px; }
.wd__v { display: block; font-size: 13px; line-height: 1.5; }

/* ============================================================
   RECOGNITION — ruled award table, year printed once per run
   ============================================================ */
.rec__row {
  display: grid;
  grid-template-columns: 120px minmax(0, 1fr) minmax(0, 1fr) minmax(0, 1fr);
  gap: 16px;
  align-items: baseline;
  padding: 13px 0;
  border-top: 1px solid var(--hairline);
}
.rec__row:last-child { border-bottom: 1px solid var(--hairline); }
.rec__b { font-size: 15px; }

@media (max-width: 809px) {
  /* grid, not wrapped flex: flex decides wrapping from basis, which
     cannot express "name and clock alone on row one". Three columns
     does, and every cell keeps its own bracketed rule. */
  .nav { display: grid; grid-template-columns: repeat(2, 1fr); gap: 0 10px; }
  .nav__cell::before { display: block; }
  .nav__cell--logo { grid-column: 1; grid-row: 1; }
  .nav__cell--clock { grid-column: 2; grid-row: 1; }
  .nav__cell { min-width: 0; }
  .ixo__scroll { padding-top: 40px; }
  /* three columns only, each placed explicitly so nothing wraps to a
     second line: number, project, year. The whole row is the link. */
  .ixo__row { grid-template-columns: 38px minmax(0, 1fr) 52px; gap: 10px; }
  .ixo__c, .ixo__t, .ixo__v { display: none; }
  /* row pinned too: sparse auto-placement will not backtrack to an
     earlier column once the cursor has passed it. */
  .ixo__i { grid-column: 1; grid-row: 1; }
  .ixo__p { grid-column: 2; grid-row: 1; }
  .ixo__y { grid-column: 3; grid-row: 1; text-align: right; }


  .rec__row { grid-template-columns: 1fr 1fr; row-gap: 4px; }
  .rec__y { grid-column: 1 / -1; }
}

/* ============================================================
   PHONE — RULE DIET (2026-08-24, Gabe: "too many lines, feels messy")

   Measured on the live site at 375: /companies drew 20 horizontal rules
   and the home page 32. Three of them were redundant rather than
   structural, and all three only bite on phone, where everything is
   stacked and the rules land on top of each other.
   ============================================================ */
@media (max-width: 809px) {

  /* 1 · The nav was SIX hairlines in the top 57px — one bracketing every
     cell. They were added because the wrapped grid needed cells separated,
     but the 2-column grid already separates them by row and by column gap.
     Six lines before a word of content is what read as mess. */
  .nav__cell::before { display: none; }

  /* 2 · A section headed by a numbered .m-row drew TWO full-width rules
     ~100px apart with nothing between them: .sect's own border, then the
     row's. The .m-row is the one carrying content ("01 · SINCE 2023"), so
     it keeps its rule and .sect gives up its own. Desktop has the width to
     read both as structure; a phone does not. */
  .sect:has(.m-row) { border-top: 0; }

  /* 3 · Each venture closed with its link underline and the row's bottom
     border TEN pixels apart, which reads as one thick double line rather
     than two edges. The underline is the link's own affordance and stays;
     the row border goes. */
  .ventures .vent .vent__row,
  .studios .studio:last-child .vent__row { border-bottom: 0; }

  /* 4 · ONE ROW, after Renell (Gabe, 2026-08-24). The wrapped grid put
     three rows and 86px of fixed, transparent nav permanently on top of
     the page, and on a dense list — the home page's Clients column — it
     landed straight on the words. Name, clock, Menu is one 40px row, and
     the destinations move into the overlay where they get real size. */
  .nav {
    display: flex;
    flex-wrap: nowrap;
    align-items: baseline;
    gap: 14px;
    row-gap: 0;
  }
  .nav__cell--link { display: none; }
  .nav__cell--menu { display: block; flex: none; }
  .nav__cell--clock { margin-left: auto; flex: none; }
  .nav__cell--logo { flex: none; }
}

/* On a phone the band has no width to spread across, so the columns
   stack and it reads as a short document under the film. */
@media (max-width: 809px) {
  /* .ws__stage's base width is declared after the earlier mobile block,
     so it wins there; the full-bleed override has to live down here. */
  .ws__stage { width: 100%; }
  .wd__inner {
    grid-template-columns: 1fr;
    gap: 16px;
    margin: 13px var(--pad) 0;
    padding-top: 13px;
  }
}
