/* ==========================================================================
   A.MOMMENSOHN — portfolio
   Design source: Figma AJjC59xXPeEI1hn92JV7W5, node 256:1186 (1728px frame)
   All tokens below are lifted from that node. Do not guess values — measure.
   ========================================================================== */

/* --- Tokens --------------------------------------------------------------- */
:root {
  /* Colour — from Figma */
  --ink:        #212121;  /* content/content-primary   */
  --ink-2:      #616161;  /* content/content-secondary */
  --red:        #ff2d55;  /* hero accent               */
  --red-alt:    #ff3b30;  /* style "Colors/Red"        */
  --rule:       #d9d9d9;  /* header hairline           */
  --surface:    #ffffff;
  --surface-2:  #f5f5f5;

  /* Type — Syne only. The Figma used Syne Mono for the small labels, but the
     site is deliberately single-family; those now use Syne Regular. */
  --font:      "Syne", "Helvetica Neue", Arial, sans-serif;

  --fs-label:   14px;  /* COMPANIES / PROJECT eyebrows */
  --fs-body:    20px;  /* nav, hero sub-line           */
  --fs-lead:    24px;  /* wordmark, case descriptions  */
  --fs-section: 32px;  /* section headings             */
  --fs-hero:    40px;  /* hero statement               */

  /* Layout — 1728 frame: 128px gutters over a 1472px content column */
  --gutter:      128px;
  --gutter-head:  64px;  /* header uses a tighter inset */
  --content:    1472px;
  --header-h:     72px;
  --strip-h:     484px;  /* full-bleed image band       */
  --case-img-h:  386px;
  --row-gap:      32px;

  /* Motion */
  --ease: cubic-bezier(0.22, 1, 0.36, 1);
}

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

html {
  scroll-behavior: smooth;
  /* keep anchored sections clear of the sticky header */
  scroll-padding-top: var(--header-h);
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  font-family: var(--font);
  font-weight: 400;
  color: var(--ink);
  background: var(--surface);
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

img, svg { display: block; max-width: 100%; }
a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; }
p  { margin: 0; }
h1, h2, h3 { margin: 0; font-weight: 400; }
ul { margin: 0; padding: 0; list-style: none; }

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

.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

.skip-link {
  position: absolute;
  top: 8px; left: 8px;
  z-index: 100;
  padding: 8px 16px;
  background: var(--ink);
  color: #fff;
  transform: translateY(-200%);
  transition: transform 0.2s var(--ease);
}
.skip-link:focus { transform: none; }

