/* ---------- View transitions (progressive enhancement) ---------- */

@view-transition {
  navigation: auto;
}
::view-transition-old(root),
::view-transition-new(root) {
  animation-duration: 0.42s;
  animation-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
}
@media (prefers-reduced-motion: reduce) {
  ::view-transition-old(root),
  ::view-transition-new(root) {
    animation: none;
  }
}

/* ---------- Fonts ---------- */

@font-face {
  font-family: "Fraunces";
  font-style: normal;
  font-weight: 300 700;
  font-display: swap;
  src: url("../fonts/fraunces-normal.woff2") format("woff2");
}
@font-face {
  font-family: "Fraunces";
  font-style: italic;
  font-weight: 300 700;
  font-display: swap;
  src: url("../fonts/fraunces-italic.woff2") format("woff2");
}
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 300 800;
  font-display: swap;
  src: url("../fonts/inter.woff2") format("woff2");
}

/* ---------- Tokens ---------- */

:root {
  --paper: #f6f1e9;
  --paper-alt: #eee5d6;
  --card: #fdfbf6;
  --ink: #201a13;
  --ink-soft: #5a4f42;
  /* was #8a7d6c — only 3.2–3.9:1 against paper/card, below WCAG AA's
     4.5:1 for the small text (captions, meta labels, TOC links) it's
     used for. This shade holds ≥4.5:1 on paper, paper-alt, and card. */
  --ink-faint: #6f6252;
  --line: rgba(32, 26, 19, 0.13);
  --line-strong: rgba(32, 26, 19, 0.24);
  --accent: #b3491f;
  --accent-ink: #7c3216;
  --accent-soft: #f1e0d3;
  --evi-tag: #5b4a8a;
  --daisy-tag: #2f6e5e;
  --fair-tag: #2f6e5e;

  --font-display: "Fraunces", "Iowan Old Style", "Palatino Linotype", serif;
  --font-body: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI",
    sans-serif;

  --content-w: 72rem;
  --narrow-w: 42rem;
  --radius: 14px;
  --radius-sm: 8px;

  --space-1: clamp(0.5rem, 0.45rem + 0.2vw, 0.75rem);
  --space-2: clamp(0.9rem, 0.8rem + 0.4vw, 1.25rem);
  --space-3: clamp(1.5rem, 1.3rem + 0.8vw, 2.25rem);
  --space-4: clamp(2.25rem, 1.9rem + 1.5vw, 3.5rem);
  --space-5: clamp(3.5rem, 2.8rem + 2.8vw, 6rem);
  --space-6: clamp(5rem, 3.8rem + 4.5vw, 9rem);
}

/* ---------- Reset ---------- */

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

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

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }
  *,
  *::before,
  *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
}

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 16px;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

img,
svg {
  max-width: 100%;
  height: auto;
  display: block;
}

a {
  color: inherit;
}

h1,
h2,
h3,
h4 {
  font-family: var(--font-display);
  font-weight: 600;
  line-height: 1.12;
  margin: 0 0 0.5em;
  letter-spacing: -0.01em;
}

p {
  margin: 0 0 1em;
}

ul,
ol {
  margin: 0 0 1em;
  padding-left: 1.25em;
}

button {
  font: inherit;
}

/* ---------- Layout helpers ---------- */

.wrap {
  max-width: var(--content-w);
  margin-inline: auto;
  padding-inline: var(--space-3);
}

.narrow {
  max-width: var(--narrow-w);
  margin-inline: auto;
}

.section {
  padding-block: var(--space-5);
}

.section-alt {
  background: var(--paper-alt);
}

.eyebrow {
  font-family: var(--font-body);
  font-weight: 600;
  font-size: 0.78rem;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--accent-ink);
  margin: 0 0 0.9em;
  display: block;
}

.skip-link {
  position: absolute;
  left: -999px;
  top: auto;
  background: var(--ink);
  color: var(--paper);
  padding: 0.9em 1.3em;
  border-radius: var(--radius-sm);
  z-index: 999;
}
.skip-link:focus {
  left: var(--space-3);
  top: var(--space-2);
}

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

/* ---------- Buttons ---------- */

