/* ==========================================================================
   Anne — Product Designer
   Single stylesheet. Sections in reading order:

     1.  Tokens
     2.  Reset & base
     3.  Layout primitives  (canvas, sticky stack, sections)
     4.  Shapes             (shared by hero + contact)
     5.  Navigation
     6.  Hero
     7.  Work
     8.  Contact
     9.  Motion             (reveals, magnetic hover)
     10. Breakpoints        (tablet ≤710px, mobile ≤420px)
   ========================================================================== */


/* 1. TOKENS ================================================================ */

:root {
  /* Colour */
  --brown:        #381b16;   /* page background, dark sections   */
  --cream:        #f0cdbf;   /* display type on dark, shapes     */
  --white:        #ffffff;
  --ink:          #163338;   /* body copy on the cream panel     */
  --paper:        #ffffff;   /* work card                        */
  --green:        #4c9a4c;   /* "available for work" dot         */
  --shape-alpha:  .2;        /* fill opacity of the big shapes   */

  /* Type */
  --font-display: "Barlow Condensed", "Arial Narrow", sans-serif;
  --font-body:    "Space Grotesk", system-ui, sans-serif;

  --size-display: 80px;      /* h1                               */
  --size-project: 56px;      /* work list                        */
  --size-lead:    20px;
  --size-body:    16px;
  --size-small:   14px;

  /* Space.
     The design is drawn on a fixed canvas that is centred in the viewport —
     1390px on desktop, 810px on tablet, 390px on mobile — exactly like the
     original. Below each of those widths the canvas simply shrinks. */
  --gutter:       80px;      /* page margin inside the canvas    */
  --canvas:       1390px;    /* composition width                */
  --card-inset:   40px;      /* margin around the work card      */
  --radius:       12px;

  /* Motion */
  --ease:         cubic-bezier(.25, 1, .5, 1);
  --ease-out:     cubic-bezier(.16, 1, .3, 1);
}


/* 2. RESET & BASE ========================================================== */

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

html { scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--brown);
  color: var(--white);
  font-family: var(--font-body);
  font-size: var(--size-body);
  font-weight: 300;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

h1, h2, p, dl, dd, dt, figure, ul { margin: 0; }
ul { padding: 0; list-style: none; }
img { display: block; max-width: 100%; height: auto; }
a  { color: inherit; text-decoration: none; }

.svg-sprite { position: absolute; }


/* 3. LAYOUT PRIMITIVES ===================================================== */

/* Centred composition track. */
.canvas {
  width: 100%;
  max-width: var(--canvas);
  margin-inline: auto;
  height: 100%;
  position: relative;
}

/* The sticky stack: each section pins at the top while the next slides over.
   Height = one viewport per section, plus a little extra to land on. */
.stack {
  position: relative;
  height: calc(100vh * 3 + 30vh);
}

.section {
  position: sticky;
  top: 0;
  height: 100vh;
  overflow: clip;
}


/* 4. SHAPES ================================================================ */

/* .shape       — positions the composition (left/top set per instance)
   .shape__body — carries the rotation and the magnetic offset
   .shape__art  — the SVG itself                                             */

/* `left`/`top` place the shape's *centre*. Centring uses negative margins
   rather than a translate, so `translate` stays free for the reveal and the
   magnetic offset. (Percentage margins resolve against the container width,
   which is also what --w is a percentage of — so both axes line up.) */
.shape {
  position: absolute;
  width: var(--w);
  aspect-ratio: 1;
  margin-top: calc(var(--w) / -2);
  margin-left: calc(var(--w) / -2);
  display: block;
}

.shape__body {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  rotate: var(--rotate, 0deg);
  /* --mx / --my are written by the magnetic-hover script */
  translate: var(--mx, 0px) var(--my, 0px);
  transition: translate .45s var(--ease-out), scale .45s var(--ease-out);
}

.shape:hover .shape__body { scale: 1.04; }

.shape__art {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  fill: var(--shape-fill, var(--cream));
  opacity: var(--shape-alpha);
  transition: opacity .45s var(--ease-out);
}

.shape:hover .shape__art { opacity: .32; }

/* Outlined variant — used for the hero tagline blob. */
.shape__art--outline {
  fill: none;
  stroke: var(--cream);
  stroke-width: 5;
  opacity: 1;
}