/* --- Shared layout helpers ------------------------------------------------ */
.shell {
  width: 100%;
  max-width: calc(var(--content) + var(--gutter) * 2);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.section-title {
  font-size: var(--fs-section);
  line-height: 1.2;
  padding-block: var(--row-gap);
}

.eyebrow {
  font-size: var(--fs-label);
  line-height: 1.2;
  color: var(--ink);
}

.rule { border: 0; border-top: 1px solid var(--rule); margin: 0; }

/* ==========================================================================
   Intro
   A full-bleed panel holding the name, which then flies into the header and
   becomes the wordmark. The flight is a FLIP transform computed in JS from
   the two real element positions, so the landing is exact at any viewport
   rather than a hand-tuned guess.
   ========================================================================== */
.intro {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: grid;
  place-items: center;
  background: var(--surface);
  /* Only a scripted page shows this — see the .js gate on .text-in. */
  visibility: hidden;
}
.js .intro { visibility: visible; }

/* Everything below is em-based off the lockup's own font-size, so scaling the
   lockup by a single factor lands the name AND the bolt on their header
   counterparts at once. The header uses 8px gap on 24px type = 1/3 em, and a
   24px bolt = 1em; both are mirrored here. */
.intro__lockup {
  display: flex;
  align-items: center;
  gap: 0.33333em;
  /* A.MOMMENSOHN is a long wordmark — at 8.5vw it filled ~90% of a 1440
     viewport, leaving no margin and making the flight almost purely vertical.
     7vw leaves real breathing room on both sides. */
  font-size: clamp(26px, 7vw, 88px);
  font-weight: 700;
  line-height: 1.2;
  color: var(--ink);
  white-space: nowrap;
  transform-origin: top left;
  will-change: transform, opacity;
}
.intro__name b { font-weight: 800; }
.intro__bolt {
  width: 1em;
  height: 1em;
  flex: none;
}

/* Start state — matches the reference: up from 20px, faded out. */
.js .intro__lockup {
  opacity: 0;
  transform: translateY(20px);
}

/* Panel retires once the flight is done. */
.intro.is-done {
  visibility: hidden;
  pointer-events: none;
}

/* The header wordmark is held back until the intro hands over to it, so the
   name is never visible in two places at once. */
.js .site-header .wordmark {
  opacity: 0;
  /* Failsafe — see below. Without it a broken main.js would leave the header
     permanently blank, since only JS adds .intro-done. */
  animation: wordmark-safety 0.01s linear 4s forwards;
}
.js body.intro-done .site-header .wordmark {
  opacity: 1;
  animation: none;
  transition: opacity 0.25s var(--ease);
}
@keyframes wordmark-safety {
  to { opacity: 1; }
}

/* Failsafe, same idea as the reference's 4s loaderSafety: if the sequence
   never runs, the panel must not trap the page behind it. visibility:hidden
   hides the lockup with it, so the panel alone is enough. */
.js .intro { animation: intro-safety 0.01s linear 4s forwards; }
@keyframes intro-safety {
  to { visibility: hidden; pointer-events: none; }
}

/* ==========================================================================
   Header — Figma 256:1187. 72px tall, 64px inset, 1px bottom hairline.
   ========================================================================== */
.site-header {
  position: sticky;
  top: 0;
  z-index: 50;
  display: flex;
  flex-direction: column;
  justify-content: center;
  min-height: var(--header-h);
  padding: 16px var(--gutter-head);
  background: var(--surface);
  box-shadow: inset 0 -1px 0 0 var(--rule);
}

.site-header__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  width: 100%;
}

.wordmark {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: var(--fs-lead);
  font-weight: 700;
  line-height: 1.2;
  white-space: nowrap;
}
/* "A" and "M" are ExtraBold, the rest of the wordmark is Bold */
.wordmark b { font-weight: 800; }
.wordmark__bolt { width: 24px; height: 24px; flex: none; }
.wordmark__role { font-weight: 400; }

.site-nav ul {
  display: flex;
  align-items: center;
  gap: 16px;
  font-size: var(--fs-body);
  color: var(--ink-2);
  white-space: nowrap;
}
.site-nav a {
  position: relative;
  transition: color 0.2s var(--ease);
  /* Both children share one grid cell: a hidden bold copy of the label sets
     the width, the visible span sits on top. So switching the active item to
     bold changes no metrics and the nav never shifts under the cursor. */
  display: inline-grid;
}
.site-nav a::before {
  content: attr(data-label);
  grid-area: 1 / 1;
  font-weight: 700;
  visibility: hidden;
  pointer-events: none;
}
.site-nav a > span {
  grid-area: 1 / 1;
  transition: font-weight 0.2s var(--ease);
}
/* underline grows from the left on hover — echoes the case-row treatment */
.site-nav a::after {
  content: "";
  position: absolute;
  left: 0; bottom: -4px;
  width: 100%; height: 1px;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.3s var(--ease);
}
.site-nav a:hover,
.site-nav a[aria-current="true"] { color: var(--ink); }
.site-nav a:hover::after,
.site-nav a[aria-current="true"]::after { transform: scaleX(1); }

/* Active section reached by scrolling — the label goes Bold. */
.site-nav a[aria-current="true"] > span { font-weight: 700; }

/* Mobile nav toggle — hidden until the nav can no longer sit on one line */
.nav-toggle { display: none; }

/* ==========================================================================
   Hero — Figma 256:1210 / 260:1245
   ========================================================================== */
.hero {
  display: flex;
  align-items: center;
  min-height: 561px;
  padding-block: var(--row-gap);
}

.hero__statement {
  display: flex;
  flex-direction: column;
  gap: 4px;
  max-width: 967px;
}

.hero__title {
  font-size: var(--fs-hero);
  font-weight: 400;
  line-height: normal;
  color: var(--ink);
}