.btn {
  display: inline-flex;
  align-items: center;
  gap: 0.5em;
  font-family: var(--font-body);
  font-weight: 600;
  font-size: 0.95rem;
  text-decoration: none;
  padding: 0.85em 1.5em;
  border-radius: 999px;
  border: 1px solid transparent;
  cursor: pointer;
  transition: transform 0.15s ease, background-color 0.15s ease,
    border-color 0.15s ease, color 0.15s ease;
}
.btn:hover {
  transform: translateY(-1px);
}
.btn-primary {
  background: var(--ink);
  color: var(--paper);
}
.btn-primary:hover {
  background: var(--accent-ink);
}
.btn-ghost {
  background: transparent;
  color: var(--ink);
  border-color: var(--line-strong);
}
.btn-ghost:hover {
  border-color: var(--ink);
  background: var(--card);
}

/* ---------- Header ---------- */

.site-header {
  position: sticky;
  top: 0;
  z-index: 50;
  background: rgba(246, 241, 233, 0.86);
  backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--line);
}

.site-header .wrap {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding-block: var(--space-2);
  gap: var(--space-3);
}

.logo {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: clamp(1.3rem, 1.15rem + 0.5vw, 1.6rem);
  text-decoration: none;
  color: var(--ink);
  letter-spacing: -0.01em;
  white-space: nowrap;
  flex-shrink: 0;
}

/* The <nav> landmark added around .nav-links (for screen-reader
   navigation) is otherwise-unstyled, so as a flex child of the header
   it still takes part in space-between/gap distribution even though it
   has no visible box of its own, at mobile widths .nav-links inside it
   goes position:absolute and the <nav> collapses to zero width, but
   header layout still reserved a gap on either side of that zero-width
   item, showing up as dead space to the right of the hamburger button.
   display:contents removes the <nav>'s own box from layout entirely
   (its children lay out as if directly in .site-header .wrap) while
   still keeping the landmark in the accessibility tree. */
#primary-nav {
  display: contents;
}

.nav-links {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  list-style: none;
  margin: 0;
  padding: 0;
}
.nav-links a {
  position: relative;
  text-decoration: none;
  font-weight: 500;
  font-size: 0.95rem;
  color: var(--ink-soft);
  white-space: nowrap;
  transition: color 0.2s ease;
}
.nav-links a:hover,
.nav-links a[aria-current="page"] {
  color: var(--ink);
}
/* The mobile "Book a call" link carries .btn-primary too (it's a real
   button, not a plain nav link) — .nav-links a above is more specific
   than .btn-primary alone (class+tag beats a lone class), so without
   this it silently overrode the button's white text back to the muted
   nav-link color, leaving it looking disabled against its dark fill. */
.nav-links a.btn-primary {
  color: var(--paper);
}
.nav-links a::after {
  content: "";
  position: absolute;
  left: 0;
  right: 100%;
  bottom: -5px;
  height: 1.5px;
  background: var(--accent);
  transition: right 0.3s cubic-bezier(0.16, 1, 0.3, 1);
}
.nav-links a:hover::after,
.nav-links a[aria-current="page"]::after {
  right: 0;
}

.nav-cta {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-shrink: 0;
  white-space: nowrap;
}

.nav-toggle {
  display: none;
  background: none;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  width: 44px;
  height: 44px;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}

.nav-mobile-only {
  display: none;
}

@media (max-width: 900px) {
  .nav-mobile-only {
    display: block;
  }
  .nav-links {
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    flex-direction: column;
    align-items: flex-start;
    gap: 0;
    background: var(--paper);
    border-bottom: 1px solid var(--line);
    box-shadow: 0 12px 24px -8px rgba(32, 26, 19, 0.18);
    padding: var(--space-2) var(--space-3);
    transform-origin: top;
    transform: scaleY(0);
    opacity: 0;
    pointer-events: none;
    transition: transform 0.18s ease, opacity 0.18s ease;
  }
  .nav-links a {
    padding: 0.7em 0;
    width: 100%;
  }
  .nav-links a:not(.btn) {
    border-bottom: 1px solid var(--line);
  }
  /* The accent underline (.nav-links a::after) is a "bottom:-5px, sits
     below the link" indicator that only works when there's empty space
     below it, true on the horizontal desktop nav, not here: full-width
     stacked links mean that -5px lands inside the *next* link's box,
     reading as a strikethrough through its text (visible on whichever
     item follows the current page). Current-page state still shows via
     color contrast; the animated underline just isn't useful in a list. */
  .nav-links a::after {
    display: none;
  }
  .nav-links.is-open {
    transform: scaleY(1);
    opacity: 1;
    pointer-events: auto;
  }
  .nav-toggle {
    display: inline-flex;
  }
  .nav-cta {
    display: none;
  }
  /* "Say hi" and "Book a call" now render as the same two real buttons
     the desktop nav-cta uses (previously "Say hi" was a plain list link
     and "Book a call" an orphaned button underneath it, an inconsistent,
     unfinished-looking pair). A border-top + extra top padding sets this
     row apart from the plain links above; each .btn is reset off the
     generic .nav-links a rule (which would otherwise flatten its padding
     and force it full-width) and given equal width via flex: 1. */
  .nav-mobile-actions {
    display: flex;
    gap: var(--space-2);
    width: 100%;
    margin-top: var(--space-2);
    padding-top: var(--space-3);
    border-top: 1px solid var(--line);
  }
  .nav-mobile-actions .btn {
    flex: 1;
    width: auto;
    padding: 0.85em 1.2em;
    justify-content: center;
  }
}