.shape__label {
  position: relative;
  z-index: 1;
  max-width: 70%;
  font-size: var(--size-body);
  font-weight: 600;
  letter-spacing: -.01em;
  line-height: 1.2;
  text-align: center;
}

.shape__glyph {
  position: relative;
  z-index: 1;
  width: 21%;
  fill: currentColor;
}


/* 5. NAVIGATION ============================================================ */

.nav {
  /* ══ NAV INTERACTION — every hover / active property lives here ══════════
     Change a value once and it applies to all three links. The rules further
     down only reference these; they hold no numbers of their own.
     The mobile block in §10 overrides the same names, nothing else.
     Behaviour that is not styling — when the active link switches, and which
     sections invert the nav — is in script.js, block 5.                    */

  --label-rest:    .75;    /* label opacity when it is not the current page */
  --label-active:  1;      /* label opacity on hover, and for the section
                              you are currently looking at                  */
  --label-fade:    .3s;    /* how long the label takes to brighten          */

  --marker-size:   12px;   /* the little shape in front of the label        */
  --marker-rest:   0;      /* marker opacity when inactive (0 = hidden)     */
  --marker-active: 1;      /* marker opacity for the current section        */
  --marker-slide: -6px;    /* how far left it starts before sliding in      */
  --marker-fade:   .35s;   /* marker fade + slide duration                  */

  --logo-fade:     .3s;    /* logo colour change when the nav inverts       */
  /* ═══════════════════════════════════════════════════════════════════════ */

  position: fixed;
  inset: 0 0 auto 0;
  z-index: 10;
  height: 80px;
  pointer-events: none;
}

.nav__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding-inline: var(--gutter);
  pointer-events: none;
}

.nav__logo,
.nav__links { pointer-events: auto; }

.nav__logo svg {
  width: 39px;
  height: auto;
  fill: var(--white);
  transition: fill var(--logo-fade) var(--ease);
}

.nav__links {
  display: flex;
  align-items: center;
  gap: 32px;
}

.nav__link {
  position: relative;
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: var(--size-small);
  font-weight: 400;
  letter-spacing: .015em;
  color: var(--white);
  opacity: .75;
  transition: opacity .3s var(--ease);
}

.nav__link:hover,
.nav__link.is-active { opacity: 1; }

/* The shape marker slides in only for the section you're on. */
.nav__marker {
  width: 12px;
  height: 12px;
  fill: var(--cream);
  opacity: 0;
  translate: -2px 0;
  transition: opacity .2s var(--ease), translate .2s var(--ease-out);
}

.nav__link.is-active .nav__marker { opacity: 1; translate: 0 0; }

/* On the cream contact panel the nav flips to dark. */
.nav.is-inverted .nav__logo svg { fill: var(--brown); }
.nav.is-inverted .nav__link     { color: var(--brown); }
.nav.is-inverted .nav__marker   { fill: var(--brown); }


/* 6. HERO ================================================================== */

.hero { background: var(--brown); }

.hero__canvas { 
  padding-inline: var(--gutter);}

.hero__intro {
  position: absolute;
  left: var(--gutter);
  bottom: 19.2%;
  width: 45.2%;
  display: flex;
  flex-direction: column;
  gap: 24px;
}

.hero__title {
  font-family: var(--font-display);
  font-size: var(--size-display);
  font-weight: 900;
  line-height: 1;
  letter-spacing: -.4px;
  text-transform: uppercase;
  color: var(--cream);
}

.hero__title .line { display: block; }

.hero__meta {
  display: flex;
  flex-direction: column;
  gap: 8px;
  font-size: var(--size-body);
  font-weight: 600;
  letter-spacing: -.01em;
}

.hero__status { display: flex; align-items: center; gap: 8px; }

.dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--green);
  animation: pulse 2.4s var(--ease) infinite;
}

@keyframes pulse {
  0%, 100% { opacity: 1;  transform: scale(1); }
  50%      { opacity: .5; transform: scale(.8); }
}

/* Hero shape placement — percentages of the canvas box. */
.shape--tagline { --w: 18.6%; left: 63.7%; top: 39.8%; }
.shape--work    { --w: 24.8%; left: 84.3%; top: 46.5%; }
.shape--contact { --w: 17.1%; left: 68%;   top: 72.7%; }

.shape--tagline .shape__label {
  max-width: 66%;
  font-weight: 300;
  line-height: 1.8;
  letter-spacing: -.03em;
}