/* Each statement line is its own block so it can animate independently.
   1.2em reproduces the blank line the original <br><br> produced, which is
   what holds the title at the 240px the Figma node specifies. */
.hero__line { display: block; }
.hero__line + .hero__line { margin-top: 1.2em; }
.hero__title .accent { font-weight: 600; color: var(--red); } /* "over 12 years..."   */

/* "Andre" — plain bold text, as in the Figma node. */
.hero__title b { font-weight: 800; }

.hero__sub {
  font-family: var(--font);
  font-weight: 400;
  font-size: var(--fs-body);
  line-height: normal;
  color: var(--ink-2);
}

/* --- Full-bleed image band ------------------------------------------------
   Four equal columns, edge to edge. The artwork is deliberately taller than
   its frame so it can drift on scroll (see .parallax in js/main.js).
   -------------------------------------------------------------------------- */
.strip {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  height: var(--strip-h);
  width: 100%;
}

.strip__cell {
  position: relative;
  overflow: hidden;
  height: 100%;
}

.strip__cell img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* extra height is the parallax travel budget */
  scale: 1.18;
  will-change: transform;
}

/* ==========================================================================
   Selected cases — Figma 262:1258
   3 equal columns: logos / description / image. 32px between rows.
   ========================================================================== */
.cases__list {
  display: flex;
  flex-direction: column;
  gap: var(--row-gap);
}

.case-row {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  align-items: start;
  gap: 0;
  padding-top: var(--row-gap);
  border-top: 1px solid var(--rule);
}
.case-row:first-child { border-top: 0; padding-top: 0; }

.case-row__meta {
  display: flex;
  flex-direction: column;
  gap: 16px;
  padding-right: 24px;
}

.case-row__logos {
  display: flex;
  align-items: center;
  gap: 4px;
  height: 32px;
}
.case-row__logos img { width: auto; height: 16px; }
/* logos supplied as 32px-tall lockups keep their own height */
.case-row__logos img.is-tall { height: 32px; }
.case-row__slash { font-size: var(--fs-lead); line-height: 1; }

.case-row__body {
  display: flex;
  flex-direction: column;
  gap: 16px;
  padding-right: 24px;
}
.case-row__desc {
  max-width: 296px;
  font-size: var(--fs-lead);
  line-height: normal;
}

.case-row__media {
  position: relative;
  height: var(--case-img-h);
  overflow: hidden;
  background: var(--surface-2);
}
.case-row__media img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  scale: 1.25;
  will-change: transform;
  transition: filter 0.4s var(--ease);
}

/* Hover, part one — every row responds. This is presence, not a click
   signal: the artwork warms slightly so the row feels alive under the
   cursor. Applies whether or not the case has a destination yet. */
/* Zooms via the `scale` property, not `transform` — the parallax pass owns
   the inline transform and an inline value would win over any rule here. */
.case-row__media img { transition: filter 0.4s var(--ease), scale 0.6s var(--ease); }
.case-row:hover .case-row__media img,
.case-row:focus-within .case-row__media img { filter: saturate(1.18) contrast(1.04); }

/* Hover, part two — only rows that actually go somewhere claim to be
   clickable: the description takes the accent and a CTA slides in. Add
   `is-link` to the article and wrap the row in an <a> once a case study
   exists; the styling below activates on its own. */
.case-row.is-link { cursor: pointer; }
.case-row.is-link:hover .case-row__media img,
.case-row.is-link:focus-within .case-row__media img {
  filter: saturate(1.3) contrast(1.06);
  scale: 1.29;   /* base is 1.25 — a 3% lift on top of the parallax slack */
}
.case-row.is-link:hover .case-row__desc,
.case-row.is-link:focus-within .case-row__desc { color: var(--red); }

.case-row__cta {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-family: var(--font);
  font-weight: 400;
  font-size: 14px;
  color: var(--ink-2);
  opacity: 0;
  transform: translateX(-8px);
  transition: opacity 0.3s var(--ease), transform 0.3s var(--ease);
}
.case-row.is-link:hover .case-row__cta,
.case-row.is-link:focus-within .case-row__cta { opacity: 1; transform: none; }

