/* ============================================================
   Studio Areal — prototype
   Three full-screen takeovers stacked with sticky scroll.
   ============================================================ */

:root {
  /* Palette — four colours, everything else is drawn from these. */
  --orange: #fc5c1e;
  --off-black: #242424;
  --gray: #e9e8e4;   /* Connect's ground */
  --white: #ffffff;

  --c-one: var(--orange);      /* panel 1 */
  --c-two: var(--off-black);   /* panel 2 */
  --c-three: var(--gray);      /* panel 3 */
  --statement: var(--off-black);

  --ease: cubic-bezier(0.22, 1, 0.36, 1);

  --pad: clamp(1.25rem, 4vw, 3.5rem);
  --nav-h: 4.5rem;
}

/* Ink for the fixed chrome, keyed to whichever panel is under it.
   JS stamps data-nav on the root as you scroll. */
:root[data-nav="0"]   { --nav-ink: var(--off-black); }
:root[data-nav="1"]   { --nav-ink: var(--white); }
:root[data-nav="2"]   { --nav-ink: var(--orange); }

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

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

body {
  margin: 0;
  background: var(--c-one);
  color: var(--white);
  font-family: "PT Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
  overflow-x: hidden;
}

/* ── Sticky stack ─────────────────────────────────────────────
   Each panel sticks to the top of the viewport; the next one
   scrolls up and covers it. Total scroll length = panels × 100vh.
   ------------------------------------------------------------ */
.stack {
  display: block;
}

.panel {
  position: sticky;
  top: 0;
  height: 100svh;
  min-height: 100svh;
  display: grid;
  place-items: center;
  padding: var(--pad);
  overflow: hidden;
  /* set per-panel below; rises as you go down the page */
  z-index: 1;
  will-change: transform;
}

.panel--one {
  background: var(--c-one);
  color: var(--white);
  z-index: 1;
  padding: 0;
}

.panel--one .panel__inner {
  position: absolute;
  inset: 0;
  width: auto;
}
.panel--two   { background: var(--c-two);   color: var(--white);     z-index: 2; }
.panel--three {
  background: var(--c-three);
  color: var(--off-black);
  z-index: 3;
  /* A section rather than a single screen now: it grows with its
     content and the bands inside it run full width. */
  position: relative;
  display: block;
  height: auto;
  min-height: 100svh;
  padding: 0;
  /* The base rule's place-items is for the grid this no longer is. Modern
     Chromium applies justify-items to block containers as well, which
     shrink-wraps and centres every child — including the full-width band. */
  place-items: normal;
}

/* Inner wrapper is what we animate on scroll (panel itself is sticky). */
.panel__inner {
  width: min(100%, 84rem);
  text-align: center;
  transform: translate3d(0, calc(var(--leave, 0) * -6vh), 0)
             scale(calc(1 - var(--leave, 0) * 0.08));
  opacity: calc(1 - var(--leave, 0) * 0.85);
  will-change: transform, opacity;
}

/* ── Panel 1 — morphing wordmark ──────────────────────────────
   The takeover is a 12-cell grid filling the whole viewport, one
   letter per cell. The grid reshapes at each breakpoint; every
   letter is an SVG scaled to fill its cell without cropping, so
   the type size follows the cells rather than the other way round.
   ------------------------------------------------------------ */
.morph {
  --cols: 2;
  --rows: 6;
  /* Percentage padding resolves against the cell's own width, so the gutter
     is always proportional to the container. The percentage itself shrinks
     with the viewport, so small screens give the letters nearly all of their
     cell and large ones can afford the air. */
  --cell-pad: 2.5%;

  position: absolute;
  inset: 0;
  margin: 0;
  display: grid;
  grid-template-columns: repeat(var(--cols), 1fr);
  grid-template-rows: repeat(var(--rows), 1fr);
  font-weight: 400;
}

/* 3 × 4 */
@media (min-width: 30em) {
  .morph { --cols: 3; --rows: 4; --cell-pad: 4%; }
}

/* 4 × 3, and it stays 4 × 3 from here up: however large the screen gets,
   the words fold and squash across the full viewport rather than
   straightening out into two long lines. */
@media (min-width: 48em) {
  .morph {
    --cols: 4;
    --rows: 3;
    --cell-pad: 6%;
  }
}

.morph__cell {
  position: relative;
  display: block;
  min-width: 0;
  min-height: 0;
  padding: var(--cell-pad);
}

/* The glyph fills the cell's content box; the SVG viewBox is the
   character's ink box, set in JS, so `meet` scales it as large as it
   fits and centres it. */