/* ---------- Hero ---------- */

.hero {
  /* Scales with viewport height, not just width — on a short laptop
     window this collapses instead of pushing the headline and CTA
     buttons below the fold (was a flat clamp up to 9rem/144px). */
  padding-block: clamp(1.5rem, 1rem + 4vh, 4.5rem) var(--space-5);
}
/* Same fix as .hero: the About page's opening section was still using
   the plain .section padding (flat clamp, up to 96px regardless of
   window height), which reproduced the same "gap on top" problem. */
.about-hero {
  padding-block: clamp(1.5rem, 1rem + 4vh, 4.5rem) var(--space-5);
}
.hero-grid {
  display: grid;
  grid-template-columns: 1.55fr 1fr;
  gap: var(--space-5);
  align-items: center;
}
.hero h1 {
  /* Capped lower than other display headings, and the extra text-column
     width above buys fewer wrapped lines — a long headline here was
     eating 5 lines of vertical space at wide-but-short viewports. */
  font-size: clamp(2.2rem, 1.7rem + 2vw, 3.3rem);
  margin-bottom: 0.35em;
}
.hero .lede {
  font-size: 1.15rem;
  color: var(--ink-soft);
  max-width: 38em;
  margin-bottom: 1.3em;
}
.hero-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  align-items: center;
}
.status-pill {
  display: inline-flex;
  align-items: center;
  gap: 0.5em;
  font-size: 0.85rem;
  font-weight: 500;
  color: var(--ink-soft);
}
.status-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: #3f8f5f;
  box-shadow: 0 0 0 3px rgba(63, 143, 95, 0.18);
}
.hero-portrait {
  position: relative;
  border-radius: var(--radius);
  overflow: hidden;
  aspect-ratio: 4/5;
  background: var(--ink);
}
.hero-portrait img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

@media (max-width: 860px) {
  .hero-grid {
    grid-template-columns: 1fr;
  }
  .hero-portrait {
    /* Visual order only (DOM/reading order is untouched, so this
       doesn't affect screen readers) — on a real phone the text block
       alone runs the full viewport height, so with the portrait after
       it in source order it never appeared without scrolling first.
       Moving it above the text puts a photo on screen immediately. */
    order: -1;
    max-width: 320px;
    aspect-ratio: 1/1;
  }
}

/* ---------- Stats strip ---------- */

.stats {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--space-3);
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
  padding-block: var(--space-4);
}
.stat b {
  display: block;
  font-family: var(--font-display);
  font-size: clamp(1.7rem, 1.4rem + 1.2vw, 2.5rem);
  font-weight: 600;
  color: var(--accent-ink);
  line-height: 1;
  margin-bottom: 0.3em;
}
.stat span {
  font-size: 0.9rem;
  color: var(--ink-soft);
}
@media (max-width: 720px) {
  .stats {
    grid-template-columns: repeat(2, 1fr);
  }
}

/* ---------- Section headings ---------- */

.section-head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--space-3);
  margin-bottom: var(--space-4);
}
.section-head h2 {
  font-size: clamp(1.7rem, 1.5rem + 1vw, 2.4rem);
  margin: 0;
}
.section-head .lede {
  color: var(--ink-soft);
  max-width: 34em;
  margin-top: 0.4em;
  margin-bottom: 0;
}

/* ---------- Case cards ---------- */

.case-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--space-3);
}