/* 7. WORK ================================================================== */

.work {
  display: grid;
  place-items: center;
  background: transparent;
}

.work__card {
  position: relative;
  width: min(calc(100% - var(--card-inset) * 2),
             calc(var(--canvas) - var(--card-inset) * 2));
  height: 90vh;
  background: var(--paper);
  border-radius: var(--radius);
  overflow: hidden;
  color: var(--brown);
}

.eyebrow {
  position: absolute;
  top: var(--gutter);
  left: var(--gutter);
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: var(--size-body);
  font-weight: 600;
  letter-spacing: -.01em;
  color: var(--brown);
}

.eyebrow__icon { width: 12px; height: 12px; fill: var(--cream); }

.projects {
  position: absolute;
  top: 50%;
  left: var(--gutter);
  translate: 0 -50%;
  width: 39%;
  display: flex;
  flex-direction: column;
  gap: 20px;
  z-index: 1;
}

.project {
  display: flex;
  align-items: center;
  gap: 12px;
  width: max-content;
}

.project__name {
  font-family: var(--font-display);
  font-size: var(--size-project);
  font-weight: 900;
  line-height: 1;
  letter-spacing: -.2px;
  text-transform: uppercase;
  color: var(--brown);
  opacity: .35;
  transition: opacity .4s var(--ease);
}

/* Every label carries its own state. `is-active` is set per label by
   script.js — on load for the first one, then on whichever you point at.
   :hover / :focus-visible are here so the label still responds on its own
   even before the script has swapped the class. */
.project.is-active .project__name,
.project:hover .project__name,
.project:focus-visible .project__name { opacity: 1; }


/* The mask: a fixed window down the right of the card, open at all times.
   Images are stacked inside it and cross-fade as you move down the list, so
   the panel is never empty and nothing spills past the card's corner. */
.work__media {
  position: absolute;
  inset: 0 0 0 auto;
  width: 50%;
  overflow: hidden;
}

.work__image {
  position: absolute;
  inset: 0;
  opacity: 0;
  transition: opacity .5s var(--ease);
}

.work__image.is-active { opacity: 1; }

/* Each picture settles back from a zoom as it comes in, so a swap has a
   little movement rather than being a flat dissolve. */
.work__image img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  scale: 1.02;
  transition: scale 1s var(--ease-out);
}

.work__image.is-active img { scale: 1; }


/* 8. CONTACT =============================================================== */

.contact { background: var(--brown); }

/* The cream panel is the canvas itself — brown shows either side of it. */
.contact__panel {
  position: relative;
  width: 100%;
  max-width: var(--canvas);
  height: 100%;
  margin-inline: auto;
  background: var(--cream);
  color: var(--ink);
}

.contact__infos {
  position: absolute;
  left: var(--gutter);
  top: 50%;
  translate: 0 -50%;
  width: 61.2%;
  display: flex;
  flex-direction: column;
  gap: 48px;
}