.morph__glyph {
  display: block;
  width: 100%;
  height: 100%;
  overflow: visible;
}

/* Hide the wordmark until the faces are measured — but only when JS is
   running, so the plain <h1> still shows if it never boots. */
.js .morph:not(.is-ready) { visibility: hidden; }

/* ── Grain ────────────────────────────────────────────────────
   Film grain over the letters only: the statement, the arrow and
   the bar all sit above these layers and stay crisp.

   Two layers rather than one, because real stock has both — a fine
   grain that boils every frame, and a coarser, slower mottle. Each
   noise field is one feTurbulence tile baked into an image, so the
   browser rasterises it once instead of running a live filter on a
   surface that repaints ten times a second. Movement is a hard step
   between offsets: grain jumps, it doesn't slide.
   ------------------------------------------------------------ */
/* One layer, not two: a blended surface costs roughly 20fps here, and
   the area matters as much as the pixels — hence only a little larger
   than the panel, with the drift kept small enough to stay covered. */
.grain {
  position: absolute;
  inset: -6%;
  z-index: 1;
  pointer-events: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='360' height='360'%3E%3Cfilter id='c'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.26' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Cfilter id='f'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='360' height='360' filter='url(%23c)' opacity='0.45'/%3E%3Crect width='360' height='360' filter='url(%23f)' opacity='0.85'/%3E%3C/svg%3E");
  background-repeat: repeat;
  opacity: 0.55;
  mix-blend-mode: overlay;
  animation: grain 720ms steps(1, end) infinite;
  will-change: transform;
}

@keyframes grain {
  0%   { transform: translate(0, 0); }
  12%  { transform: translate(-2%, -3%); }
  25%  { transform: translate(2%, -2%); }
  37%  { transform: translate(-3%, 2%); }
  50%  { transform: translate(2%, 3%); }
  62%  { transform: translate(-2%, 2%); }
  75%  { transform: translate(3%, -2%); }
  87%  { transform: translate(-2%, -2%); }
  100% { transform: translate(0, 0); }
}

/* Softness on the letters so the edges aren't digital-clean, and a gate
   that breathes. A gate weave (a sub-pixel drift of the whole frame) was
   here too, but moving a filtered layer under a blended one forces the
   grain to recomposite across the panel: the weave and the blur cost
   about 12fps each, and the blur is on every edge all the time. */
.morph {
  filter: blur(var(--letter-blur, 0.6px));
  animation: gate 5s steps(1, end) infinite;
}

@keyframes gate {
  0%, 100% { opacity: 1; }
  38%      { opacity: 0.955; }
  41%      { opacity: 1; }
  67%      { opacity: 0.97; }
  69%      { opacity: 0.998; }
  86%      { opacity: 0.962; }
}

/* ── Panel 1 — statement ──────────────────────────────────────
   Sits on top of the letter grid, centred in the viewport. The
   grid is the background layer; this is what you read.
   ------------------------------------------------------------ */
.statement {
  position: absolute;
  inset: 0;
  z-index: 2;
  margin: 0;
  display: grid;
  place-items: center;
  padding: var(--pad);
  color: var(--statement);
  text-align: center;
  text-wrap: balance;
  /* Placeholder face — to be decided. */
  font-family: "PT Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 1.75rem; /* one size everywhere; the line folds instead */
  line-height: 1.3;
  letter-spacing: -0.01em;
}


/* ── Panels 2 & 3 — placeholder content ───────────────────── */
.eyebrow {
  margin: 0 0 1.5rem;
  font-size: 0.75rem;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  opacity: 0.6;
}

.panel__title {
  margin: 0 0 1.25rem;
  font-family: "Martian Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
  font-weight: 400;
  font-size: clamp(2.75rem, 11vw, 9rem);
  line-height: 0.95;
  letter-spacing: -0.01em;
}

.panel__lede {
  margin: 0 auto;
  max-width: 42ch;
  font-size: clamp(0.875rem, 1.4vw, 1.0625rem);
  line-height: 1.6;
  opacity: 0.72;
}


/* ── Films — poster grid ─────────────────────────────────────
   Stacked and full width up to 62em, where two 2:3 posters can't
   both be large in one screen — so below that the section grows
   past the viewport and scrolls, and the posters take the width.
   From 62em the pair sits side by side, sized off the panel's
   height with a buffer that grows with the screen.
   ------------------------------------------------------------ */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.panel--two {
  /* Never less than the bar's own height, so nothing tucks under it. */
  --buffer: max(calc(var(--nav-h) + 1rem), clamp(2rem, 9vh, 6rem));
  --gap: clamp(1.75rem, 6vh, 3.5rem);
  --meta: 2.5rem; /* the caption under each poster counts against the height */
  padding-block: 0;
}