.case-card {
  display: flex;
  flex-direction: column;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
  text-decoration: none;
  color: var(--ink);
  transition: transform 0.18s ease, box-shadow 0.18s ease, border-color 0.18s ease;
}
.case-card:hover {
  transform: translateY(-3px);
  border-color: var(--line-strong);
  box-shadow: 0 18px 34px -22px rgba(32, 26, 19, 0.35);
}
.case-card.is-flagship {
  grid-column: span 2;
  display: grid;
  grid-template-columns: 1.1fr 1fr;
}
.case-card.is-flagship:hover {
  transform: none;
}
.case-media {
  background: var(--paper-alt);
  aspect-ratio: 16/10;
  overflow: hidden;
  position: relative;
}
.case-card.is-flagship .case-media {
  aspect-ratio: auto;
  height: 420px;
}
.case-media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: top;
  transition: transform 0.4s ease;
}
.case-card:hover .case-media img {
  transform: scale(1.03);
}
.case-body {
  padding: var(--space-3);
  display: flex;
  flex-direction: column;
  flex: 1;
}
.case-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4em;
  margin-bottom: 0.9em;
}
.tag {
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  padding: 0.35em 0.7em;
  border-radius: 999px;
  background: var(--accent-soft);
  color: var(--accent-ink);
}
.tag.tag-muted {
  background: transparent;
  border: 1px solid var(--line-strong);
  color: var(--ink-soft);
}
.case-card.is-flagship .case-tags .tag:first-child {
  background: var(--ink);
  color: var(--paper);
}
.case-body h3 {
  font-size: 1.3rem;
  margin-bottom: 0.4em;
}
.case-card.is-flagship .case-body h3 {
  font-size: clamp(1.5rem, 1.3rem + 0.8vw, 1.9rem);
}
.case-body p {
  color: var(--ink-soft);
  font-size: 0.95rem;
  margin-bottom: 1.2em;
}
.case-outcome {
  margin-top: auto;
  padding-top: 0.9em;
  border-top: 1px dashed var(--line-strong);
  font-size: 0.88rem;
  font-weight: 600;
  color: var(--accent-ink);
  display: flex;
  align-items: center;
  gap: 0.5em;
}
.case-card.is-coming-soon {
  opacity: 0.72;
}
.case-card.is-coming-soon .case-media {
  filter: grayscale(45%);
}

@media (max-width: 860px) {
  .case-grid {
    grid-template-columns: 1fr;
  }
  .case-card.is-flagship {
    grid-column: span 1;
    grid-template-columns: 1fr;
  }
  .case-card.is-flagship .case-media {
    min-height: 0;
  }
}

/* ---------- Case study page ---------- */

.case-hero {
  padding-block: var(--space-5) var(--space-4);
}
.case-hero .eyebrow-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5em;
  margin-bottom: var(--space-2);
}
.case-hero h1 {
  font-size: clamp(2.1rem, 1.7rem + 2vw, 3.3rem);
  max-width: 18em;
}
.case-hero .dek {
  font-size: 1.15rem;
  color: var(--ink-soft);
  max-width: 42em;
}

.meta-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: var(--space-3);
  padding-block: var(--space-4);
  border-top: 1px solid var(--line);
  margin-top: var(--space-4);
}
.meta-grid dt {
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-faint);
  margin-bottom: 0.4em;
}
.meta-grid dd {
  margin: 0;
  font-size: 0.98rem;
}

.case-cover {
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--paper-alt);
}

/* A handful of case covers are a single narrow phone screenshot rather
   than a wide composite — stretched across the full content width they
   blow up into an oversized, cropped-looking mess. Cap and centre those
   instead (marked by hand in the HTML with this modifier, since CSS has
   no way to know an image's intrinsic aspect ratio ahead of load). */
.case-cover.is-portrait {
  max-width: min(22rem, 70%);
  margin-inline: auto;
  padding-block: var(--space-3);
}
.case-cover.is-portrait img {
  border-radius: var(--radius-sm);
}

/* in-page TOC */
.case-toc {
  position: sticky;
  top: 5rem;
  align-self: start;
}
.case-toc-list {
  list-style: none;
  margin: 0;
  padding: 0;
  border-left: 2px solid var(--line);
}
.case-toc-list a {
  display: block;
  text-decoration: none;
  font-size: 0.9rem;
  color: var(--ink-faint);
  padding: 0.45em 0 0.45em 1em;
  margin-left: -2px;
  border-left: 2px solid transparent;
  transition: color 0.15s ease, border-color 0.15s ease;
}
.case-toc-list a:hover {
  color: var(--ink);
}
.case-toc-list a.is-active {
  color: var(--accent-ink);
  border-left-color: var(--accent);
  font-weight: 600;
}

