/* ==========================================================================
   1324 — home page
   Layout and copy come from the Claude Design wireframe; the motion and
   surface treatments are carried over from the holding page (holding.html).
   ========================================================================== */

:root {
  --bg:      #fffefb;
  --surface: #ffffff;
  --ink:     #2b2721;
  --ink-2:   #5c5648;
  --ink-3:   #8a8272;
  --line:    #ece5d5;
  --line-2:  #e7e0d0;
  --accent:  #f3d488;
  --turq:    #f2b82e;   /* the accent that draws — every hairline is this */
  --dark:    #100f0d;

  /* Type. Google Sans is Google's own brand face — it is not on Google Fonts
     and is not licensed for use off Google's own products, so it cannot be
     linked here. DM Sans is the nearest licensed equivalent: same geometric
     skeleton, same low contrast, near-circular bowls, double-storey a and
     single-storey g, and it is variable, so one file covers the 11px labels
     and the 58px headings. Change this one line and the whole site follows.

     --meta was Space Mono. It now points at the same family, because one
     face everywhere was the brief; set it back to 'Space Mono', monospace to
     restore the two-voice system — and add it to the font link, which now
     requests DM Sans and nothing else. */
  --sans: 'DM Sans', system-ui, sans-serif;
  --meta: var(--sans);

  /* How much the h1 corners soften. See the h1 rule for how it works. */
  --h1-round: .03em;

  --gutter: 30px;
  --maxw:   1800px;

  /* x of the content edge. The page's vertical rules, the rules between
     sections and the rails all hang off this one number, so the whole line
     system stays on the same grid. Percentages resolve per use site — every
     element that reads it spans the full page width. */
  --edge: max(var(--gutter), calc((100% - var(--maxw)) / 2 + var(--gutter)));

  --card:  425px;   /* rail card width — from the design */
  /* How far content sits inside the rules. One number for cards and for the
     page's own margins, so a heading and the card below it start on the
     same line. */
  --inset: 24px;
  --head-gap: 80px;   /* under every section title */

  --ease:  cubic-bezier(.19, 1, .22, 1);   /* the holding page's entry easing */
}

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

html { scroll-behavior: smooth; }

body {
  background: var(--bg);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 16px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  /* clip, not hidden — `hidden` would make <body> a scroll container, which
     breaks the sticky stage the pinned rail hangs off. */
  overflow-x: clip;
}

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

h1, h2, h3, h4 {
  font-family: var(--sans);
  font-weight: 500;
  letter-spacing: normal;
  line-height: 1.14;
  text-wrap: pretty;
}

/* The page's heading voice — scoped to the section heads it was designed for.
   Unscoped it leaked onto every h1 and h2 in the parked sub-pages, where a
   100px "The outcome" out-sizes the statistics it introduces.

   Leading comes down hard: 1.14 is a setting for a 40px heading and falls
   apart at three and a half times that. Tracking is now the face's own
   default. The measure is counted in lines rather than characters — 34ch at
   this size would be 2,000px of line.

   The rounding: there is no way to round a typeface's corners in CSS, but a
   same-colour stroke painted *behind* the fill gets there, because the join
   style browsers use on text strokes is round. paint-order is what keeps it
   behind — painted on top it eats into the counters. The weight drops a step
   because the stroke puts back the weight it takes. Nothing here is
   load-bearing: without -webkit-text-stroke you get plain 450. */
.section-head h1,
.section-head h2 {
  font-weight: 450;
  -webkit-text-stroke: var(--h1-round) currentColor;
  paint-order: stroke fill;
  font-size: 100px;
  line-height: .92;
  max-width: 13ch;
}
/* Below ~900px a 100px line starts crowding the screen, so it scales from
   there. 11.1vw meets 100px exactly at the breakpoint — no jump. */
@media (max-width: 900px) {
  .section-head h1,
  .section-head h2 { font-size: 11.1vw; max-width: none; }
}

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

:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 3px;
  border-radius: 4px;
}

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

/* The gutter puts the vertical rules where they are; the inset keeps text off
   them. Ruled boxes — the logo grid, the gallery — reach back out to the rules
   with a negative margin, because there the rule *is* their edge. */
.wrap {
  max-width: var(--maxw);
  margin: 0 auto;
  padding-left: calc(var(--gutter) + var(--inset));
  padding-right: calc(var(--gutter) + var(--inset));
}

main > section { position: relative; }