.contact__intro {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.contact__title {
  font-family: var(--font-display);
  font-size: var(--size-display);
  font-weight: 900;
  line-height: 1;
  letter-spacing: -.4px;
  text-transform: uppercase;
  color: var(--brown);
}

.contact__lead {
  font-size: var(--size-lead);
  font-weight: 300;
  line-height: 1.55;
  letter-spacing: -.025em;
}

.details {
  display: flex;
  flex-wrap: wrap;
  gap: 48px;
}

.details__item { display: flex; flex-direction: column; gap: 2px; }

.details dt {
  font-size: var(--size-small);
  font-weight: 700;
  letter-spacing: .18em;
  text-transform: uppercase;
  line-height: 1.25;
}

.details dd {
  font-size: var(--size-lead);
  font-weight: 300;
  line-height: 1.55;
  letter-spacing: -.025em;
}

.details dd a { transition: opacity .3s var(--ease); }
.details dd a:hover { opacity: .6; }

/* Shape cluster — its own coordinate box, mirroring the original artboard. */
.contact__shapes {
  position: absolute;
  right: 2.8%;
  top: 50%;
  translate: 0 -50%;
  width: 34%;
  aspect-ratio: 473 / 657;
  color: var(--brown);
}

.contact__shapes .shape {
  --shape-fill: var(--brown);
  margin: 0;                   /* positioned from the top-left here */
}

.contact__shapes .shape__label { color: var(--brown); }

.shape--mail     { --w: 56%;   left: 6.3%;  top: 4.6%;  }
.shape--linkedin { --w: 28.5%; left: 46.9%; top: 30.7%; }
.shape--dribbble { --w: 34.9%; left: 61.1%; top: 48.9%; }
.shape--resume   { --w: 49.5%; left: 23.7%; top: 58.4%; }


/* 9. MOTION ================================================================ */

/* Character-by-character headline reveal. Split by script.js — without JS the
   text simply renders as normal. */
.word { display: inline-block; white-space: nowrap; }

.char {
  display: inline-block;
  opacity: 0;
  transform: translateY(20px);
}

/* Line-by-line headline reveal. Each line rises and fades in, staggered by its
   --i (set in the markup). Add or remove a line and give it the next index.
   Everything below is scoped to `.js` so the page stays fully visible when
   JavaScript is off — script.js puts that class on <html>. */
.js [data-lines] .line {
  opacity: 0;
  transform: translateY(24px);
}

.js [data-lines].is-revealed .line {
  opacity: 1;
  transform: none;
  transition: opacity .7s var(--ease-out), transform .7s var(--ease-out);
  transition-delay: calc(var(--i, 0) * 110ms);
}

/* Element reveals. `drop` falls in from above, `rise` lifts from below.
   These use `transform`, never `translate`, so they compose cleanly with any
   layout centring an element already does with `translate`. */
.js [data-reveal] {
  opacity: 0;
  transition: opacity .7s var(--ease-out), transform .7s var(--ease-out);
}

.js [data-reveal="drop"] { transform: translateY(-40px); }
.js [data-reveal="rise"] { transform: translateY(40px); }

.js [data-reveal].is-revealed { opacity: 1; transform: none; }

/* Stagger the three hero shapes. */
.shape--tagline { transition-delay: .15s; }
.shape--work    { transition-delay: .25s; }
.shape--contact { transition-delay: .35s; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }

  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }

  /* Only `transform` is reset — `translate` is layout (centring), not motion. */
  .js [data-lines] .line,
  .js [data-reveal] { opacity: 1; transform: none; }
}

/* 10. BREAKPOINTS ==========================================================
   Each breakpoint restates the canvas width and the handful of positions that
   genuinely change. Shape coordinates are percentages of their container, so
   the composition holds together at any width inside the range.
   ========================================================================== */

/* --- Tablet: ------------------------------------------------ */
@media (max-width: 710px) {
  :root {
    --gutter:     56px;
    --card-inset: 56px;
  }

  /* Headline moves to the top. Anchored to the gutter — not centred on a
     percentage — so it lines up with the nav logo at every width. */
  .hero__intro {
    left: var(--gutter);
    translate: none;
    top: 128px;
    bottom: auto;
    width: calc(100% - var(--gutter) * 2);
  }

  /* Anchored by edge here rather than by centre. */
  .shape--tagline,
  .shape--work,
  .shape--contact {
    margin: 0;
    top: auto;
  }

  .shape--tagline { --w: 39.5%; left: 5.3%;  bottom: 81px;  }
  .shape--work    { --w: 40.6%; left: 51%;   bottom: 195px; }
  .shape--contact { --w: 28.8%; left: 36.9%; bottom: 36px;  }

  /* Work: list centres, imagery steps back. */
  .eyebrow {
    top: 56px;
    left: 50%;
    translate: -50% 0;
  }

  .projects {
    left: 50%;
    translate: -50% -50%;
    width: 100%;
    align-items: center;
    text-align: center;
    gap: 40px;
  }

  .project { width: auto; justify-content: center; }
  .project__name  { opacity: 1; }
  .work__media    { display: none; }

  /* Contact: text at the top, shapes as a band across the bottom. */
  .contact__infos {
    top: 56px;
    left: var(--gutter);
    translate: none;
    width: calc(100% - var(--gutter) * 2);
    gap: 24px;
  }

  .details { flex-direction: column; gap: 24px; }

  .contact__shapes {
    right: 0;
    left: 0;
    top: auto;
    bottom: 208px;
    translate: 0 0;
    width: 100%;
    height: 36%;
    aspect-ratio: auto;
  }

  .shape--resume   { --w: 35.4%; left: 6.6%;  top: -13%; }
  .shape--mail     { --w: 40.1%; left: 56.2%; top: -24%; }
  .shape--linkedin { --w: 20.4%; left: 39.8%; top: 21.8%; }
  .shape--dribbble { --w: 24.9%; left: 37.5%; top: -10.3%; }
}