.case-layout {
  display: grid;
  grid-template-columns: 180px 1fr;
  gap: var(--space-5);
}
@media (max-width: 900px) {
  .case-layout {
    grid-template-columns: 1fr;
  }
  .case-toc {
    display: none;
  }
}

.case-copy {
  max-width: 42rem;
}
.case-copy h2 {
  font-size: clamp(1.5rem, 1.3rem + 0.7vw, 1.9rem);
  margin-top: 2.2em;
  scroll-margin-top: 5.5rem;
}
.case-copy h3 {
  font-size: 1.15rem;
  margin-top: 1.8em;
}
.case-copy p,
.case-copy li {
  color: var(--ink-soft);
}
.case-copy p {
  font-size: 1.02rem;
}
.case-copy strong {
  color: var(--ink);
}
.case-section {
  scroll-margin-top: 5.5rem;
}

.pull {
  font-family: var(--font-display);
  font-style: italic;
  font-size: 1.4rem;
  line-height: 1.4;
  color: var(--ink);
  border-left: 3px solid var(--accent);
  padding-left: 1em;
  margin: 1.6em 0;
}

.figure {
  margin: 2em 0;
}
.figure img {
  border-radius: var(--radius-sm);
  border: 1px solid var(--line);
}
.figure figcaption {
  font-size: 0.85rem;
  color: var(--ink-faint);
  margin-top: 0.7em;
}

/* A few figures are a single narrow portrait screenshot rather than the
   wide composites most figures are — full case-copy width (42rem) turns
   those into an oddly tall column that eats a lot of scroll for one
   image. Cap and centre those instead, same idea as .case-cover.is-
   portrait. Width only: height stays auto, so the image's real aspect
   ratio is untouched, it's just smaller, not cropped or stretched. */
.figure.is-portrait img {
  display: block;
  max-width: min(20rem, 80%);
  margin-inline: auto;
}
.figure.is-portrait figcaption {
  text-align: center;
}
/* Lighter touch for a composite that's tall for a real reason (several
   stacked screens with text worth reading) rather than being one small
   subject stretched across the full column — shrink it less, so the
   in-screen copy doesn't shrink into unreadable. */
.figure.is-tall img {
  display: block;
  max-width: min(28rem, 90%);
  margin-inline: auto;
}
.figure.is-tall figcaption {
  text-align: center;
}

.stat-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: var(--space-3);
  margin: 2em 0;
  padding: var(--space-3);
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}
.stat-row .stat b {
  font-size: clamp(1.5rem, 1.3rem + 0.8vw, 2.1rem);
}

.callout {
  margin: 2em 0;
  padding: var(--space-3);
  border-radius: var(--radius);
  border: 1px solid var(--line-strong);
  background: var(--paper-alt);
}
.callout .eyebrow {
  margin-bottom: 0.6em;
}

.learnings {
  margin: 2.4em 0;
  padding: var(--space-3);
  border-radius: var(--radius);
  background: var(--ink);
  color: var(--paper);
}
.learnings .eyebrow {
  color: var(--accent-soft);
}
.learnings h3 {
  color: var(--paper);
  font-size: 1.1rem;
  margin-top: 1.2em;
}
.learnings h3:first-of-type {
  margin-top: 0;
}
.learnings p,
.learnings li {
  color: rgba(246, 241, 233, 0.82);
}
.learnings strong {
  color: var(--paper);
}

.next-case {
  border-top: 1px solid var(--line);
  padding-block: var(--space-5);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  text-decoration: none;
  color: var(--ink);
}
.next-case .eyebrow {
  margin-bottom: 0.3em;
}
.next-case h3 {
  margin: 0;
  font-size: clamp(1.4rem, 1.2rem + 1vw, 2rem);
}
.next-case .arrow {
  font-size: 1.8rem;
  flex-shrink: 0;
  transition: transform 0.15s ease;
}
.next-case:hover .arrow {
  transform: translateX(6px);
}

/* ---------- About page ---------- */

.about-grid {
  display: grid;
  grid-template-columns: 1fr 1.3fr;
  gap: var(--space-5);
  align-items: start;
}
.about-portrait {
  position: sticky;
  top: 6rem;
  border-radius: var(--radius);
  overflow: hidden;
  aspect-ratio: 4/5;
  background: var(--ink);
}
.about-portrait img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Slow, ambient frost-glint loop on the About portrait — a light
   catches-the-ice sweep that never stops, distinct from the one-shot
   entrance reveal above. Liked, but parked in favor of the simpler TV-
   static effect below; kept here (inert) in case it comes back.