/* --------------------------------------------------------------------------
   Five sections, one treatment.

   Every one of them puts a large heading in its top-left corner, and the
   typed transition carries the eye from one to the next. Only #work pins:
   it is the only section with something for the scroll to drive. The other
   four take exactly as much scroll as their content needs, which is what
   gives the page its uneven — deliberate — cadence: a short beat, a read,
   the long sideways hold, a read, and a short close.

   Sections 1 and 5 are beats. Short on purpose: their titles type in and
   wipe out rather than sitting still.
   -------------------------------------------------------------------------- */

/* A beat is short, but not shorter than the transition that carries it: the
   title needs its type-in, a beat of stillness and its wipe, and below about
   half a screen there is no stillness left between the two.

   The close is a screen because it has to be. A title finishes typing when
   its section reaches the top of the viewport, and the last section can only
   get there if a full screen of page follows it — at 70svh the closing line
   would still be typing itself when the page ran out of scroll. */
/* Stated whole rather than as height + tail: with border-box a min-height
   swallows its own padding, so a tail added that way never showed up. */
.sec.sec-open  { min-height: 84svh; }
.sec.sec-close { min-height: 114svh; }

/* --- 5 · the close: where we are, and how to reach us -------------------- */
.close-body {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: space-between;
  gap: 40px 64px;
}
.towns {
  list-style: none;
  display: flex; flex-wrap: wrap; gap: 8px 64px;
}
.towns li { display: flex; flex-direction: column; gap: 4px; }
.towns span:first-child { font-size: 19px; }
.towns span:last-child {
  font-family: var(--meta);
  font-size: 10px; letter-spacing: .2em; text-transform: uppercase;
  color: var(--ink-3);
}
.mail { font-size: 34px; line-height: 1.5; color: var(--ink-2); }

/* Anything still standing in for real content says so on the page, not only
   in the markup. Nothing ships with one of these still on it. */
[data-placeholder] {
  color: var(--ink-3);
  text-decoration: underline dotted var(--turq);
  text-underline-offset: .28em;
  text-decoration-thickness: 2px;
}

/* --------------------------------------------------------------------------
   Typed — one caret writes every section title and deletes it again.

   All five titles share one fixed layer, because they all settle in the same
   place and because the caret has to survive a section boundary for the
   effect to read as one continuous line of typing. The real headings stay in
   their sections, keeping their space in the layout and their place in the
   reading order.
   -------------------------------------------------------------------------- */
/* Same measure as .wrap — centred, capped, then indented — so a title in the
   layer lands exactly where its heading sits in the flow. */
.title-layer {
  position: fixed;
  top: var(--inset);
  left: 0; right: 0;
  max-width: var(--maxw);
  margin-inline: auto;
  z-index: 5;
  pointer-events: none;
}
.tl-title {
  position: absolute; top: 0;
  left: calc(var(--gutter) + var(--inset));
  right: calc(var(--gutter) + var(--inset));
  font-family: var(--sans);
  font-weight: 450;
  font-size: 100px;
  line-height: .92;
  letter-spacing: normal;
  color: var(--dark);
  max-width: 13ch;
  -webkit-text-stroke: var(--h1-round) currentColor;
  paint-order: stroke fill;
}
@media (max-width: 900px) { .tl-title { font-size: 11.1vw; max-width: none; } }

/* Clipped, not hidden. `visibility: hidden` takes an element out of the
   accessibility tree, which — with a decorative replacement layer over the
   top — left the page with no headings at all. Clipping paints nothing while
   keeping the heading both in the tree and in its own layout box, which is
   what the layer measures itself against. */
.section-head .is-typed-head {
  clip-path: inset(50%);
  -webkit-user-select: none; user-select: none;
}

/* Characters are hidden with opacity rather than removed, so the line is laid
   out once and keeps its wrap points: the caret runs along a line that was
   measured whole, and nothing reflows under it as the words appear. */
.tl-w { white-space: nowrap; }
.tl-c { opacity: 0; }

.tl-caret {
  position: absolute; left: 0; top: 0;
  will-change: transform;
}
/* The bar is a pseudo-element so the blink and the show/hide are on separate
   elements — a running animation on opacity would otherwise outrank the
   inline opacity that swaps the caret for the hint. */
.tl-caret { width: 5px; }
.tl-caret::after {
  content: ''; position: absolute; inset: 0;
  background: var(--turq);
  animation: caret-blink 1.06s steps(1, end) infinite;
}
/* Held, not pulsed — a caret that is dark for as long as it is lit reads as a
   cursor; anything softer reads as a fade. */