/* --- In-between widths: text only ----------------------------------------
   From 1024px down to the mobile breakpoint there is not enough room for the
   hero composition to read — the shapes crowd the headline. They are hidden
   through this range and come back at ≤500px, where the mobile layout stacks
   them below the text.

   The range stops one pixel above the mobile breakpoint so the two never
   overlap; if you move the 500px breakpoint, move the 501px here to match. */
@media (max-width: 1024px) and (min-width: 501px) {
  .hero .shape--tagline,
  .hero .shape--work,
  .hero .shape--contact { display: none; }

  /* With the shapes gone the headline is the whole composition: it takes the
     full width instead of the 45% column it shared with them, and sits in the
     middle of the section so it stays put as the window resizes rather than
     hanging off the bottom edge. */
  .hero__intro {
    top: 50%;
    bottom: auto;
    translate: 0 -50%;
    width: calc(100% - var(--gutter) * 2);
  }
}

/* --- Mobile: */
@media (max-width: 500px) {
  :root {
    --gutter:       24px;
    --card-inset:   24px;
    --size-display: 56px;
    --size-project: 40px;
    --size-lead:    16px;
    --size-small:   10px;
  }

  /* Nav collapses to three shape markers. */
  .nav__links { gap: 40px; }
  .nav__link  { gap: 0; opacity: 1; }
  .nav__link span { display: none; }

  .nav__marker {
    width: 24px;
    height: 24px;
    opacity: .4;
    translate: 0 0;
  }

  .nav__link.is-active .nav__marker { opacity: 1; }

  /* Hero — width is inherited from the block above (gutter-anchored). */
  .hero__intro {
    top: 104px;
    gap: 16px;
  }

  .hero__title  { letter-spacing: -1.1px; }
  .hero__meta   { gap: 8px; font-size: 14px; }
  .eyebrow      { font-size: 14px; }

  .shape--tagline { --w: 50.8%; left: 10%;   bottom: 169px; }
  .shape--work    { --w: 48%;   left: 46.7%; bottom: 58px;  }
  .shape--contact { --w: 39.7%; left: 11.5%; bottom: 36px;  }

  .shape--tagline .shape__label { line-height: 1.6; }

  .shape--work .shape__label,
  .shape--contact .shape__label { font-size: 14px; }

  /* Work */
  .eyebrow  { top: 40px; }
  .projects { width: 86%; gap: 32px; }

  /* Contact */
  .contact__infos {
    top: 66px;               /* clears the nav */
    gap: 24px;
  }

  .contact__lead { line-height: 1.6; }
  .details       { gap: 16px; }
  .details dt    { letter-spacing: .16em; }

  .contact__shapes {
    bottom: 40px;
    height: 48%;
  }

  .shape--linkedin { --w: 34.9%; left: 7.3%;  top: 6.3%;  }
  .shape--mail     { --w: 48.4%; left: 43.5%; top: 7.2%;  }
  .shape--resume   { --w: 40.2%; left: 9.9%;  top: 41.9%; }
  .shape--dribbble { --w: 47.1%; left: 47.2%; top: 38.7%; }
}


/* 11. PROJECT PAGE =========================================================
   Used by project.html. Nothing above this line is affected — the page opts
   in with `class="page--project"` on <body>, which flips the palette.

   Every content block shares one horizontal padding, `--pad-x`, which holds
   the text column at 910px on a wide screen and falls back to the page gutter
   as the window narrows. That single clamp replaces a set of breakpoints.
   ========================================================================== */

.page--project {
  --project-bg:    #ffffff;   /* page + article                            */
  --project-cover: #fff7f7;   /* behind the cover image                    */
  --project-ink:   #000000;   /* body copy and section headings            */

  /* 910px column on desktop, gutter-width once the screen is narrower. */
  --pad-x:      clamp(var(--gutter), calc((100% - 910px) / 2), 240px);
  --block-space: 120px;       /* between any two blocks — one value, so the
                                 rhythm down the page is even everywhere    */
  --block-gap:  56px;         /* between the parts of one block            */
  --size-h2:    56px;         /* section headings                          */
  --head-gap:   24px;         /* between a heading and its paragraphs      */
  --meta-gap:   80px;         /* between credit columns                    */

  background: var(--project-bg);
  color: var(--project-ink);
}