.about-portrait::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(
    115deg,
    transparent 30%,
    rgba(255, 255, 255, 0.3) 46%,
    rgba(255, 255, 255, 0.5) 50%,
    rgba(255, 255, 255, 0.3) 54%,
    transparent 70%
  );
  background-size: 250% 250%;
  mix-blend-mode: overlay;
  pointer-events: none;
  animation: frost-glint 11s ease-in-out infinite;
}
@keyframes frost-glint {
  0%, 100% {
    background-position: 115% 115%;
  }
  50% {
    background-position: -15% -15%;
  }
}
*/

/* Active in its place: a light TV-static flicker, like the snow on a
   dead channel. An SVG feTurbulence noise tile (desaturated to grayscale
   via feColorMatrix — raw turbulence is colored per-channel and reads as
   faint rainbow speckle, not static, without it), nudged to a new offset
   every few frames via steps() so it reads as flicker rather than a
   smooth slide. Plain opacity, no blend mode — blend modes like overlay
   wash out to nearly nothing on this photo and vary a lot by browser;
   a flat low-opacity layer is what actually stays visible everywhere. */
.about-portrait::after {
  content: "";
  position: absolute;
  inset: 0;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='200' viewBox='0 0 200 200'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='1.05' numOctaves='2' stitchTiles='stitch' result='t'/%3E%3CfeColorMatrix in='t' type='saturate' values='0'/%3E%3CfeComponentTransfer%3E%3CfeFuncR type='linear' slope='2.2' intercept='-0.6'/%3E%3CfeFuncG type='linear' slope='2.2' intercept='-0.6'/%3E%3CfeFuncB type='linear' slope='2.2' intercept='-0.6'/%3E%3C/feComponentTransfer%3E%3C/filter%3E%3Crect width='200' height='200' filter='url(%23n)'/%3E%3C/svg%3E");
  background-size: 200px 200px;
  opacity: 0.3;
  pointer-events: none;
  animation: tv-static 0.5s steps(4) infinite;
}
@keyframes tv-static {
  0% { transform: translate(0, 0); }
  20% { transform: translate(-3%, 2%); }
  40% { transform: translate(2%, -3%); }
  60% { transform: translate(-2%, -2%); }
  80% { transform: translate(3%, 3%); }
  100% { transform: translate(0, 0); }
}

/* Same idea, warm instead of cold — a slow amber glint drifting over the
   watercolour, echoing the "dusk" light it depicts. */
.dusk-glow {
  position: relative;
  overflow: hidden;
  border-radius: var(--radius-sm);
}
.dusk-glow img {
  display: block;
}
.dusk-glow::after {
  content: "";
  position: absolute;
  inset: 0;
  /* wide/short box (the painting is a ~2.6:1 landscape) reads better as a
     horizontal glint than the portrait's corner-to-corner diagonal —
     background-size is 100% tall so the band always spans the full image
     height regardless of the element's own aspect ratio. */
  background: linear-gradient(
    100deg,
    transparent 42%,
    rgba(255, 214, 150, 0.6) 48%,
    rgba(255, 240, 210, 0.9) 50%,
    rgba(255, 214, 150, 0.6) 52%,
    transparent 58%
  );
  background-size: 320% 100%;
  mix-blend-mode: overlay;
  pointer-events: none;
  animation: dusk-glint 14s ease-in-out infinite;
}
@keyframes dusk-glint {
  0%, 100% {
    background-position: 145% 50%;
  }
  50% {
    background-position: -45% 50%;
  }
}
.about-copy p {
  font-size: 1.05rem;
  color: var(--ink-soft);
}
@media (max-width: 860px) {
  .about-grid {
    grid-template-columns: 1fr;
  }
  .about-portrait {
    position: relative;
    top: 0;
    max-width: 320px;
  }
}

.personality-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--space-2);
  margin-top: var(--space-3);
}
.personality-item {
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: var(--space-2);
  font-size: 0.95rem;
  color: var(--ink-soft);
}
.personality-item b {
  display: block;
  font-family: var(--font-display);
  font-size: 1.05rem;
  color: var(--ink);
  margin-bottom: 0.2em;
}
@media (max-width: 560px) {
  .personality-grid {
    grid-template-columns: 1fr;
  }
}