@keyframes caret-blink { 0%, 55% { opacity: 1 } 56%, 100% { opacity: .12 } }

@media (prefers-reduced-motion: reduce) { .tl-caret { animation: none } }

/* --------------------------------------------------------------------------
   Services — a plain vertical list. No cards, no rail, no pin: this section
   is read rather than scrubbed, so it takes none of the scroll.
   -------------------------------------------------------------------------- */
.sec.sec-list { padding-bottom: 14vh; }
.service-list {
  list-style: none;
  width: 100%;
  max-width: var(--maxw);
  margin: 0 auto;
  padding: 0 calc(var(--gutter) + var(--inset));
}
.service-list li {
  /* Big enough to hold its own under a 100px heading, small enough that seven
     of them still read as a list rather than seven more headings. */
  font-size: 34px;
  line-height: 1.5;
  color: var(--ink-2);
}

/* --------------------------------------------------------------------------
   Buttons and links
   -------------------------------------------------------------------------- */

.btn {
  display: inline-block;
  padding: 12px 22px;
  border-radius: 10px;
  font-weight: 600;
  font-size: 15px;
  line-height: 1.2;
  white-space: nowrap;
  transition: transform .25s var(--ease), box-shadow .25s var(--ease), background-color .25s ease;
}
.btn:hover { transform: translateY(-2px); box-shadow: 0 8px 20px rgba(43, 39, 33, .13); }
.btn:active { transform: translateY(0); }