/* --- Cover ---------------------------------------------------------------
   Pinned at the top while the article scrolls up over it — the same move the
   home page makes between its sections. */

.cover {
  position: sticky;
  top: 0;
  height: 800px;
  max-width: var(--canvas);
  margin-inline: auto;
  background: var(--project-cover);
  overflow: clip;
}

.cover__image {
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* Anchor the crop to the top edge, so a tall photo keeps its head in frame
     instead of being centred and losing it. */
  object-position: top;
}

/* Back link. Fixed rather than parked on the cover, so it stays reachable the
   whole way down the page, and above everything else — the sticky cover, the
   article and the ticker all sit below it.

   The bar spans the window but its track is the canvas, so the link lands on
   the same gutter as the copy. Same construction as the home page nav. */
.backlink-bar {
  position: fixed;
  inset: 0 0 auto 0;
  z-index: 100;
  height: 80px;
  display: flex;
  align-items: center;
  max-width: var(--canvas);
  margin-inline: auto;
  padding-inline: var(--gutter);
  pointer-events: none;         /* only the link itself is clickable */
}

.backlink {
  pointer-events: auto;
  display: flex;
  align-items: center;
  gap: 12px;
  font-size: 14px;              /* fixed — the label reads the same on every
                                   device, so it does not follow --size-small */
  font-weight: 400;
  letter-spacing: -.015em;
  color: var(--brown);
}

.backlink__arrow {
  width: 32px;
  height: 32px;
  fill: currentColor;
  /* Slides back towards the page it returns to. */
  transition: translate .35s var(--ease-out);
}

.backlink:hover .backlink__arrow { translate: -6px 0; }


/* --- Article -------------------------------------------------------------
   Sits above the cover and carries its own background, so it wipes the cover
   as it rises. */

.article {
  position: relative;
  z-index: 1;
  max-width: var(--canvas);
  margin-inline: auto;
  background: var(--project-bg);

  /* One gap value between every block, and the same again top and bottom. */
  display: flex;
  flex-direction: column;
  gap: var(--block-space);
  padding-block: var(--block-space);
}


/* --- Blocks --------------------------------------------------------------
   Five variants, all the same shape: padding, then a column of parts. */

/* Blocks carry no vertical padding of their own — the article spaces them,
   so every gap down the page is exactly --block-space. */
.block {
  display: flex;
  flex-direction: column;
  gap: var(--block-gap);
  padding-inline: var(--pad-x);
}

/* Heading + paragraphs. */
.block__head {
  display: flex;
  flex-direction: column;
  gap: var(--head-gap);
}

.block__title {
  font-family: var(--font-display);
  font-size: var(--size-project);          /* 56px; the h1 is bumped below */
  font-weight: 900;
  line-height: 1;
  letter-spacing: -.2px;
  text-transform: uppercase;
  color: var(--project-ink);
}

h1.block__title {
  font-size: var(--size-display);          /* 80px */
  letter-spacing: -.4px;
  color: var(--brown);
}

.block__title em { font-style: normal; }   /* emphasis is a line break, not italics */

.block p {
  font-size: 16px;
  font-weight: 300;
  line-height: 1.8;
  letter-spacing: -.03em;
}

.block__head p + p { margin-top: -12px; }  /* paragraphs sit tighter than the head gap */

/* --wide sits on the text margin, so images and videos line up with the copy
   above them. --bleed ignores it and runs the full width of the page. */
.block--bleed { padding-inline: 0; }

/* Phone screens sit in a row that is wider than the text column, so they get
   the page gutter rather than --pad-x. Both the screens and the space between
   them are relative: the images share whatever is left over, and the gap
   scales with the viewport, so the row keeps its proportions all the way down
   to the mobile breakpoint, where it stacks. */
/* A grid, not a row: three per line, so a block of six wraps to two lines and
   one `gap` governs the space both across and down.

   `minmax(0, 1fr)` rather than `1fr` — a bare `1fr` floors each column at its
   content's own width, and a <video> carrying a width attribute would push its
   column to full size and squash the others. */
.block--phones {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(16px, 3.7vw, 56px);
  align-items: start;
  padding-inline: var(--pad-x);
}