/* Rows without a destination yet — flagged so the gap is visible, not hidden */
.case-row__status {
  font-family: var(--font);
  font-weight: 400;
  font-size: 14px;
  color: var(--ink-2);
}
.case-row__status::before { content: "// "; }

/* ==========================================================================
   Experience — content sourced from the LinkedIn CV export
   ========================================================================== */
.experience__list { display: flex; flex-direction: column; }

.job {
  display: grid;
  grid-template-columns: 1fr 2fr;
  gap: 0;
  padding-block: var(--row-gap);
  border-top: 1px solid var(--rule);
}

.job__company {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding-right: 24px;
}
.job__name {
  font-size: var(--fs-lead);
  font-weight: 700;
  line-height: 1.2;
}
.job__span,
.job__place {
  font-family: var(--font);
  font-weight: 400;
  font-size: var(--fs-label);
  color: var(--ink-2);
}

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

.role__header {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 4px 12px;
}
.role__title { font-size: var(--fs-lead); line-height: 1.2; }
.role__dates {
  font-family: var(--font);
  font-weight: 400;
  font-size: var(--fs-label);
  color: var(--ink-2);
}
.role__desc {
  max-width: 62ch;
  margin-top: 8px;
  font-size: 16px;
  line-height: 1.6;
  color: var(--ink-2);
}
.role__desc strong { font-weight: 600; color: var(--ink); }

/* Credentials strip — certifications, education, languages */
.credentials {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--row-gap);
  padding-block: var(--row-gap);
  border-top: 1px solid var(--rule);
}
.credentials__group { display: flex; flex-direction: column; gap: 16px; }
.credentials__group li { font-size: 16px; line-height: 1.6; color: var(--ink-2); }
.credentials__group li strong { font-weight: 600; color: var(--ink); }

/* ==========================================================================
   About me — gallery grid + copy blocks. Content is still to be written, so
   every slot is an explicitly labelled placeholder rather than filler prose.
   ========================================================================== */
.about__intro {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--row-gap);
  padding-bottom: var(--row-gap);
}
.about__lead {
  font-size: var(--fs-section);
  line-height: 1.3;
  max-width: 20ch;
}
.about__copy { display: flex; flex-direction: column; gap: 16px; max-width: 62ch; }
.about__copy p { font-size: 16px; line-height: 1.7; color: var(--ink-2); }

/* 6-cell asymmetric gallery: two wide, four square */
.gallery {
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  gap: 8px;
  padding-bottom: var(--row-gap);
}
.gallery__cell {
  position: relative;
  overflow: hidden;
  background: var(--surface-2);
  aspect-ratio: 1 / 1;
  grid-column: span 2;
}
.gallery__cell--wide { grid-column: span 3; aspect-ratio: 3 / 2; }
.gallery__cell img {
  position: absolute;
  inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  transition: transform 0.6s var(--ease);
}
.gallery__cell:hover img { transform: scale(1.04); }

/* Placeholder cell — states what belongs there and at what size */
.placeholder {
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  gap: 4px;
  padding: 16px;
  background:
    repeating-linear-gradient(45deg, #ececec 0 8px, #f5f5f5 8px 16px);
  border: 1px dashed var(--rule);
}
.placeholder__label {
  font-family: var(--font);
  font-weight: 400;
  font-size: 12px;
  line-height: 1.4;
  color: var(--ink-2);
}

/* Text placeholder blocks in About */
.text-slot {
  padding: 24px;
  border: 1px dashed var(--rule);
  background: var(--surface-2);
}
.text-slot .placeholder__label { margin-bottom: 8px; }
.text-slot p { font-size: 16px; line-height: 1.7; color: var(--ink-2); }

/* ==========================================================================
   Get in touch — click-to-copy email and phone, plus LinkedIn
   ========================================================================== */
.contact { padding-bottom: 96px; }

.contact__lead {
  font-size: var(--fs-hero);
  line-height: normal;
  max-width: 967px;
  padding-bottom: var(--row-gap);
}
.contact__lead .accent { font-weight: 600; color: var(--red); }

.contact__list {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--row-gap);
}