.panel--two .panel__inner {
  height: 100%;
  display: grid;
  align-items: center;
}

.films {
  margin: 0;
  padding: 0;
  list-style: none;
  display: grid;
  grid-template-columns: minmax(0, min(100%, 34rem));
  justify-content: center;
  /* Posters keep their own proportions, so rest them on a common bottom
     edge — otherwise the captions sit at two different heights. */
  align-items: end;
  gap: var(--gap);
}

/* Stacked: taller than the viewport, so this panel scrolls rather
   than pinning — otherwise its lower half could never be reached. */
@media (max-width: 61.99em) {
  .panel--two {
    position: relative;
    height: auto;
    min-height: 100svh;
    /* Only the top has a bar to clear. */
    padding-block: var(--buffer) clamp(1.5rem, 4vh, 3rem);
  }

  .panel--two .panel__inner { height: auto; }
}

@media (min-width: 62em) {
  .films {
    --poster-h: calc(100svh - 2 * var(--buffer) - var(--meta));
    --poster-w: calc(var(--poster-h) * 2 / 3);
    grid-template-columns: repeat(2, minmax(0, var(--poster-w)));
  }
}

.film {
  display: block;
  color: inherit;
  text-decoration: none;
}

.film__poster {
  position: relative;
  display: flex;
  align-items: flex-end;
  width: 100%;
  overflow: hidden;
  background: var(--gray);
  color: var(--off-black);
  transition: transform 450ms var(--ease);
}

/* Posters keep their own proportions — these are designed sheets, and
   cropping them to a house ratio would cut into the artwork. The 2:3
   frame is only the shape of the placeholder. */
.film__poster.is-empty { aspect-ratio: 2 / 3; }

.film__poster--cascade { background: var(--orange); }

.film__poster img {
  display: block;
  width: 100%;
  height: auto;
}

/* Shown only where the artwork is missing — JS marks the frame. */
.film__fallback {
  display: none;
  padding: clamp(0.75rem, 1.6vw, 1.5rem);
  font-size: clamp(1rem, 1.9vw, 1.6rem);
  line-height: 1.1;
}

.film__poster.is-empty .film__fallback { display: block; }

.film:hover .film__poster,
.film:focus-visible .film__poster { transform: translateY(-0.5rem); }

.film__meta {
  display: block;
  margin-top: 0.85rem;
  text-align: left; /* the panel centres its content; posters read left */
}

.film__name {
  display: block;
  font-size: clamp(0.9rem, 1.4vw, 1.0625rem);
  line-height: 1.3;
}

.film__note {
  display: block;
  margin-top: 0.35rem;
  font-size: 0.75rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  opacity: 0.55;
}


/* ── Button ───────────────────────────────────────────────────
   Square, like everything else here. Off-black on orange rather
   than white on orange, which is too thin a contrast to read at
   this size.
   ------------------------------------------------------------ */
.button {
  display: inline-block;
  padding: 1rem 1.75rem;
  background: var(--orange);
  color: var(--white);
  font-size: 0.8125rem;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  text-decoration: none;
  transition:
    background 300ms var(--ease),
    color 300ms var(--ease),
    transform 300ms var(--ease);
}

.button:hover,
.button:focus-visible {
  background: var(--off-black);
  color: var(--white);
  transform: translateY(-2px);
}

/* ── Connect ──────────────────────────────────────────────────
   Banded rather than boxed: a lead, two columns, one slab of
   orange for the thing we actually want from a reader, then the
   address. The asterisk from the landing marks each section.
   ------------------------------------------------------------ */
.connect {
  padding-top: calc(var(--nav-h) + clamp(1rem, 5vh, 3.5rem));
}

.connect__inner {
  width: min(100%, 68rem);
  margin: 0 auto;
  padding-inline: var(--pad);
}

.connect__title {
  margin: 0 0 clamp(1.5rem, 4vh, 2.5rem);
  font-family: "Martian Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
  font-weight: 400;
  font-size: clamp(1.75rem, 4.6vw, 3.5rem);
  line-height: 1.1;
  letter-spacing: -0.02em;
}

.connect__intro {
  margin: 0;
  max-width: 34ch;
  font-size: clamp(1.05rem, 2vw, 1.4rem);
  line-height: 1.45;
}

/* The intro keeps the size it had when it led the section, rather than
   dropping to the body size of the column it now sits in. */