.btn-ink    { background: #3d372d; color: #f4f0e6; font-weight: 500; padding: 11px 20px; border-radius: 9px; }
.btn-accent { background: var(--accent); color: var(--ink); padding: 14px 26px; }
.btn-sm     { padding: 12px 22px; border-radius: 9px; font-size: 14px; }

.link-underline {
  font-size: 15px; font-weight: 600;
  border-bottom: 2px solid var(--accent);
  padding-bottom: 3px;
  white-space: nowrap;
  transition: border-color .25s ease;
}
.link-underline:hover { border-bottom-color: var(--ink); }

/* --------------------------------------------------------------------------
   The 1324 mark — four cells, four faces, flickering (see site.js)
   -------------------------------------------------------------------------- */

.brand {
  display: inline-flex; align-items: center; gap: 12px;
  cursor: pointer;
}

/* No panel, no rules between cells — the four faces carry the mark on their own */
.mark {
  display: inline-flex; align-items: stretch;
  height: 38px;
}
.mark .cell {
  display: flex; align-items: center; justify-content: center;
  width: 30px;
  font-size: 21px; line-height: 1;
  color: var(--ink);
}


/* Footer variant: the mark returns at scale, in the holding page's palette */
.mark-lg { height: 64px; }
.mark-lg .cell { width: 50px; font-size: 36px; color: #302a20; }
.mark-lg .cell[data-char="2"] { font-size: 32px; }
.mark-lg .cell[data-char="4"] { font-size: 40px; }

/* --------------------------------------------------------------------------
   Placeholders — swap each .ph for a real <img> when art lands
   -------------------------------------------------------------------------- */

.ph {
  display: flex; align-items: center; justify-content: center;
  text-align: center;
  background: var(--ph, #eae6dc);
  color: var(--ph-ink, rgba(43, 39, 33, .38));
  font-family: var(--meta);
  font-variant-numeric: tabular-nums;
  font-size: 10px; letter-spacing: .2em; text-transform: uppercase;
  padding: 12px;
  border-radius: inherit;
}

/* --------------------------------------------------------------------------
   Horizontal rails (services, work)
   -------------------------------------------------------------------------- */

/* The rail is exactly the content column, so it both starts its first card on
   the left rule and clips the run at the right one — nothing shows in the
   margins. */
.rail {
  margin-left: var(--edge);
  margin-right: var(--edge);
  overflow-x: auto;
  overflow-y: hidden;
  scroll-snap-type: x mandatory;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
}
.rail::-webkit-scrollbar { display: none; }

/* No gap: the cards butt together and the hairline between them is the only
   division, so a rail reads as one ruled band rather than a row of objects. */
.rail-track {
  display: flex;
  gap: 0;
  padding: 0 0 14px;
  width: max-content;
  min-width: 100%;
}
.rail-track::after { content: ''; flex: 0 0 1px; }

/* Ruled on all four sides. Top and bottom run on across the whole rail, so a
   run of cards closes as one band; the left rule divides them. The first card
   needs none of its own — the page's left vertical already stands exactly
   where it would go. */
.card {
  flex: 0 0 var(--card);
  scroll-snap-align: start;
  display: flex; flex-direction: column;
  transition: transform .4s var(--ease);
}
/* The one divider kept: without it seven cards run into one continuous block
   of text. Neutral, not accent. */
.card + .card { border-left: 1px solid var(--line); }




/* --------------------------------------------------------------------------
   Case studies — seven captures of seven different sites, sitting side by side
   in one band. Left raw they fight each other, so every plate goes through the
   same treatment and only gives up its own colour when you ask for it:

     · the artwork is desaturated and washed in the accent, so the run reads as
       one material rather than seven palettes
     · the same vignette top and bottom on every plate, whatever's underneath
     · the same typographic furniture — index, disciplines, name — in the same
       places, which unifies harder than colour does

   Hover lifts the wash and the real design comes back.
   -------------------------------------------------------------------------- */

/* Unpinned — narrow screens, touch, reduced motion — the rail is a native
   swipe rail, and then a card has to carry its own height: everything inside
   a case card is absolutely positioned, so without one it collapses to
   nothing. The pinned rule below overrides this when the stage takes over. */
.card-case { height: min(66svh, 560px); }

.card-case {
  padding: 0;
  position: relative;
  /* Same stock as the portraits — the screen prints onto the page colour
     rather than onto a dark plate, so both runs read as one press. */
  background: var(--bg);
  overflow: hidden;
}

/* Just an overflow box — the wash is baked into the image's own filter chain
   rather than laid over it as a blended layer. A blend layer looked the same
   but composited off the GPU, and with seven of them inside a track that
   transforms every frame it locked the renderer solid. */
.case-plate {
  position: absolute; inset: 0;
  overflow: hidden;
}

/* Desaturated and washed in the accent at rest, its own colour on hover.

   This started as a real halftone — a canvas renderer, two plates printed out
   of register, same as the portraits carried — and it worked, but it was too
   much next to everything else on the page. The renderer has since been taken
   out altogether; it is in the history if it is ever wanted back.

   Two things learned the hard way if it ever comes back: mix-blend-mode and
   SVG filters both froze the renderer outright at this size. Seven ~1200px
   layers inside a rail that transforms every frame have to stay on paths the
   GPU will take — plain alpha and the cheap CSS filters, nothing else.

   Captures are far taller than any card, hence cropping from the top. */
.case-img {
  position: absolute; inset: 0;
  display: block;
  width: 100%; height: 100%;
  object-fit: cover;
  object-position: 50% 0;
  filter: grayscale(1) contrast(1.04);
  transition: filter .45s var(--ease);
}

/* Flat paint, not a blend mode — see above. The accent taken most of the way
   to black: still warm, but dark enough to carry light type and to make the
   jump to full colour on hover land. */
.case-plate::after {
  content: '';
  position: absolute; inset: 0;
  background: var(--case-wash, #5a4312);
  opacity: var(--wash, .8);
  transition: opacity .45s var(--ease);
}

.card-case:hover .case-img,
.card-case:focus-visible .case-img { filter: none; }
.card-case:hover .case-plate::after,
.card-case:focus-visible .case-plate::after { opacity: 0; }

/* Nothing to hover with — show more of the work at rest instead */
@media (hover: none) {
  .card-case { --wash: .5; }
  .case-img { filter: grayscale(.4); }
}

/* The same vignette on every plate, so they share a shape as well as a colour,
   and the type over them holds regardless of the artwork. */
/* No scrim. The plate is one flat field, so the labels have nothing to sit on
   once the photograph comes up — they leave on hover instead. You're looking
   at the work at that point, and it's the only way to keep this flat without
   putting a gradient or a shadow back under the type. */
.case-meta, .card-case h3 {
  transition: opacity .3s var(--ease);
}
.card-case:hover .case-meta,
.card-case:hover h3,
.card-case:focus-visible .case-meta,
.card-case:focus-visible h3 { opacity: 0; }

.case-meta {
  position: absolute;
  top: var(--inset); left: var(--inset); right: var(--inset);
  z-index: 2;
  display: flex; gap: 16px;
  font-family: var(--meta);
  font-variant-numeric: tabular-nums;
  font-size: 10px; font-style: normal;
  letter-spacing: .2em; text-transform: uppercase;
  color: rgba(246, 243, 236, .74);
}
.case-meta i, .case-meta em { font-style: normal; }
.case-meta i { color: var(--turq); }

.card-case h3 {
  position: absolute;
  left: var(--inset); right: var(--inset); bottom: var(--inset);
  z-index: 2;
  font-size: 23px;
  letter-spacing: -.01em;
  color: #f6f3ec;
}

/* Grid: separate tiles, so each is ruled on all four sides and sized by ratio */
.rail-progress {
  max-width: var(--maxw);
  margin: 8px auto 0;
  padding: 0 var(--gutter);
}
.rail-progress i {
  display: block;
  height: 2px;
  border-radius: 2px;
  background: var(--turq);
  width: var(--w, 30%);
  transform: translateX(var(--x, 0));
  transition: width .2s ease;
}
.rail-progress::before {
  content: '';
  display: block;
  height: 2px;
  border-radius: 2px;
  background: var(--line-2);
  margin-bottom: -2px;
}

/* Nothing under the cards — their bottom rule is where the section ends. The
   intro keeps its own air above, so it isn't sitting on the rule overhead.
   .sec is the shared behaviour: all five sections are the same section over and
   over, so everything structural hangs off the one class. */
.sec { padding: 0; }
.sec .rail-track { padding-bottom: 0; }
/* The heading sits the same distance from the rule above it as from the rule
   beside it, so it reads as set into the corner of the cell. The air it used
   to have above now goes underneath, between it and the cards. */
.sec .section-head {
  padding-top: var(--inset);
  margin-bottom: var(--head-gap);
}
.sec .section-head h1,
.sec .section-head h2 { color: var(--dark); }

/* ---- Pinned mode: vertical scroll drives the rail sideways ---- */

/* Off by default, so the section is exactly the markup it was before. */
.pin, .pin-stage { display: contents; }

.is-pinned { padding: 0; }
.is-pinned .pin { display: block; height: var(--pin-h, 200vh); }
.is-pinned .pin-stage {
  display: flex;
  flex-direction: column;
  justify-content: center;
  position: sticky; top: 0;
  height: 100vh;
  overflow: hidden;
}
@supports (height: 100svh) { .is-pinned .pin-stage { height: 100svh; } }
/* .wrap's `margin: 0 auto` would otherwise override the stage's stretch and
   shrink the section head to its own text width, centring it. */
.pin-stage > .wrap { width: 100%; }

/* Deliberately not clipped. The rail keeps its page margins, so the run still
   starts on the content edge, but a card leaving that edge stays visible all
   the way to the side of the screen instead of being cut at the margin. The
   pinned stage is what finally clips, and it is full-bleed. */
.is-pinned .rail {
  overflow: visible;
  scroll-snap-type: none;
}
.is-pinned .rail-track {
  will-change: transform;
  padding-bottom: 0;
}
.is-pinned .rail-progress { margin-top: 28px; }

/* ---- The rail takes what the heading leaves ----
   The stage pins to the top of the viewport and takes the whole of it; the
   heading takes what it needs and the rail takes the rest, so the cards size
   themselves off the screen. No numbers to keep in sync — flexbox does the
   subtraction. */
.sec.is-pinned .pin-stage {
  top: 0;
  height: 100svh;
  justify-content: flex-start;
  padding-bottom: 0;
}
.sec.is-pinned .rail { flex: 1; min-height: 0; }
.sec.is-pinned .rail-track { height: 100%; }
/* Capped rather than filling the stage — a card the full height of the screen
   reads as a panel, not a card. Stretch still applies, so they hang from the
   top of the rail and the run keeps one baseline. */
/* Sized off the viewport, not off the rail: centring means the cards no longer
   stretch, and a case card's content is all absolutely positioned, so without
   a height of its own it collapses to nothing. min() keeps it inside the rail
   on short screens. */
.sec.is-pinned .card { min-height: 0; height: min(58svh, 100%); }
/* On the floor of the screen rather than centred — the heading takes whatever
   it needs at the top and the run always starts from the same line. */
.sec.is-pinned .rail-track { align-items: flex-end; }
.sec.is-pinned .rail-progress { margin-top: 0; }

/* --------------------------------------------------------------------------
   The two of us — photographs, plain.

   These carried a halftone screen redrawn in canvas, two plates printed out
   of register. It was the most expensive thing on the page, and the section
   pinned only to hold it still while it resolved. Both are gone.
   -------------------------------------------------------------------------- */

.folk-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  margin-left: calc(var(--inset) * -1);
  margin-right: calc(var(--inset) * -1);
  column-gap: 72px;
}

.folk {
  background: var(--bg);
  padding: 56px var(--inset) 44px;
  display: flex; flex-direction: column; align-items: center;
  gap: 30px;
}
.folk img, .folk-slot {
  display: block;
  width: min(420px, 74%);
  /* The img carries width/height attributes so the space is reserved before
     it loads. Those map to presentational CSS height, and a specified height
     is what aspect-ratio yields to — so it has to be released explicitly. */
  height: auto;
  aspect-ratio: 1;
  object-fit: cover;
}
/* The slot waiting on the second photograph. Sized like the picture it will
   hold, so nothing moves in the grid when the file lands. */
.folk-slot {
  display: grid; place-items: center;
  background: var(--line-2);
  font-family: var(--meta);
  font-size: 10px; letter-spacing: .2em; text-transform: uppercase;
}

.folk figcaption {
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  text-align: center;
}
.folk-name { font-family: var(--sans); font-size: 19px; }
.folk-role {
  font-family: var(--meta);
  font-variant-numeric: tabular-nums;
  font-size: 10px; letter-spacing: .2em; text-transform: uppercase;
  color: var(--ink-3);
}

@media (max-width: 720px) {
  .folk-grid { grid-template-columns: 1fr; }
  .folk { padding: 40px var(--inset) 32px; }
}

/* --------------------------------------------------------------------------
   Footer — the accent, at full strength. It was the one dark band on the page;
   now it's the one saturated one, and the type on it goes to deep teal-ink
   rather than cream, which is the only pairing that holds at this chroma.
   -------------------------------------------------------------------------- */

/* --------------------------------------------------------------------------
   Reveal on scroll — a quieter echo of the holding page's entry
   -------------------------------------------------------------------------- */

.reveal > * { transition: opacity .95s var(--ease), transform .95s var(--ease); }
/* Rails draw themselves card by card; rules draw rather than fade; the pin
   wrapper must never take a transform or it breaks its own sticky child. */
.js .reveal:not(.in) > *:not(.dotgrid):not(.rail):not(.rail-progress):not(.pin) {
  opacity: 0;
  transform: translateY(18px);
}
.reveal .card {
  transition: opacity .8s var(--ease) var(--d, 0s), transform .8s var(--ease) var(--d, 0s);
}
.js .reveal:not(.in) .card { opacity: 0; transform: translateY(22px); }

/* --------------------------------------------------------------------------
   Sub-pages — services.html and case-study.html
   Separation comes from surface panels and soft shadows rather than rules,
   to match the home page after the strokes came off.
   -------------------------------------------------------------------------- */

.panel {
  background: var(--surface);
  border-radius: 14px;
  box-shadow: 0 14px 34px -24px rgba(43, 39, 33, .22);
}

.page-head { position: relative; padding: 56px 0 72px; }
.page-head .wrap { position: relative; z-index: 1; }

.crumbs {
  display: flex; flex-wrap: wrap; gap: 10px;
  font-family: var(--meta);
  font-variant-numeric: tabular-nums;
  font-size: 11px; letter-spacing: .2em; text-transform: uppercase;
  color: var(--ink-3);
  margin-bottom: 28px;
}
.crumbs a { transition: color .2s ease; }
.crumbs a:hover { color: var(--ink); }
.crumbs span[aria-current] { color: var(--ink); }

.tag-row { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 24px; }
.tag {
  font-family: var(--meta);
  font-variant-numeric: tabular-nums;
  font-size: 10px; letter-spacing: .16em; text-transform: uppercase;
  color: var(--ink-2);
  background: var(--line);
  padding: 7px 13px; border-radius: 999px;
}

.page-lede {
  font-size: 19px; line-height: 1.6;
  color: var(--ink-2);
  max-width: 60ch;
  margin-top: 26px;
}

/* A section of a sub-page. Heading sits in cols 2-6, body in 7-12. */
.block { position: relative; padding: 72px 0; }
.block > .wrap { position: relative; z-index: 1; }
.block-lede { font-size: 18px; line-height: 1.6; color: var(--ink-2); max-width: 54ch; margin-top: 16px; }

.block-split {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  column-gap: 32px; row-gap: 36px;
}
.block-split > * { grid-column: 1 / -1; }
@media (min-width: 1101px) {
  .block-split > .block-head { grid-column: 2 / span 4; }
  .block-split > .block-body { grid-column: 7 / span 5; }
  .block-split.wide > .block-body { grid-column: 7 / span 6; }
}

.prose p { font-size: 17px; line-height: 1.68; color: var(--ink-2); }
.prose p + p { margin-top: 18px; }

/* Dash lists — from the design's "what's included" and challenge bullets */
.dash-list { list-style: none; display: flex; flex-direction: column; gap: 14px; margin-top: 8px; }
.dash-list li { display: flex; gap: 14px; font-size: 17px; line-height: 1.5; color: var(--ink-2); }
.dash-list li::before { content: '—'; color: var(--accent); flex: none; }

/* Numbered steps */
/* 175px min lets four steps sit in one row inside a six-column body, and
   lets three stretch to fill it. */
.steps { display: grid; grid-template-columns: repeat(auto-fit, minmax(175px, 1fr)); gap: 28px; }
.steps-wide { grid-template-columns: repeat(4, 1fr); }
.step-n {
  font-family: var(--meta);
  font-variant-numeric: tabular-nums;
  font-size: 11px; letter-spacing: .2em;
  color: var(--ink-3);
  margin-bottom: 12px;
}
.steps h3 { font-size: 19px; margin-bottom: 10px; }
.steps p { font-size: 15px; line-height: 1.6; color: var(--ink-2); }

/* Label / value bar — client, year, services, timeline */
.meta-bar {
  display: grid; grid-template-columns: repeat(4, 1fr); gap: 32px;
  padding: 30px 34px;
}
.meta-k {
  font-family: var(--meta);
  font-variant-numeric: tabular-nums;
  font-size: 10px; letter-spacing: .22em; text-transform: uppercase;
  color: var(--ink-3);
  margin-bottom: 10px;
}
.meta-v { font-size: 16px; color: var(--ink); }

/* A single oversized paragraph, used as the case study overview */
.overview p {
  font-family: var(--sans);
  font-weight: 400;
  font-size: clamp(20px, 2vw, 28px);
  line-height: 1.42;
  letter-spacing: -.01em;
  color: var(--ink);
  max-width: 34ch;
}

/* Four columns of short lists — "what we delivered" */
.col-lists { display: grid; grid-template-columns: repeat(auto-fit, minmax(175px, 1fr)); gap: 28px; }
.col-lists h3 {
  font-family: var(--meta);
  font-variant-numeric: tabular-nums;
  font-size: 10px; font-weight: 400;
  letter-spacing: .22em; text-transform: uppercase;
  color: var(--ink-3);
  margin-bottom: 16px;
}
.col-lists ul { list-style: none; display: flex; flex-direction: column; gap: 10px; }
.col-lists li { font-size: 16px; color: var(--ink-2); }

/* Figures */
.figure { margin-top: 36px; }
.figure .ph { border-radius: 14px; }
.figure figcaption {
  font-family: var(--meta);
  font-variant-numeric: tabular-nums;
  font-size: 11px; letter-spacing: .16em; text-transform: uppercase;
  color: var(--ink-3);
  margin-top: 14px;
}
.figure-wide .ph { aspect-ratio: 16 / 9; }
.figure-pair { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 28px; }
.figure-pair .ph { aspect-ratio: 4 / 3; }

/* Pull quote */
.pullquote { padding: 46px 52px; }
.pullquote blockquote {
  font-family: var(--sans);
  font-size: clamp(20px, 2vw, 27px);
  line-height: 1.44; letter-spacing: -.01em;
  color: var(--ink);
  max-width: 44ch;
}
.quote-by { display: flex; align-items: center; gap: 16px; margin-top: 30px; }
.quote-by .ph { width: 46px; height: 46px; border-radius: 50%; flex: none; font-size: 8px; }
.quote-name { font-size: 15px; font-weight: 600; }
.quote-role { font-size: 14px; color: var(--ink-3); }

/* Outcome stats */
.stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(165px, 1fr)); gap: 32px; }
.stat-n {
  font-family: var(--sans);
  font-weight: 500;
  font-size: clamp(34px, 3.6vw, 52px);
  letter-spacing: -.03em;
  line-height: 1;
}
.stat-n small { font-size: .45em; color: var(--ink-3); letter-spacing: 0; }
.stat-k { font-size: 15px; color: var(--ink-2); margin-top: 12px; max-width: 22ch; }

/* Credits */
/* Explicit thirds — auto-fit would leave these three columns huddled on the
   left of a 1800px page with the rest of the row empty. */
.credits { display: grid; grid-template-columns: repeat(3, 1fr); gap: 40px; }
.credits h3 {
  font-family: var(--meta);
  font-variant-numeric: tabular-nums;
  font-size: 10px; font-weight: 400;
  letter-spacing: .22em; text-transform: uppercase;
  color: var(--ink-3);
  margin-bottom: 18px;
}
.credit-row { display: flex; flex-direction: column; gap: 2px; }
.credit-row + .credit-row { margin-top: 14px; }
.credit-name { font-size: 16px; }
.credit-role { font-size: 14px; color: var(--ink-3); }
.chips { display: flex; flex-wrap: wrap; gap: 8px; }

/* Link strip — "other things we do" */
.chip-links { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 24px; }
.chip-links a {
  font-size: 15px;
  padding: 11px 18px; border-radius: 999px;
  background: var(--surface);
  box-shadow: 0 10px 24px -20px rgba(43, 39, 33, .3);
  transition: transform .25s var(--ease), box-shadow .25s var(--ease);
}
.chip-links a:hover { transform: translateY(-2px); box-shadow: 0 14px 28px -18px rgba(43, 39, 33, .4); }

/* Gallery of projects on a service page. Ruled like the rails — the 1px gap
   over a turquoise ground draws the lines, which keeps them correct however
   the columns rewrap. */
.gallery {
  display: grid; grid-template-columns: repeat(4, 1fr);
  margin-left: calc(var(--inset) * -1);
  margin-right: calc(var(--inset) * -1);
  gap: 1px;
  background: var(--line);
}
.gallery a {
  display: flex; flex-direction: column; gap: 14px;
  background: var(--bg);
  padding: var(--inset);
}
.gallery .ph { aspect-ratio: 4 / 3; border-radius: 12px; }
.gallery span:last-child { font-family: var(--sans); font-size: 17px; }

/* CTA band and next-project link */
.cta-band {
  display: flex; align-items: flex-end; justify-content: space-between; gap: 40px;
  padding: 48px 52px;
}
.cta-band p { font-size: 17px; line-height: 1.6; color: var(--ink-2); max-width: 46ch; margin-top: 14px; }
.cta-band .btn { flex: none; }

.next-project { display: block; padding: 38px 44px; }
.next-k {
  font-family: var(--meta);
  font-variant-numeric: tabular-nums;
  font-size: 10px; letter-spacing: .22em; text-transform: uppercase;
  color: var(--ink-3);
  margin-bottom: 16px;
}
.next-row { display: flex; align-items: center; justify-content: space-between; gap: 32px; }
.next-title { font-family: var(--sans); font-size: clamp(22px, 2.2vw, 32px); }
.next-sub { font-size: 15px; color: var(--ink-2); margin-top: 8px; }
.next-arrow { font-size: 28px; flex: none; transition: transform .3s var(--ease); }
.next-project:hover .next-arrow { transform: translateX(8px); }

@media (max-width: 900px) {
  .meta-bar { grid-template-columns: repeat(2, 1fr); gap: 26px; padding: 26px 24px; }
  .credits, .gallery, .steps-wide { grid-template-columns: repeat(2, 1fr); }
  .pullquote { padding: 32px 26px; }
  .cta-band { flex-direction: column; align-items: flex-start; padding: 32px 26px; }
  .next-project { padding: 28px 24px; }
  .block { padding: 56px 0; }
  .page-head { padding: 36px 0 52px; }
}

/* --------------------------------------------------------------------------
   Responsive
   -------------------------------------------------------------------------- */

@media (max-width: 1100px) {
  :root { --gutter: 18px; --card: min(425px, 76vw); }

}

@media (max-width: 720px) {
  :root { --gutter: 12px; --inset: 16px; }

  .service-list li, .mail { font-size: 26px; }

  .credits, .gallery, .steps-wide { grid-template-columns: 1fr; }
}

/* --------------------------------------------------------------------------
   Reduced motion — every treatment above is decoration; none of it is content
   -------------------------------------------------------------------------- */

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


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

  .js .reveal:not(.in) > *:not(.dotgrid),
  .js .reveal:not(.in) .card {
    opacity: 1;
    transform: none;
  }
}