.contact-item {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 8px;
  padding-top: 16px;
  border-top: 1px solid var(--rule);
}
.contact-item__label {
  font-family: var(--font);
  font-weight: 400;
  font-size: var(--fs-label);
  color: var(--ink-2);
}
.contact-item__value {
  display: inline-flex;
  flex-wrap: wrap;          /* hint drops to its own line before it wraps mid-word */
  align-items: center;
  gap: 4px 12px;
  font-size: var(--fs-lead);
  line-height: 1.3;
  text-align: left;
  transition: color 0.2s var(--ease);
}
.contact-item__value:hover { color: var(--red); }

/* Copy affordance: state flips to a confirmation, then back */
.copy-hint {
  font-family: var(--font);
  font-weight: 400;
  font-size: 12px;
  white-space: nowrap;
  color: var(--ink-2);
  transition: color 0.2s var(--ease);
}
.copy-btn[data-copied="true"] .copy-hint { color: var(--red); }

.site-footer {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding-block: var(--row-gap);
  border-top: 1px solid var(--rule);
  font-family: var(--font);
  font-weight: 400;
  font-size: var(--fs-label);
  color: var(--ink-2);
}

/* ==========================================================================
   Text entry — motion blur-in, word by word
   Modelled on Jitter's "Motion Blur" template: the words resolve out of a
   heavy blur in reading order, with only a little travel. The blur carries
   the motion; the offset just gives it a direction.
   ========================================================================== */

/* The container holds everything back until the fonts land. Animating before
   then would play the whole entry on the fallback face and jump when the real
   one swapped in. */
.js .text-in {
  opacity: 0;
  /* Failsafe: the reveal is released by a JS class, so if main.js never runs
     — syntax error, blocked script, CSP — the hero would stay hidden forever.
     This shows it unconditionally after 2s. */
  animation: text-in-failsafe 0.01s linear 2s forwards;
}
.js body.is-ready .text-in {
  opacity: 1;
  animation: none;
}

@keyframes text-in-failsafe {
  to { opacity: 1; }
}

/* One animated unit per word. inline-block because transforms do not apply to
   inline boxes; the spaces between them stay as text nodes, so lines still
   break normally. */
.js .word-in {
  display: inline-block;
  opacity: 0;
  /* Same failsafe reasoning as the container — the words carry their own
     hidden state, so they need their own way out of it. */
  animation: word-in-failsafe 0.01s linear 2s forwards;
}

@keyframes word-in-failsafe {
  to { opacity: 1; }
}

.js body.is-ready .word-in {
  animation: word-blur-in 0.9s cubic-bezier(0.16, 1, 0.3, 1) both;
  animation-delay: calc(var(--w, 0) * 42ms);
}

@keyframes word-blur-in {
  from {
    opacity: 0;
    filter: blur(12px);
    transform: translateY(0.16em) scale(1.05);
  }
  to {
    opacity: 1;
    filter: blur(0);
    transform: none;
  }
}

/* ==========================================================================
   Floating back to top
   A pill that appears once the visitor has scrolled past the fold. Bottom
   centre, matching the reference: hairline border, soft shadow, fully
   rounded.
   ========================================================================== */
.to-top {
  position: fixed;
  left: 50%;
  bottom: 24px;
  z-index: 60;

  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 10px 18px;

  font-size: 14px;
  line-height: 1;
  color: var(--ink);

  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: 999px;
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.10), 0 1px 2px rgba(0, 0, 0, 0.06);

  /* Hidden but laid out, so showing it costs no layout work. Both halves of
     the transform live here because translateX is what centres it — writing
     only translateY in the visible state would knock it off centre. */
  opacity: 0;
  transform: translateX(-50%) translateY(12px);
  pointer-events: none;
  transition: opacity 0.25s var(--ease), transform 0.25s var(--ease),
              box-shadow 0.25s var(--ease);
}

.to-top.is-visible {
  opacity: 1;
  transform: translateX(-50%);
  pointer-events: auto;
}

.to-top:hover {
  box-shadow: 0 10px 28px rgba(0, 0, 0, 0.14), 0 1px 2px rgba(0, 0, 0, 0.06);
}
.to-top:hover .to-top__arrow { transform: translateY(-2px); }

.to-top__arrow {
  font-size: 15px;
  line-height: 1;
  transition: transform 0.25s var(--ease);
}