/* A screen can be a still or a video; both are sized the same way, so a clip
   sits at exactly the height of the stills beside it.

   By default each keeps its own proportions (`auto`). If a block mixes assets
   of different shapes, set --screen-ratio on that section and everything in it
   is forced to match, cropping rather than sitting taller or shorter:

     <section class="block block--phones" style="--screen-ratio: 750 / 1624">  */
.block--phones img,
.block--phones video {
  width: 100%;
  height: auto;
  aspect-ratio: var(--screen-ratio, auto);
  object-fit: cover;
  border-radius: var(--radius);
}

.block--wide img,
.block--wide video,
.block--bleed img { display: block; width: 100%; height: auto; }

/* Hairline divider between two blocks, full width of the page. */
.rule {
  height: 1px;
  border: 0;
  margin: 0;
  background: var(--cream);
}


/* --- Credits / figures ---------------------------------------------------
   `.meta` is the Role / Agency / Scope row; `.meta--even` is the results row,
   where the columns share the width equally. */

.meta {
  display: flex;
  flex-wrap: wrap;
  gap: var(--meta-gap);
}

.meta--even .meta__item { flex: 1 1 0; }

.meta__item {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.meta dt {
  font-size: var(--size-small);
  font-weight: 700;
  letter-spacing: .18em;
  text-transform: uppercase;
  line-height: 1.25;
  color: var(--ink);
}

.meta dd {
  font-size: var(--size-body);
  font-weight: 300;
  line-height: 1.8;
  letter-spacing: -.03em;
}


/* --- Ticker --------------------------------------------------------------
   Runs to the next project. script.js (block 7) clones the list until it is
   wide enough to loop, then sets the two custom properties below. */

.ticker {
  display: block;
  /* The cover is sticky for the whole page, so it would otherwise show
     through the top of this band. Lifting the ticker onto the same layer as
     the article keeps it on its own background. */
  position: relative;
  z-index: 1;
  max-width: var(--canvas);
  margin-inline: auto;
  height: 119px;
  overflow: clip;
  background: var(--cream);
}

.ticker__track {
  display: flex;
  align-items: center;
  gap: 24px;
  height: 100%;
  width: max-content;
  font-size: var(--size-lead);
  font-weight: 400;
  letter-spacing: -.025em;
  color: var(--brown);
  animation: ticker-scroll var(--ticker-duration, 20s) linear infinite;
}

@keyframes ticker-scroll {
  to { transform: translateX(var(--ticker-shift, -50%)); }
}

@media (prefers-reduced-motion: reduce) {
  .ticker__track { animation: none; }
}


/* --- Page-load fade ------------------------------------------------------
   Arriving from the work list, the whole page fades up rather than snapping
   in. `both` holds the opening frame until the animation starts, so there is
   no flash of the finished page first.

   Put `page--project` (or just this animation) on any page you want to enter
   the same way. */

/* Applied to the page's parts rather than <body>: an ancestor mid-fade has an
   opacity below 1, which would make it the containing block for the fixed
   back-link and leave it scrolling with the page until the fade finished. */
.page--project .backlink-bar,
.page--project .cover,
.page--project .article,
.page--project .ticker { animation: page-in .8s var(--ease-out) both; }

@keyframes page-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
  .page--project .backlink-bar,
  .page--project .cover,
  .page--project .article,
  .page--project .ticker { animation: none; }
}


/* --- Project page breakpoints -------------------------------------------
   --pad-x handles itself; only the vertical rhythm changes. */

@media (max-width: 710px) {
  .page--project {
    --pad-y:      40px;
    --head-gap:   16px;
    --meta-gap:   24px;
  }

  .meta { flex-direction: column; }

  /* Phone screens stack one under the other rather than shrinking to nothing.
     This is at the tablet breakpoint, not the mobile one, because plenty of
     phones report more than 420px — a 430px handset would otherwise still get
     three screens side by side. */
  .block--phones {
    grid-template-columns: 1fr;
    gap: var(--block-gap);
  }
}

@media (max-width: 420px) {
  .page--project {
    --pad-y-media: 0px;       /* images go edge to edge */
    --block-gap:   24px;
    --head-gap:    24px;
    --meta-gap:    16px;
  }

  .cover    { height: 100vh; }
  .backlink { left: var(--gutter); }

  .block--wide { padding-inline: 0; }

}