.connect__cols--top .connect__col p.connect__intro {
  font-size: clamp(1.05rem, 2vw, 1.4rem);
  line-height: 1.45;
  max-width: 30ch;
}

.connect__label {
  margin: 0 0 1.25rem;
  font-size: 0.75rem;
  font-weight: 400;
  letter-spacing: 0.2em;
  text-transform: uppercase;
}

.connect__cols--top { margin-top: clamp(1.25rem, 3vh, 2.25rem); }

.connect__cols {
  margin-top: clamp(1.75rem, 4vh, 3rem);
  display: grid;
  gap: clamp(2.5rem, 6vw, 5rem);
}

.connect__block + .connect__block { margin-top: clamp(2.25rem, 5vh, 3.5rem); }

@media (min-width: 48em) {
  .connect__cols { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

.connect__block--letter p { font-style: italic; }

/* The signature is a name, not part of the letter's voice. */
.connect__block--letter p:has(b),
.connect__block--letter b { font-style: normal; }

.connect__col p {
  margin: 0 0 1rem;
  max-width: 44ch;
  font-size: 0.95rem;
  line-height: 1.7;
}

.connect__col em { font-style: italic; }

.connect__col p a {
  color: inherit;
  border-bottom: 1px solid currentColor;
  text-decoration: none;
  transition: color 250ms var(--ease);
}

.connect__col p a:hover,
.connect__col p a:focus-visible { color: var(--orange); }

.connect__portrait {
  position: relative;
  display: flex;
  align-items: flex-end;
  width: min(100%, 14rem);
  margin-bottom: 1.5rem;
  overflow: hidden;
  background: var(--off-black);
  color: var(--gray);
}

.connect__portrait.is-empty { aspect-ratio: 4 / 5; }

.connect__portrait img {
  display: block;
  width: 100%;
  height: auto;
}

.connect__portrait-fallback {
  display: none;
  padding: 0.85rem;
  font-size: 0.8rem;
}

.connect__portrait.is-empty .connect__portrait-fallback { display: block; }

.connect__contact {
  padding-block: clamp(3rem, 8vh, 5.5rem) clamp(2rem, 6vh, 4rem);
}

.connect__email {
  color: inherit;
  text-decoration: none;
  border-bottom: 1px solid var(--orange);
  transition: color 300ms var(--ease);
}

.connect__email:hover,
.connect__email:focus-visible {
  color: var(--orange);
}

/* ── Lightbox ─────────────────────────────────────────────────
   A black curtain, the image, and the way out.
   ------------------------------------------------------------ */
body.is-boxed { overflow: hidden; }

.lightbox {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: clamp(0.5rem, 2vw, 2rem);
  padding: clamp(3.5rem, 8vh, 5rem) clamp(0.75rem, 3vw, 3rem);
  background: rgba(10, 10, 10, 0.94);
  color: var(--white);
}

.lightbox[hidden] { display: none; }

.lightbox__frame {
  margin: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.85rem;
  min-width: 0;
}

.lightbox__frame img {
  display: block;
  max-width: 100%;
  max-height: calc(100svh - 9rem);
  width: auto;
  height: auto;
  object-fit: contain;
}

.lightbox figcaption {
  font-size: 0.7rem;
  letter-spacing: 0.2em;
  opacity: 0.6;
}

.lightbox button {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  cursor: pointer;
  opacity: 0.75;
  transition: opacity 200ms var(--ease);
}

.lightbox button:hover,
.lightbox button:focus-visible { opacity: 1; }

.lightbox button[hidden] { display: none; }

.lightbox__close {
  position: absolute;
  top: clamp(1rem, 3vh, 2rem);
  right: clamp(1rem, 3vw, 2rem);
  width: 2.75rem;
  height: 2.75rem;
}

.lightbox__step { width: 2.75rem; height: 2.75rem; }

.lightbox svg { width: 1.6rem; height: 1.6rem; }

/* ── Global footer ────────────────────────────────────────────
   Sits at the foot of Connect and of every film page. The rule and
   the text share one box, so the ends of the line and the ends of
   the text meet.
   ------------------------------------------------------------ */
.site-footer__inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem 1.5rem;
  padding-top: 1rem;
  margin-top: 2rem;
  border-top: 1px solid currentColor;
  font-size: 0.75rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

.site-footer__contact {
  display: flex;
  align-items: center;
  gap: 1rem;
}

/* .site-footer a sets an underline and beats a bare class on specificity,
   so this has to match at least as tightly. */
.site-footer a.site-footer__social {
  display: block;
  border: 0;
  opacity: 0.75;
  transition: opacity 250ms var(--ease);
}

.site-footer a.site-footer__social:hover,
.site-footer a.site-footer__social:focus-visible { opacity: 1; }

.site-footer__social svg {
  display: block;
  width: 1.15rem;
  height: 1.15rem;
}

.panel--three .site-footer {
  width: min(100%, 68rem);
  margin: 0 auto;
  padding: 0 var(--pad) clamp(2rem, 6vh, 3.5rem);
}

.site-footer a {
  color: inherit;
  text-decoration: none;
  border-bottom: 1px solid currentColor;
}

/* ── Top bar ──────────────────────────────────────────────────
   Fixed over everything, no ground of its own. Its ink follows
   whichever panel is passing underneath.
   ------------------------------------------------------------ */
.nav {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 20;
  min-height: var(--nav-h);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 0 var(--pad);
  color: var(--nav-ink, var(--off-black));
  background: transparent;
  transition:
    color 350ms var(--ease),
    background-color 350ms var(--ease);
  /* Let clicks through everywhere except the controls themselves. */
  pointer-events: none;
}

.nav a,
.nav button { pointer-events: auto; }

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

/* The bar only takes a ground where it would otherwise sit on top of
   reading matter: over Connect on the home page, and everywhere on a
   film page. Over the letters it stays clear. */
:root[data-nav="2"] .nav,
.film-page .nav {
  background: var(--gray);
}

/* Wordmark — one random face per letter, rolled once on load. */
.nav__brand {
  font-size: 1.125rem;
  line-height: 1;
  white-space: nowrap;
}

.nav__brand span { display: inline-block; }

.nav__links {
  display: flex;
  align-items: center;
  gap: clamp(1.25rem, 3vw, 2.5rem);
  font-size: 1rem;
}

.nav__links a {
  padding: 0.25rem 0;
  border-bottom: 1px solid transparent;
}

.nav__links a:hover,
.nav__links a:focus-visible { border-bottom-color: currentColor; }

.nav__links a.is-active { border-bottom-color: currentColor; }

/* Hamburger — only below the breakpoint. */
.nav__toggle {
  display: none;
  width: 2.25rem;
  height: 2.25rem;
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  cursor: pointer;
}

.nav__toggle span {
  display: block;
  width: 1.5rem;
  height: 1px;
  margin: 0 auto;
  background: currentColor;
  transition: transform 250ms var(--ease), opacity 200ms linear;
}

.nav__toggle span + span { margin-top: 6px; }

.nav.is-open .nav__toggle span:first-child { transform: translateY(3.5px) rotate(45deg); }
.nav.is-open .nav__toggle span:last-child  { transform: translateY(-3.5px) rotate(-45deg); }

@media (max-width: 47.99em) {
  .nav { align-items: flex-start; padding-top: 1.25rem; }
  .nav__toggle { display: block; }

  .nav__links {
    position: absolute;
    top: var(--nav-h);
    /* Pulled out by its own padding, so the links still line up with the
       bar above them. */
    right: max(0px, calc(var(--pad) - 1rem));
    flex-direction: column;
    align-items: flex-end;
    gap: 0.75rem;
    padding: 1rem;
    /* Whatever ground the bar has, including none. */
    background: inherit;
    font-size: 1.25rem;
    opacity: 0;
    transform: translateY(-0.5rem);
    pointer-events: none;
    transition: opacity 250ms var(--ease), transform 250ms var(--ease);
  }

  .nav.is-open .nav__links {
    opacity: 1;
    transform: none;
    pointer-events: auto;
  }
}

/* ── Down arrow — landing only ────────────────────────────────
   Rests, gives a short double bounce, rests again.
   ------------------------------------------------------------ */
.scroll-arrow {
  position: absolute;
  left: 50%;
  bottom: clamp(1.25rem, 4vh, 2.75rem);
  z-index: 3;
  translate: -50% 0;
  display: block;
  width: clamp(1.5rem, 3vw, 2.25rem);
  color: var(--off-black);
}

.scroll-arrow svg {
  display: block;
  width: 100%;
  height: auto;
  animation: nudge 4.5s var(--ease) infinite;
}

@keyframes nudge {
  0%, 62%, 100% { transform: translateY(0); }
  70%           { transform: translateY(28%); }
  78%           { transform: translateY(0); }
  86%           { transform: translateY(14%); }
  92%           { transform: translateY(0); }
}

/* ── Reduced motion ───────────────────────────────────────── */
@media (prefers-reduced-motion: reduce) {
  .panel__inner { transform: none; opacity: 1; }
  .scroll-arrow svg { animation: none; }
  .grain { animation: none; }
  .morph { animation: none; }
}