@media (max-width: 560px) {
  .to-top { bottom: 16px; padding: 9px 16px; font-size: 13px; }
}

/* ==========================================================================
   Scroll reveal — opt-in, and inert when the visitor prefers reduced motion
   ========================================================================== */
.reveal {
  opacity: 0;
  transform: translateY(24px);
  transition: opacity 0.7s var(--ease), transform 0.7s var(--ease);
}
.reveal.is-visible { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .reveal { opacity: 1; transform: none; transition: none; }
  /* The pill still appears and works, it just does not slide or nudge. */
  .to-top { transform: translateX(-50%); }
  .to-top.is-visible { transform: translateX(-50%); }
  .to-top:hover .to-top__arrow { transform: none; }
  /* Text is simply present — no blur, no rise. The name still cycles on
     click, it just swaps instantly. */
  .js .text-in,
  .js body.is-ready .text-in,
  .js .word-in,
  .js body.is-ready .word-in {
    opacity: 1;
    animation: none;
    filter: none;
    transform: none;
  }
  .strip__cell img,
  .case-row__media img { scale: 1; transform: none !important; }
  *, *::before, *::after { transition-duration: 0.01ms !important; animation-duration: 0.01ms !important; }
}

/* ==========================================================================
   Responsive — the Figma frame is 1728px. Below that the gutters and type
   scale down, then the 3-column case grid collapses to a stack.
   ========================================================================== */
@media (max-width: 1440px) {
  :root { --gutter: 80px; --gutter-head: 48px; }
}

@media (max-width: 1200px) {
  :root {
    --gutter: 48px;
    --gutter-head: 32px;
    --fs-hero: 34px;
    --fs-section: 28px;
    --fs-lead: 20px;
    --fs-body: 17px;
  }
  .hero { min-height: 460px; }
  :root { --strip-h: 380px; --case-img-h: 300px; }
}

@media (max-width: 900px) {
  :root {
    --gutter: 24px;
    --gutter-head: 24px;
    --fs-hero: 28px;
    --strip-h: 280px;
  }

  /* Nav collapses behind a toggle */
  .nav-toggle {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    font-family: var(--font);
    font-weight: 400;
    font-size: 14px;
    color: var(--ink-2);
  }
  .site-nav {
    position: absolute;
    top: 100%; left: 0; right: 0;
    background: var(--surface);
    box-shadow: inset 0 -1px 0 0 var(--rule);
    display: none;
  }
  .site-nav[data-open="true"] { display: block; }
  .site-nav ul {
    flex-direction: column;
    align-items: flex-start;
    gap: 0;
    padding: 8px var(--gutter-head) 16px;
  }
  .site-nav li { width: 100%; }
  .site-nav a { display: block; padding-block: 12px; }
  .site-nav a::after { display: none; }

  .strip { grid-template-columns: repeat(2, 1fr); height: auto; }
  .strip__cell { height: var(--strip-h); }

  .case-row,
  .job,
  .credentials,
  .about__intro,
  .contact__list { grid-template-columns: 1fr; gap: 24px; }

  .case-row__meta,
  .case-row__body,
  .job__company { padding-right: 0; }
  .case-row__desc { max-width: none; }
  .case-row__media { height: 260px; }

  .gallery { grid-template-columns: repeat(2, 1fr); }
  .gallery__cell,
  .gallery__cell--wide { grid-column: span 1; aspect-ratio: 1 / 1; }
}

@media (max-width: 560px) {
  :root { --fs-hero: 24px; --fs-section: 22px; --strip-h: 200px; }
  .wordmark { font-size: 16px; gap: 6px; }
  .wordmark__bolt { width: 16px; height: 16px; }
  /* The full lockup cannot fit beside the menu button at this width and was
     pushing the page into horizontal overflow. The role is stated in the
     hero and the page title, so drop it here rather than shrink it further. */
  .wordmark__role { display: none; }
  .contact { padding-bottom: 48px; }
}

/* Print — a recruiter hitting Cmd+P should get something readable */
@media print {
  .site-header, .strip, .nav-toggle, .case-row__cta { display: none; }
  body { color: #000; }
  .case-row, .job { break-inside: avoid; }
}