.gallery {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-2);
  margin-top: var(--space-3);
}
.gallery img {
  border-radius: var(--radius-sm);
  aspect-ratio: 4/3;
  object-fit: cover;
  width: 100%;
}
@media (max-width: 640px) {
  .gallery {
    grid-template-columns: repeat(2, 1fr);
  }
}

/* ---------- Work index ---------- */

.work-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}
.work-row {
  display: grid;
  grid-template-columns: 180px 1fr auto;
  gap: var(--space-3);
  align-items: center;
  padding: var(--space-2);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  text-decoration: none;
  color: var(--ink);
  background: var(--card);
  transition: border-color 0.15s ease, transform 0.15s ease;
}
.work-row:hover {
  border-color: var(--line-strong);
  transform: translateX(3px);
}
.work-row .case-media {
  aspect-ratio: 4/3;
  border-radius: var(--radius-sm);
}
.work-row h3 {
  font-size: 1.2rem;
  margin-bottom: 0.3em;
}
.work-row p {
  margin: 0;
  font-size: 0.92rem;
  color: var(--ink-soft);
}
.work-row .arrow {
  font-size: 1.5rem;
  color: var(--ink-faint);
}
@media (max-width: 640px) {
  .work-row {
    grid-template-columns: 1fr;
  }
  .work-row .arrow {
    display: none;
  }
}

/* ---------- Philosophy / quote block ---------- */

.quote-block {
  text-align: center;
}
.quote-block blockquote {
  font-family: var(--font-display);
  font-style: italic;
  font-size: clamp(1.5rem, 1.2rem + 1.6vw, 2.3rem);
  line-height: 1.35;
  margin: 0 auto 0.6em;
  max-width: 30em;
}
.quote-block cite {
  font-style: normal;
  color: var(--ink-faint);
  font-size: 0.95rem;
}

/* ---------- Contact / CTA ---------- */

.cta-block {
  text-align: center;
  padding-block: var(--space-6);
}
.cta-block h2 {
  font-size: clamp(2rem, 1.7rem + 1.6vw, 3rem);
}
.cta-actions {
  display: flex;
  justify-content: center;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-top: var(--space-3);
}

/* ---------- Footer ---------- */

.site-footer {
  border-top: 1px solid var(--line);
  padding-block: var(--space-4);
}
.site-footer .wrap {
  display: flex;
  justify-content: space-between;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-2);
}
.footer-links {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-3);
  list-style: none;
  margin: 0;
  padding: 0;
  max-width: 100%;
}
.footer-links a {
  overflow-wrap: anywhere;
}
.footer-links a {
  text-decoration: none;
  color: var(--ink-soft);
  font-size: 0.9rem;
}
.footer-links a:hover {
  color: var(--ink);
}
.footer-fine {
  font-size: 0.82rem;
  color: var(--ink-faint);
}

/* ---------- Reveal-on-scroll ---------- */

.reveal {
  opacity: 0;
  transform: translateY(18px);
  transition: opacity 0.7s cubic-bezier(0.16, 1, 0.3, 1),
    transform 0.7s cubic-bezier(0.16, 1, 0.3, 1);
}
.reveal.is-visible {
  opacity: 1;
  transform: none;
}

/* Cards in a grid cascade in rather than arriving all at once. */
.case-grid .case-card.reveal {
  transition-delay: calc(var(--stagger, 0) * 70ms);
}
.case-grid .case-card:nth-child(1) {
  --stagger: 0;
}
.case-grid .case-card:nth-child(2) {
  --stagger: 1;
}
.case-grid .case-card:nth-child(3) {
  --stagger: 2;
}
.case-grid .case-card:nth-child(4) {
  --stagger: 3;
}
.case-grid .case-card:nth-child(5) {
  --stagger: 4;
}
.case-grid .case-card:nth-child(6) {
  --stagger: 5;
}
.work-list .work-row.reveal {
  transition-delay: calc(var(--stagger, 0) * 60ms);
}
.work-list .work-row:nth-child(1) {
  --stagger: 0;
}
.work-list .work-row:nth-child(2) {
  --stagger: 1;
}
.work-list .work-row:nth-child(3) {
  --stagger: 2;
}
.work-list .work-row:nth-child(4) {
  --stagger: 3;
}
.work-list .work-row:nth-child(5) {
  --stagger: 4;
}
.work-list .work-row:nth-child(6) {
  --stagger: 5;
}

/* Signature photography treatment: imagery settles from a desaturated,
   slightly enlarged state into full colour at rest, on entrance — used
   on portraits and case-study figures, not on utility UI thumbnails. */
.hero-portrait img,
.about-portrait img,
.case-cover img,
.figure img {
  transition: filter 1.1s cubic-bezier(0.16, 1, 0.3, 1),
    transform 1.1s cubic-bezier(0.16, 1, 0.3, 1);
}
.case-cover.reveal img,
.figure.reveal img {
  filter: grayscale(1) saturate(0.6);
  transform: scale(1.045);
}
.case-cover.reveal.is-visible img,
.figure.reveal.is-visible img {
  filter: grayscale(0) saturate(1);
  transform: scale(1);
}

/* ---------- Load-in choreography (above-the-fold, plays on paint —
   not scroll-gated, so it never depends on JS having run yet) ---------- */

@keyframes rise-in {
  from {
    opacity: 0;
    transform: translateY(22px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}
@keyframes settle-in {
  from {
    opacity: 0;
    filter: grayscale(1) saturate(0.5);
    transform: scale(1.06);
  }
  to {
    opacity: 1;
    filter: grayscale(0) saturate(1);
    transform: scale(1);
  }
}

.hero .eyebrow {
  animation: rise-in 0.7s cubic-bezier(0.16, 1, 0.3, 1) both;
}
.hero h1 {
  animation: rise-in 0.8s cubic-bezier(0.16, 1, 0.3, 1) 0.08s both;
}
.hero .lede {
  animation: rise-in 0.8s cubic-bezier(0.16, 1, 0.3, 1) 0.16s both;
}
.hero .hero-actions {
  animation: rise-in 0.8s cubic-bezier(0.16, 1, 0.3, 1) 0.24s both;
}
.hero-portrait {
  animation: rise-in 0.9s cubic-bezier(0.16, 1, 0.3, 1) 0.1s both;
}
.hero-portrait img {
  animation: settle-in 1.3s cubic-bezier(0.16, 1, 0.3, 1) 0.2s both;
}

.case-hero .eyebrow-row {
  animation: rise-in 0.7s cubic-bezier(0.16, 1, 0.3, 1) both;
}
.case-hero h1 {
  animation: rise-in 0.8s cubic-bezier(0.16, 1, 0.3, 1) 0.08s both;
}
.case-hero .dek {
  animation: rise-in 0.8s cubic-bezier(0.16, 1, 0.3, 1) 0.16s both;
}
.case-hero .meta-grid {
  animation: rise-in 0.8s cubic-bezier(0.16, 1, 0.3, 1) 0.24s both;
}
.case-cover {
  animation: rise-in 0.9s cubic-bezier(0.16, 1, 0.3, 1) 0.28s both;
}
.case-cover img {
  animation: settle-in 1.3s cubic-bezier(0.16, 1, 0.3, 1) 0.36s both;
  /* the scroll-reveal filter/transform rules above are redundant with
     this on first paint but take back over correctly if this cover is
     ever re-entered via the .reveal system (e.g. back-forward cache) */
}

.about-portrait {
  animation: rise-in 0.9s cubic-bezier(0.16, 1, 0.3, 1) both;
}
.about-portrait img {
  animation: settle-in 1.3s cubic-bezier(0.16, 1, 0.3, 1) 0.15s both;
}
.about-copy > h1 {
  animation: rise-in 0.7s cubic-bezier(0.16, 1, 0.3, 1) both;
}
.about-copy > p:first-of-type {
  animation: rise-in 0.8s cubic-bezier(0.16, 1, 0.3, 1) 0.1s both;
}

/* ---------- Reading progress (case studies) ---------- */

.reading-progress {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  height: 3px;
  z-index: 60;
  pointer-events: none;
  background: transparent;
}
.reading-progress-bar {
  height: 100%;
  width: 0%;
  background: var(--accent);
}

@media (prefers-reduced-motion: reduce) {
  .reveal,
  .hero .eyebrow,
  .hero h1,
  .hero .lede,
  .hero .hero-actions,
  .hero-portrait,
  .hero-portrait img,
  .case-hero .eyebrow-row,
  .case-hero h1,
  .case-hero .dek,
  .case-hero .meta-grid,
  .case-cover,
  .case-cover img,
  .about-portrait,
  .about-portrait img,
  .about-copy > h1,
  .about-copy > p:first-of-type {
    opacity: 1;
    transform: none;
    filter: none;
    animation: none;
    transition: none;
  }
  .about-portrait::after,
  .dusk-glow::after {
    animation: none;
    opacity: 0;
  }
}

/* ---------- Misc ---------- */

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