/* ==========================================================================
   1. Tokens. Mirrors the Figma variables in joelcalvert.com (page "site"):
   collections Colors, Sizes and Case Study Colors, plus the paint and text styles.
   This section is the only place color literals are allowed. See DESIGN-SYSTEM.md.
   ========================================================================== */

@font-face {
  font-family: "SeoulHangang";
  src: url("../fonts/seoul-hangang-m.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Geist";
  src: url("../fonts/geist-300.woff2") format("woff2");
  font-weight: 300;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Geist";
  src: url("../fonts/geist-400.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

/* Brand typefaces for the case study Color and Type panels (OFL, self-hosted).
   Browsers only download these on the pages that use them. */
@font-face { font-family: "Rubik"; src: url("../fonts/brand/rubik-latin-500.woff2") format("woff2"); font-weight: 500; font-display: swap; }
@font-face { font-family: "PT Sans"; src: url("../fonts/brand/pt-sans-latin-400.woff2") format("woff2"); font-weight: 400; font-display: swap; }
@font-face { font-family: "Kode Mono"; src: url("../fonts/brand/kode-mono-latin-500.woff2") format("woff2"); font-weight: 500; font-display: swap; }
@font-face { font-family: "Piazzolla"; src: url("../fonts/brand/piazzolla-latin-400.woff2") format("woff2"); font-weight: 400; font-display: swap; }
@font-face { font-family: "Barlow"; src: url("../fonts/brand/barlow-latin-400.woff2") format("woff2"); font-weight: 400; font-display: swap; }
@font-face { font-family: "Ancizar Sans"; src: url("../fonts/brand/ancizar-sans-latin-400.woff2") format("woff2"); font-weight: 400; font-display: swap; }
@font-face { font-family: "Stoke"; src: url("../fonts/brand/stoke-latin-400.woff2") format("woff2"); font-weight: 400; font-display: swap; }

/* --beam is a registered color so it can transition when a card is hovered */
@property --beam {
  syntax: "<color>";
  inherits: true;
  initial-value: #00ffff;
}
@property --home-beam {
  syntax: "<color>";
  inherits: true;
  initial-value: #00ffff;
}
@property --cs-default {
  syntax: "<color>";
  inherits: true;
  initial-value: #191b1a;
}

:root {
  /* Colors collection */
  --color-background: #191b1a;
  --color-background-0: #191b1a00;
  --color-background-overlay: #191b1a29; /* 16% */
  --color-background-thick: #212121cc;   /* dark dark gray at 80% */
  --color-background-95: #191b1af2;      /* lightbox scrim, not in Figma */
  --color-white: #ffffff;
  --color-light-gray: #bababa;
  --color-dark-gray: #464646;
  --color-dark-dark-gray: #212121;

  /* Case Study Colors collection: default (image background) and beam */
  --advictory-default: #063450;
  --advictory-beam: #3477c3;      /* brand Light Blue (2026-10-30; was #009fff). 3.8:1 on background: borders and lines only */
  --advictory-beam-text: #528bcc; /* Light Blue lifted 15% toward white: 4.9:1, for hover and copied text */
  --lost-mary-default: #00341a; /* 2026-10-30: greener and a tad darker, away from Rosemary Lane's teal #145352 (was #003d37) */
  --lost-mary-beam: #00ff5a; /* 2026-10-30: a touch bluer (was #00ff37), close in hue to the default */
  --dantes-default: #9f2038;
  --dantes-beam: #ff0030;
  --dantes-beam-text: #ff264f; /* beam lifted 15% toward white: 4.65:1 on background, beam is 4.36:1 */
  --smmi-default: #1b3b4a;
  --smmi-beam: #3cc9bd;      /* lighter Dome Green (2026-10-30; was Goldenrod #f6bd43, muddy as a glow). About 8:1 on the background, so text uses it as is */
  --smmi-beam-text: #3cc9bd;
  --rosemary-default: #145352;
  --rosemary-beam: #00e388;

  /* Brand palettes shown in the case study Color and Type panels (from each brand guide) */
  --av-blue: #003c61;
  --av-red: #f3242b;
  --av-gold: #ffa600;
  --av-light-blue: #3477c3;
  --av-black: #202020;
  --av-dark-gray: #373737;
  --av-mid-gray: #626262;
  --av-light-gray: #d7d7d7;
  --av-extra-light-gray: #f5f5f5;
  --smmi-blue: #1b3b4a;
  --smmi-green: #00716a;
  --smmi-gold: #f6bd43;
  --dantes-red: #9f2038;
  --dantes-gold: #cc802f;
  --dantes-black: #231f20;
  --dantes-white: #ffffff;
  --swatch-ink-dark: #191b1a;   /* label text on light swatches */

  /* The live beam. Pages and hovered cards swap it (see styles.css) */
  --site-beam: #00ffff;  /* the site's own brand glow, first color of the home loop */
  --beam: var(--site-beam);
  --color-beam-0: color-mix(in srgb, var(--beam) 0%, transparent);
  --color-beam-faded: color-mix(in srgb, var(--beam) 32%, transparent);
  /* Beam used as text (hover and copied states). Same as beam except Dante's (see below) */
  --beam-text: var(--beam);

  /* Paint styles: a neutral at 90% over the beam */
  --color-text: color-mix(in srgb, #e2e2e2 90%, var(--beam));   /* faded bright beam */
  --color-border: color-mix(in srgb, #2c2c2c 90%, var(--beam)); /* faded dark beam */

  /* Sizes collection */
  /* Sizes collection, three Figma modes. Mobile first ("mobile", below 744px),
     then "desktop" (744 to 1439: tablets and small laptops) and "wide" (1440 and up). */
  --size-xxs-small: 4px;
  --size-xs-small: 8px;
  --size-x-small: 11px;   /* 12 from 744px */
  --size-small: 12px;     /* 14 */
  --size-regular: 14px;   /* 16 */
  --size-medium: 16px;    /* 24 */
  --size-large: 24px;     /* 36 */
  --size-x-large: 36px;   /* 54 */
  --size-xx-large: 36px;  /* 64 */
  --size-xxx-large: 48px; /* 64 desktop, 80 wide */
  --size-caption: 11px;   /* 12 from 744px. Caption text style (2026-10-30) */

  --radius-button: var(--size-xxs-small);
  --radius-card: var(--size-xs-small);
  --header-fade: 100px;
  --section-pad-y: 100px;
  --grid-gap: 8px;

  /* Layout widths from the Figma frames */
  --column: 400px;          /* hero and vertical card */
  --card-wide: 835px;       /* horizontal card, 1440px and up */
  --section-max: 1200px;    /* case study sections, 1440px and up */
  --hero-max: 1600px;       /* case study hero text lines up inside this, 1680px and up (2026-10-30) */

  /* Type */
  --font-display: "SeoulHangang", Georgia, serif;
  --font-body: "Geist", system-ui, -apple-system, "Segoe UI", sans-serif;
  /* Text styles are bound to the Sizes variables in Figma:
     Body = regular, Tag Regular = small, Tag Small = x small,
     Display Medium = large, Display Large = xxx large (64 at tablet and laptop). */
  --leading-body: 1.5;
  --tracking-tag: 0.24em;

  --icon-size: 16px;
  --ease: cubic-bezier(0.2, 0, 0, 1);
  --duration: 160ms;
  --duration-beam: 320ms;
}

/* Figma Sizes "desktop" mode (744 to 1439) */
@media (min-width: 744px) {
  :root {
    --size-x-small: 12px;
    --size-small: 14px;
    --size-regular: 16px;
    --size-medium: 24px;
    --size-large: 36px;
    --size-x-large: 54px;
    --size-xx-large: 64px;
    --size-xxx-large: 64px;
    --size-caption: 12px;
  }
}

/* Figma Sizes "wide" mode (1440 and up): only xxx large changes */
@media (min-width: 1440px) {
  :root { --size-xxx-large: 80px; }
}

/* ==========================================================================
   2. Styles. Mobile first.
   Breakpoints (DESIGN-SYSTEM.md section 2):
     tablet   744px   iPad mini frame (also covers 1024 to 1279)
     laptop  1280px   MacBook Air frame
     desktop 1440px   Desktop frame (horizontal cards, 80px display type)
   ========================================================================== */

/* ---------- Beam: one live accent color per page ---------- */

/* Case study pages set their own beam and image color on <html> */
.cs--advictory { --beam: var(--advictory-beam); --cs-default: var(--advictory-default); --beam-text: var(--advictory-beam-text); }
.cs--lost-mary { --beam: var(--lost-mary-beam); --cs-default: var(--lost-mary-default); }
.cs--dantes    { --beam: var(--dantes-beam);    --cs-default: var(--dantes-default); --beam-text: var(--dantes-beam-text); }
.cs--smmi      { --beam: var(--smmi-beam);      --cs-default: var(--smmi-default); --beam-text: var(--smmi-beam-text); }
.cs--rosemary  { --beam: var(--rosemary-beam);  --cs-default: var(--rosemary-default); }

/* Home cards carry their own image color and beam. A card's own hover state uses
   --card-beam, a fixed color, so the card goes straight from its resting look to its
   beam. Only page-wide details (glow, text tint) follow the transitioning --beam. */
.case-study--advictory { --cs-default: var(--advictory-default); --card-beam: var(--advictory-beam); --card-beam-text: var(--advictory-beam-text); }
.case-study--lost-mary { --cs-default: var(--lost-mary-default); --card-beam: var(--lost-mary-beam); --card-beam-text: var(--lost-mary-beam); }
.case-study--dantes    { --cs-default: var(--dantes-default);    --card-beam: var(--dantes-beam);    --card-beam-text: var(--dantes-beam-text); }
.case-study--smmi      { --cs-default: var(--smmi-default);      --card-beam: var(--smmi-beam);      --card-beam-text: var(--smmi-beam-text); }
.case-study--rosemary  { --cs-default: var(--rosemary-default);  --card-beam: var(--rosemary-beam);  --card-beam-text: var(--rosemary-beam); }

/* Home: the beam starts on the site's own blue, then drifts through the case study colors in
   the order the cards appear (2026-10-30), 6 seconds on each, 36 seconds a loop. With JavaScript (html.beam-js), js/site.js drives --home-beam instead: hovering or
   focusing a card fades straight to its color, and leaving the card picks the loop up from
   that color, so there is no jump (2026-10-30). The keyframes and the hover rules below are
   the no-JavaScript fallback.
   Text uses the beam lifted 20% toward white so it stays readable at every point. */
@keyframes home-beam {
  0%, 100% { --home-beam: var(--site-beam); }
  16.667%  { --home-beam: var(--dantes-beam); }
  33.333%  { --home-beam: var(--lost-mary-beam); }
  50%      { --home-beam: var(--advictory-beam); }
  66.667%  { --home-beam: var(--rosemary-beam); }
  83.333%  { --home-beam: var(--smmi-beam); }
}
:root:has(.home) {
  --home-beam: var(--site-beam);
  --beam: var(--home-beam);
  --beam-text: color-mix(in srgb, var(--home-beam), var(--color-white) 20%);
}
@media (prefers-reduced-motion: no-preference) {
  :root:not(.beam-js):has(.home) { animation: home-beam 36s linear infinite; }
}

/* Home: the hovered or focused card sets the beam for the whole page */
:root:not(.beam-js):has(.case-study--advictory:focus-within) { --beam: var(--advictory-beam); --beam-text: var(--advictory-beam-text); }
:root:not(.beam-js):has(.case-study--lost-mary:focus-within) { --beam: var(--lost-mary-beam); }
:root:not(.beam-js):has(.case-study--dantes:focus-within)    { --beam: var(--dantes-beam); --beam-text: var(--dantes-beam-text); }
:root:not(.beam-js):has(.case-study--smmi:focus-within)      { --beam: var(--smmi-beam); --beam-text: var(--smmi-beam-text); }
:root:not(.beam-js):has(.case-study--rosemary:focus-within)  { --beam: var(--rosemary-beam); }
@media (hover: hover) {
  :root:not(.beam-js):has(.case-study--advictory:hover) { --beam: var(--advictory-beam); --beam-text: var(--advictory-beam-text); }
  :root:not(.beam-js):has(.case-study--lost-mary:hover) { --beam: var(--lost-mary-beam); }
  :root:not(.beam-js):has(.case-study--dantes:hover)    { --beam: var(--dantes-beam); --beam-text: var(--dantes-beam-text); }
  :root:not(.beam-js):has(.case-study--smmi:hover)      { --beam: var(--smmi-beam); --beam-text: var(--smmi-beam-text); }
  :root:not(.beam-js):has(.case-study--rosemary:hover)  { --beam: var(--rosemary-beam); }
}

@media (prefers-reduced-motion: no-preference) {
  :root { transition: --beam var(--duration-beam) var(--ease); }
  :root.beam-js:has(.home) { transition: none; } /* the script eases every frame itself */
}

/* ---------- Load-in motion ----------
   A short fade and 8px rise, staggered top to bottom, under one second in total.
   Accessibility:
   - Only runs when the visitor has not asked for reduced motion.
   - CSS only. Elements are fully visible by default, so if the animation never
     runs (no CSS animation support, JS off, print) nothing stays hidden.
   - Opacity never removes content from screen readers or the tab order.
   - Fades start at 1% opacity, not 0, so browsers still count the first paint of the
     largest text or image (page speed scores treat 0% as not painted yet).
   - Images fade with no delay.
   - Only the first screen animates. Content below the fold is left alone. */
@keyframes load-rise {
  from { opacity: 0.01; transform: translateY(8px); }
}
@keyframes load-fade {
  from { opacity: 0.01; }
}

@media (prefers-reduced-motion: no-preference) {
  .hero__title,
  .hero__content,
  .hero .button-group,
  .hero__view-all,
  .home__case-studies > .case-study:nth-child(-n+3) {
    animation: load-rise 600ms var(--ease) backwards;
  }
  .hero__content                                  { animation-delay: 100ms; }
  /* Case studies: title, then the details box, then The Problem and The Solution */
  .cs-hero .cs-summary {
    animation: load-rise 600ms var(--ease) 100ms backwards;
  }
  .cs-hero .cs-visit {
    animation: load-rise 600ms var(--ease) 200ms backwards;
  }
  .hero .button-group                             { animation-delay: 200ms; }
  .hero__view-all                                 { animation-delay: 250ms; }
  .home__case-studies > .case-study:nth-child(1) { animation-delay: 150ms; }
  .home__case-studies > .case-study:nth-child(2) { animation-delay: 250ms; }
  .home__case-studies > .case-study:nth-child(3) { animation-delay: 350ms; }

  .header-nav { animation: load-fade 600ms var(--ease) 300ms backwards; }
  .glow       { animation: load-fade 1200ms var(--ease) backwards; }
  .cs-hero__image { animation: load-fade 800ms var(--ease) backwards; }
}

/* ---------- Scroll-in motion (case study sections) ----------
   Every block in the sections fades in and rises 16px as it enters the screen: figures
   (image or motion with its caption), section text, color rows and type specimens
   (2026-10-30; previously images only).
   Tied to scroll position (CSS scroll-driven animation), not time, so fast skimming is
   never slowed down, and scrolling back up shows everything already in place.
   Browsers without support, and visitors with reduced motion, see the images normally. */
@keyframes scroll-rise {
  from { opacity: 0.01; transform: translateY(16px); }
}

@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    /* Figures holding a video stay put: the moving, fading frame can stop autoplay in some
       browsers (VS Code's preview among them) */
    .cs-sections :is(.cs-figure:not(:has(video)), .cs-section > .image, .cs-section__text, .cs-overview__content > *, .cs-brand__text, .swatch-row, .specimen) {
      animation: scroll-rise linear both;
      animation-timeline: view();
      animation-range: entry 0% entry 60%;
    }
  }
}

@media print {
  .cs-sections * { animation: none !important; }
}

/* ---------- Base ---------- */

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

[hidden] { display: none !important; }

html {
  background: var(--color-background);
  color: var(--color-text);
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
}

@media (prefers-reduced-motion: no-preference) {
  html { scroll-behavior: smooth; }
}

html.has-lightbox { overflow: hidden; }

body {
  margin: 0;
  min-height: 100svh;
  font-family: var(--font-body);
  font-size: var(--size-regular);
  font-weight: 300;
  line-height: var(--leading-body);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

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

h1, h2, h3, p { margin: 0; font-weight: inherit; font-size: inherit; }

a { color: inherit; }

:focus-visible {
  outline: 2px solid var(--beam-text); /* the readable beam: at least 3:1 on every page */
  outline-offset: 2px;
  border-radius: var(--radius-button);
}

.visually-hidden {
  position: absolute !important;
  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: var(--size-regular);
  left: var(--size-regular);
  z-index: 100;
  padding: var(--size-xs-small) var(--size-regular);
  background: var(--color-background);
  color: var(--color-text);
  border: 1px solid var(--beam);
  border-radius: var(--radius-button);
  transform: translateY(-200%);
}
.skip-link:focus { transform: none; }

/* ---------- Text styles (one class per Figma style) ---------- */

.display-large,
.display-medium {
  font-family: var(--font-display);
  font-weight: 400;
  letter-spacing: 0;
  color: var(--color-white);
  overflow-wrap: break-word;
}
/* Display Large = xxx large: 48 mobile, 64 desktop, 80 wide */
.display-large  { font-size: var(--size-xxx-large); line-height: 1; }
.display-medium { font-size: var(--size-large); line-height: 1.2; }
/* Card titles use the faded bright beam, like the rest of the card text (Figma card component) */
.case-study .display-medium { color: var(--color-text); }

.tag,
.tag-small,
.body,
.body-bold {
  text-box: trim-both cap alphabetic;
  overflow-wrap: break-word;
}
.tag,
.tag-small {
  font-weight: 400;
  line-height: 1.4;
  letter-spacing: var(--tracking-tag);
  text-transform: uppercase;
}
.tag       { font-size: var(--size-small); }
.tag-small { font-size: var(--size-x-small); }
.body      { font-size: var(--size-regular); font-weight: 300; line-height: 1.5; }
.body-bold { font-size: var(--size-regular); font-weight: 400; line-height: 1.4; }
/* Caption (not in Figma yet): image captions, discipline tags, project details values */
.caption {
  font-size: var(--size-caption);
  font-weight: 400;
  line-height: 1.4;
  letter-spacing: 0.01em;
  text-box: trim-both cap alphabetic;
}

/* ---------- Page glow (28%; 2026-10-30, was 16%) ----------
   A living light source at the bottom edge of the screen on every page and size (2026-10-30;
   previously the right edge from 1280 on some pages), in two layers
   (2026-10-30, previously one breathing gradient):
   - ::before, the core: the broad glow, breathing and drifting slowly.
   - ::after, the flares: long tongues of light that lick outward and sway like a flame.
   (Rolling waves were tried and removed the same day: their bands showed hard edges at
   the top and bottom of the screen.) Loops have unrelated lengths, so the motion never
   visibly repeats. Transform and opacity only, no blur filters. Reduced motion: still. */
.glow {
  --glow-at: 50% 100%;
  --glow-soft: color-mix(in srgb, var(--beam) 45%, transparent);
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  overflow: hidden;
  opacity: 0.22; /* 2026-10-30: 0.28 was a little strong on some case studies */
}
.glow::before,
.glow::after {
  content: "";
  position: absolute;
  inset: 0;
  transform-origin: var(--glow-at);
}
.glow::before {
  background: radial-gradient(ellipse 186.3% 62% at var(--glow-at), var(--beam), var(--color-beam-0));
}
/* Flares: five tongues rising from the bottom edge */
.glow::after {
  background:
    radial-gradient(ellipse 26% 88% at 50% 100%, var(--beam), var(--glow-soft) 30%, var(--color-beam-0) 70%),
    radial-gradient(ellipse 20% 66% at 34% 100%, var(--beam), var(--glow-soft) 28%, var(--color-beam-0) 70%),
    radial-gradient(ellipse 18% 58% at 66% 100%, var(--beam), var(--glow-soft) 28%, var(--color-beam-0) 70%),
    radial-gradient(ellipse 14% 44% at 18% 100%, var(--glow-soft), var(--color-beam-0) 70%),
    radial-gradient(ellipse 14% 48% at 82% 100%, var(--glow-soft), var(--color-beam-0) 70%);
  opacity: 0.7;
}

/* Core: brightness wavers between 78% and 100%, swells up to 4% and drifts a few pixels */
@keyframes glow-flicker {
  0%   { opacity: 1; }
  11%  { opacity: 0.9; }
  19%  { opacity: 0.97; }
  34%  { opacity: 0.82; }
  46%  { opacity: 0.95; }
  58%  { opacity: 0.78; }
  71%  { opacity: 0.93; }
  83%  { opacity: 0.86; }
  100% { opacity: 1; }
}
@keyframes glow-drift {
  0%   { transform: translate(0, 0) scale(1); }
  27%  { transform: translate(-0.6%, 0.4%) scale(1.03); }
  52%  { transform: translate(0.3%, -0.5%) scale(1.01); }
  78%  { transform: translate(-0.3%, 0.2%) scale(1.04); }
  100% { transform: translate(0, 0) scale(1); }
}
/* Flares: sway around the source and stretch outward, like a flame licking upward */
@keyframes glow-flare-up {
  0%   { transform: rotate(0deg) scale(1, 1); }
  17%  { transform: rotate(-3deg) scale(1.04, 1.12); }
  33%  { transform: rotate(2deg) scale(0.97, 0.94); }
  52%  { transform: rotate(4deg) scale(1.03, 1.18); }
  68%  { transform: rotate(-2deg) scale(1, 1.02); }
  84%  { transform: rotate(-4deg) scale(1.05, 1.14); }
  100% { transform: rotate(0deg) scale(1, 1); }
}
@keyframes glow-flare-flicker {
  0%   { opacity: 0.7; }
  13%  { opacity: 0.95; }
  24%  { opacity: 0.55; }
  41%  { opacity: 0.85; }
  55%  { opacity: 0.5; }
  69%  { opacity: 1; }
  86%  { opacity: 0.6; }
  100% { opacity: 0.7; }
}

@media (prefers-reduced-motion: no-preference) {
  .glow::before {
    will-change: opacity, transform;
    animation:
      glow-flicker 7.3s ease-in-out infinite,
      glow-drift 13.7s ease-in-out infinite;
  }
  .glow::after {
    will-change: opacity, transform;
    animation:
      glow-flare-up 9.1s ease-in-out infinite,
      glow-flare-flicker 4.3s ease-in-out infinite;
  }
}

.page,
.footer-nav { position: relative; z-index: 1; }

/* ---------- button-link (Figma 459:534) ---------- */

.button-link {
  display: inline-flex;
  align-items: center;
  gap: var(--size-xxs-small);
  padding: 0;
  border: 0;
  border-radius: var(--radius-button);
  background: none;
  color: var(--color-text);
  font: inherit;
  font-weight: 300;
  line-height: 1.5;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  transition: color var(--duration) var(--ease), border-color var(--duration) var(--ease);
}
.button-link:hover,
.button-link:focus-visible { color: var(--beam-text); }

.button-link__label { text-box: trim-both cap alphabetic; }

.button-link svg {
  width: var(--icon-size);
  height: var(--icon-size);
  flex: none;
}

.button-link--box {
  padding: var(--size-xs-small) var(--size-medium);
  border: 1px solid var(--color-beam-faded);
}
.button-link--box:hover,
.button-link--box:focus-visible { border-color: var(--beam); }

.button-link--icon { padding: var(--size-xs-small); }

/* Email copy: swap the label and icon. The button sizes to the label it shows,
   so "Email" sits 4px from its icon like every other button */
.swap { display: inline-flex; }
.swap__copied { display: none; }
.is-copied .swap__default { display: none; }
.is-copied .swap__copied { display: inline; }
.is-copied svg.swap__copied { display: block; }
.button-link.is-copied { color: var(--beam-text); }
.button-link--box.is-copied { border-color: var(--beam); }

.button-group {
  display: flex;
  flex-wrap: wrap;
  gap: var(--size-regular);
}


/* ---------- heading-body group (Figma 474:2725) ---------- */

.heading-body {
  display: flex;
  flex-direction: column;
  gap: var(--size-regular);
}
/* Paragraph spacing = regular, measured like Figma's paragraph spacing: on top of the
   normal space between lines. The paragraphs are trimmed to cap height and baseline,
   so the flex gap alone looked like an ordinary line break. 1lh - 1cap adds back
   the space a line break already has, then the gap adds regular. */
.heading-body > .body + .body { margin-top: calc(1lh - 1cap); }
.heading-body__heading {
  display: flex;
  align-items: center;
  gap: var(--size-x-small);
}
.heading-body__heading::before {
  content: "";
  flex: none;
  width: var(--size-medium);
  height: 1px;
  background: linear-gradient(to left, var(--beam), var(--color-beam-0)); /* beam at the right, fading left */
}

/* ---------- hero (Figma 533:1318) ---------- */

.hero {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--size-x-large);
  width: 100%;
  max-width: var(--column);
}
.hero__title {
  display: flex;
  flex-direction: column;
  gap: var(--size-regular);
  width: 100%;
}
.hero__content {
  display: flex;
  flex-direction: column;
  gap: var(--size-large);
}

/* ---------- Project details (case study metadata, not in Figma yet) ----------
   Label (Tag Small) above value (Body), in a two-column grid under the title. */
.cs-meta {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--size-large) var(--size-large); /* rows 36px apart on desktop (was 24) */
  width: 100%;
  margin: 0;
}
.cs-meta__item {
  display: flex;
  flex-direction: column;
  gap: var(--size-x-small); /* label to value: 12px (was 8) */
  min-width: 0;
}
.cs-meta__item--wide { grid-column: 1 / -1; }
.cs-meta dt { color: var(--color-text); opacity: 0.72; }
.cs-meta dd { margin: 0; }
.cs-meta { grid-template-columns: minmax(0, 1fr) minmax(0, 2fr); }

/* ---------- Discipline tags (case study details and home cards) ----------
   One source per project: the case study's list; sync-nav.mjs copies it to the card. */
.tags {
  display: flex;
  flex-wrap: wrap;
  gap: var(--size-xs-small) 0;
  margin: 0;
  padding: 0;
  list-style: none;
}
/* Plain text separated by middle dots (2026-10-30) */
.tags > li:not(:last-child)::after {
  content: "·";
  margin: 0 0.5em;
  opacity: 0.72;
}
.case-study__heading .tags { margin-top: var(--size-xs-small); opacity: 0.72; }

/* Details box: Year, Tools, Disciplines in the card border color. The takeaway sits above it */
.cs-summary {
  display: flex;
  flex-direction: column;
  gap: var(--size-medium);
  width: 100%;
  padding: var(--size-medium); /* 24 on desktop (was 36) */
  border: 1px solid var(--color-border);
  border-radius: var(--radius-card);
}
.cs-takeaway { margin-top: var(--size-xs-small); color: var(--color-white); }

/* Case study hero: title block, details box and Visit link sit 24/36 apart (2026-10-30; 36/54 above
   the details box before that, then briefly 16/24). The text uses up to 560px from 744 and the whole column from 1280
   (was 400), so long names (Dante's, St. Matthew) wrap like the short ones instead of 4 lines. */
.cs-hero .hero { gap: var(--size-large); } /* 24 on phones, 36 from 744 (was 16/24) */
@media (min-width: 744px) { .cs-hero .hero { max-width: 560px; } }
@media (min-width: 1280px) { .cs-hero .hero { max-width: none; } }

/* Overview (2026-10-30): The Problem and The Solution, moved out of the hero so it carries
   only the title, client, takeaway, and details. First block in .cs-sections, so it gets
   the same spacing as every section. Stacked on phones, two columns from 744. */
.cs-overview { padding-bottom: var(--size-large); }
.cs-overview__content {
  display: grid;
  gap: var(--size-large);
  padding: 0 var(--size-large);
}
@media (min-width: 744px) {
  .cs-overview__content { grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: var(--grid-gap); }
}

/* ---------- Image captions (case study sections) ---------- */
.cs-figure {
  display: flex;
  flex-direction: column;
  gap: var(--size-xs-small);
  min-width: 0;
  margin: 0;
}
/* Motion media (Lottie and video): same frame as .image. A transparent button over it
   opens the lightbox, with the same hover state as images. */
.media-frame { position: relative; display: flex; min-width: 0; }
.media-frame > .media { flex: 1 1 auto; }
.media__enlarge {
  position: absolute;
  inset: 0;
  padding: 0;
  border: 0;
  border-radius: var(--radius-card);
  background: none;
  color: var(--beam);
  cursor: zoom-in;
}
.media__enlarge:focus-visible .image__overlay { opacity: 1; }
@media (hover: hover) {
  .media__enlarge:hover .image__overlay { opacity: 1; }
}
.media {
  position: relative;
  width: 100%;
  aspect-ratio: 1 / 1;
  border-radius: var(--radius-card);
  overflow: hidden;
  background: var(--cs-default);
}
.media--lottie { padding: 12%; }
.media--on-black { background: var(--dantes-black); }
.media--lottie svg { display: block; }
/* Video: centered on the case study color with some padding around it */
.media--video {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 8%;
}
.media video {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
}
.cs-figure__caption { font-weight: 300; opacity: 0.64; } /* Geist Light (was 400 at 72%) */

/* ---------- Color and Type panel (case studies) ---------- */
.cs-brand {
  display: flex;
  flex-direction: column;
  gap: var(--size-large);
}
.cs-brand__text { padding: 0 var(--size-large) var(--size-large); }
.cs-brand__panel {
  display: flex;
  flex-direction: column;
  gap: var(--size-xs-small);
}
/* Colors: the primary brand colors share the first row, full width. Everything else shares
   the second row, where secondary colors get twice the width of tertiary ones (the grays).
   Both rows always fill the width. On phones the second row becomes two columns, and an odd
   last swatch spans both so nothing sits alone at half width. */
.swatches {
  display: flex;
  flex-direction: column;
  gap: var(--size-xs-small);
}
.swatch-row,
.specimens {
  margin: 0;
  padding: 0;
  list-style: none;
}
.swatch-row {
  display: flex;
  gap: var(--size-xs-small);
}
.swatch-row > li { flex: 1 1 0; min-width: 0; display: flex; } /* swatches in a row share one height */
.swatch-row .swatch { flex: 1 1 auto; }
/* Phones: primary colors stay the largest blocks and no row is left half empty.
   Primary row: two columns; with an odd count the first color spans the full width
   (AdVictory: Blue full width, then Red and Gold). Other colors: three columns when
   the count divides by three (AdVictory's six, St. Matthew's three), otherwise two. */
@media (max-width: 743px) {
  .swatch-row {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
  .swatch-row--primary > li:first-child:nth-last-child(odd) { grid-column: 1 / -1; }
  .swatch-row:not(.swatch-row--primary):has(> li:nth-child(3n):last-child) { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .swatch-row:not(.swatch-row--primary):not(:has(> li:nth-child(3n):last-child)) > li:last-child:nth-child(odd) { grid-column: 1 / -1; }
}

/* Typefaces: stacked on phones. From 744, two columns, and with an odd count the first
   (primary heading) typeface spans the full width so the rest pair up below it. From 1280,
   all in one row. */
.specimens {
  display: grid;
  gap: var(--size-xs-small);
}
@media (min-width: 744px) {
  .specimens { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .specimens > .specimen:first-child:nth-last-child(odd) { grid-column: 1 / -1; }
}
@media (min-width: 1280px) {
  .specimens { display: flex; }
  .specimens > .specimen { flex: 1 1 0; min-width: 0; }
}

.swatch {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: var(--size-large);
  width: 100%;
  min-height: 180px; /* was 120 */
  padding: var(--size-regular);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-card);
  color: var(--color-white);
}
.swatch__hex { font-size: var(--size-regular); font-variant-numeric: tabular-nums; }
.swatch--light { color: var(--swatch-ink-dark); }

.swatch--av-blue { background: var(--av-blue); }
.swatch--av-red { background: var(--av-red); }
.swatch--av-gold { background: var(--av-gold); color: var(--swatch-ink-dark); }
.swatch--av-light-blue { background: var(--av-light-blue); }
.swatch--av-black { background: var(--av-black); }
.swatch--av-dark-gray { background: var(--av-dark-gray); }
.swatch--av-mid-gray { background: var(--av-mid-gray); }
.swatch--av-light-gray { background: var(--av-light-gray); color: var(--swatch-ink-dark); }
.swatch--av-extra-light-gray { background: var(--av-extra-light-gray); color: var(--swatch-ink-dark); }
.swatch--dantes-red { background: var(--dantes-red); }
.swatch--dantes-gold { background: var(--dantes-gold); color: var(--swatch-ink-dark); }
.swatch--dantes-black { background: var(--dantes-black); }
.swatch--dantes-white { background: var(--dantes-white); color: var(--swatch-ink-dark); }
.swatch--smmi-blue { background: var(--smmi-blue); }
.swatch--smmi-green { background: var(--smmi-green); }
.swatch--smmi-gold { background: var(--smmi-gold); color: var(--swatch-ink-dark); }

.specimen {
  display: flex;
  flex-direction: column;
  gap: var(--size-xs-small);
  padding: var(--size-regular);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-card);
  /* Solid black panel (2026-10-30, was transparent): the brand's own black where it has one,
     otherwise the site black, so the glow never shows through the type */
  background: var(--specimen-bg, var(--color-background));
}
.cs--dantes .specimen    { --specimen-bg: var(--dantes-black); }
.cs--advictory .specimen { --specimen-bg: var(--av-black); }
.specimen__aa {
  font-size: 80px;
  line-height: 1;
  color: var(--color-white);
}
.specimen__sample {
  margin-bottom: var(--size-regular);
  font-size: var(--size-medium);
  line-height: 1.2;
  color: var(--color-white);
}
.specimen__role { opacity: 0.72; }
.specimen--rubik :is(.specimen__aa, .specimen__sample) { font-family: "Rubik", var(--font-body); font-weight: 500; }
.specimen--pt-sans :is(.specimen__aa, .specimen__sample) { font-family: "PT Sans", var(--font-body); font-weight: 400; }
.specimen--kode-mono :is(.specimen__aa, .specimen__sample) { font-family: "Kode Mono", ui-monospace, monospace; font-weight: 500; }
.specimen--stoke :is(.specimen__aa, .specimen__sample) { font-family: "Stoke", Georgia, serif; font-weight: 400; }
.specimen--ancizar-sans :is(.specimen__aa, .specimen__sample) { font-family: "Ancizar Sans", var(--font-body); font-weight: 400; }
.specimen--piazzolla :is(.specimen__aa, .specimen__sample) { font-family: "Piazzolla", Georgia, serif; font-weight: 400; }
.specimen--barlow :is(.specimen__aa, .specimen__sample) { font-family: "Barlow", var(--font-body); font-weight: 400; }

.about__location { opacity: 0.72; }

/* ---------- header nav (Figma 470:1734) ---------- */

/* The track spans header + content, so the sticky header leaves with the content
   when the footer scrolls in (matches the Figma structure). */
.header-track {
  position: absolute;
  inset: 0;
  z-index: 10;
  pointer-events: none;
}

.header-nav {
  position: sticky;
  top: 0;
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  gap: var(--grid-gap);
  padding: var(--size-regular) var(--size-regular) var(--header-fade);
  background: linear-gradient(
    to bottom,
    var(--color-background) 3.846%,
    var(--color-background-0)
  );
}

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

/* Solid band behind the nav row, then the 100px fade. Without it, scrolled text ran
   under Previous / Next / Copy email (the original fade is only solid for its top 4%).
   It fades in over the first stretch of scrolling, so the hero image still runs up
   behind the nav at the top of the page. Browsers without scroll-driven animations
   show the band all the time. */
.header-nav { isolation: isolate; }
.header-nav::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background: linear-gradient(
    to bottom,
    var(--color-background) calc(100% - var(--header-fade)),
    var(--color-background-0)
  );
}
@supports (animation-timeline: scroll()) {
  .header-nav::before {
    animation: header-band linear both;
    animation-timeline: scroll(root);
    animation-range: 0 40vh;
  }
}
@keyframes header-band {
  from { opacity: 0; }
}

/* Touch targets: at least 24px tall (WCAG 2.2 target size) without changing the look */
.header-nav .button-link,
.footer-nav__links .button-link { min-height: 24px; }

.header-nav__start {
  grid-column: 1 / span 6;
  display: flex;
  align-items: center;
}
.header-nav__end {
  grid-column: 7 / span 6;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--size-medium);
}

.header-nav__name {
  text-decoration: none;
  white-space: nowrap;
  transition: color var(--duration) var(--ease);
}
.header-nav__name:hover { color: var(--beam-text); }

/* Previous only shows at laptop and up, per the Figma frames */
.header-nav__prev { display: none; }

@media (min-width: 1280px) {
  .header-nav { padding: var(--size-large) var(--size-large) var(--header-fade); }
  .header-nav__prev { display: inline-flex; }
}

/* Home: the header Copy email waits until the hero's own buttons scroll away
   (js/site.js sets .is-past-hero). It fades in, and never shows from 1280 up. */
.header-track:has(+ .home) .header-nav__end {
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--duration-beam) var(--ease), visibility 0s linear var(--duration-beam);
}
.is-past-hero .header-track:has(+ .home) .header-nav__end {
  opacity: 1;
  visibility: visible;
  transition: opacity var(--duration-beam) var(--ease), visibility 0s;
}

/* ---------- footer nav (Figma 470:1593) ---------- */

.footer-nav {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 100svh;
  padding: 0 var(--size-regular);
}

.footer-nav__inner {
  display: grid;
  justify-items: center;
  gap: var(--size-x-large);
  text-align: center;
}

.footer-nav__title {
  display: grid;
  justify-items: center;
  gap: var(--size-x-small);
}

/* The footer name uses xxx large: 48 mobile, 64 desktop, 80 wide */
.footer-nav__name { font-size: var(--size-xxx-large); }

.footer-nav__home {
  text-decoration: none;
  transition: color var(--duration) var(--ease);
}
.footer-nav__home:hover { color: var(--beam-text); }

.footer-nav__links {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--size-medium);
}
.footer-nav--home .footer-nav__links { gap: var(--size-large); }

/* ---------- case-study card (Figma 470:989) ---------- */

.case-study {
  position: relative;
  isolation: isolate;
  display: flex;
  flex-direction: column;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-card);
  overflow: hidden;
  transition: border-color var(--duration) var(--ease);
}

/* Hover glow: beam to background from the right edge, at 32% */
.case-study::before {
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(
    ellipse 100% 61% at 100% 50%,
    var(--card-beam),
    var(--color-background)
  );
  z-index: -1;
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--duration) var(--ease);
}

.case-study__image {
  position: relative;
  flex: none;
  aspect-ratio: 336 / 220;
  background: var(--cs-default);
  transition: background-color var(--duration) var(--ease);
}
.case-study__image img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Not positioned, so the title link's ::after stretches over the whole card.
   It grows to fill the card when a neighbor is taller */
.case-study__text {
  flex: 1 0 auto;
  display: flex;
  flex-direction: column;
  gap: var(--size-large);
  padding: var(--size-x-large) var(--size-large);
}

.case-study__heading {
  display: flex;
  flex-direction: column;
  gap: var(--size-x-small);
}

.case-study__link {
  color: inherit;
  text-decoration: none;
}
/* Stretch the title link over the whole card: one tab stop per card */
.case-study__link::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
}
.case-study--linked { cursor: pointer; }
.case-study__link:focus-visible { outline: none; }
.case-study--linked:has(.case-study__link:focus-visible) {
  outline: 2px solid var(--card-beam);
  outline-offset: 2px;
}

.case-study--linked:focus-within { border-color: var(--card-beam); }
.case-study--linked:focus-within::before { opacity: 0.32; }
.case-study--linked:focus-within .case-study__image { background-color: var(--card-beam); }
.case-study--linked:focus-within .case-study__cta { color: var(--card-beam-text); }
@media (hover: hover) {
  .case-study--linked:hover { border-color: var(--card-beam); }
  .case-study--linked:hover::before { opacity: 0.32; }
  .case-study--linked:hover .case-study__image { background-color: var(--card-beam); }
  .case-study--linked:hover .case-study__cta { color: var(--card-beam-text); }
}

/* Horizontal variant, 1440px and up */
@media (min-width: 1440px) {
  .case-study { flex-direction: row; }
  .case-study__image {
    flex: 1 1 0;
    aspect-ratio: auto;
    min-width: 0;
  }
  .case-study__image img { position: absolute; inset: 0; }
  .case-study__text {
    flex: none;
    width: var(--column);
    justify-content: center;
  }
}

/* ---------- Home (Figma section 512:656) ---------- */

.home__hero {
  display: flex;
  align-items: center;
  justify-content: center; /* hero block centered on phones and tablets */
  min-height: 100svh;
  padding: var(--size-x-large) var(--size-large) var(--size-large);
}

/* Grid with equal rows: every card is as tall as the tallest one */
/* Stacked cards size to their content. Equal heights only for the horizontal cards (1440 up) */
.home__case-studies {
  display: grid;
  align-items: start;
  gap: var(--size-large);
  padding: 0 var(--size-regular);
  scroll-margin-top: var(--size-x-large);
}

/* Tablet and small laptop (744 to 1279): the hero is no longer a full screen, so the
   first row of cards shows above the fold. Cards sit in two columns. */
@media (min-width: 744px) {
  .home {
    width: 100%;
    max-width: 960px;
    margin: 0 auto;
  }
  .home__hero {
    justify-content: center; /* hero block centered on tablets (2026-10-30) */
    min-height: 100svh; /* full screen height on tablets too (2026-10-30; was content height) */
    padding: calc(var(--header-fade) + var(--size-large)) var(--size-regular) var(--size-x-large);
  }
  .home__case-studies {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    padding-bottom: var(--section-pad-y);
  }
}

@media (min-width: 1280px) {
  .home {
    display: flex;
    align-items: flex-start;
    justify-content: center;
    gap: var(--size-x-large);
    width: auto;
    max-width: none;
  }
  .home__hero {
    position: sticky;
    top: 0;
    flex: none;
    width: var(--column);
    height: 100svh;
    min-height: 0;
    padding: 0;
    justify-content: center;
  }
  .home__case-studies {
    flex: none;
    grid-template-columns: none;
    width: calc(var(--column) + 2 * var(--size-regular));
    padding: var(--section-pad-y) var(--size-regular);
  }
  .hero__view-all { display: none; }
  .footer-nav--home { display: none; }
  /* The sticky hero keeps its own Copy email in view, so the header one never shows */
  .header-track:has(+ .home) .header-nav__end { display: none; }
}

@media (min-width: 1440px) {
  .home__case-studies {
    width: calc(var(--card-wide) + 2 * var(--size-regular));
    grid-auto-rows: 1fr;
    align-items: stretch;
  }
}

/* ---------- About page (not in Figma yet; built from the hero and footer parts) ---------- */

.about {
  display: flex;
  justify-content: center;
  min-height: 100svh;
  padding: calc(var(--header-fade) + var(--size-large)) var(--size-regular) var(--section-pad-y);
}
.about .hero { max-width: 560px; }
.about__photo {
  width: 160px;
  height: auto;
  aspect-ratio: 1 / 1;
  border-radius: var(--radius-card);
  object-fit: cover;
  background: var(--cs-default);
}
.about__list {
  display: flex;
  flex-direction: column;
  gap: var(--size-regular);
  margin: 0;
  padding: 0;
  list-style: none;
}

/* ---------- image (Figma 458:32) ---------- */

.image {
  position: relative;
  display: block;
  width: 100%;
  aspect-ratio: 1 / 1;
  padding: 0;
  border: 0;
  border-radius: var(--radius-card);
  overflow: hidden;
  background: var(--cs-default);
  color: var(--beam);
  cursor: zoom-in;
  transition: background-color var(--duration) var(--ease);
}
.image img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.image__overlay {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 0;
  transition: opacity var(--duration) var(--ease);
}
.image__icon {
  display: flex;
  padding: var(--size-x-small);
  border: 1px solid var(--beam);
  border-radius: 999px;
  background: var(--color-background);
  color: var(--beam);
}
.image__icon svg { width: var(--size-medium); height: var(--size-medium); }

.image:focus-visible { background-color: var(--beam); }
.image:focus-visible .image__overlay { opacity: 1; }
@media (hover: hover) {
  .image:hover { background-color: var(--beam); }
  .image:hover .image__overlay { opacity: 1; }
}

/* ---------- Case study (Figma section 512:657) ---------- */

.cs-hero {
  display: flex;
  flex-direction: column;
  min-height: 100svh; /* at least a full screen on phones and tablets (2026-10-30); the text centers in the space below the image */
}

.cs-hero__image {
  flex: none;
  height: 300px;
  aspect-ratio: auto;
  border-radius: 0;
}

.cs-hero__text {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--size-x-large) var(--size-large) var(--size-large); /* bottom matches section text padding, so hero to first section equals section to section */
}

.cs-sections {
  display: flex;
  flex-direction: column;
  gap: 72px; /* more room between sections (2026-10-30; was 24) */
  padding: 72px var(--size-regular) 0; /* the hero sits the same distance above the first section (2026-10-30) */
}

.cs-section {
  display: flex;
  flex-direction: column;
  gap: var(--size-medium); /* 16 on phones, 24 from 744: keeps each caption with its own image (was 8) */
}

.cs-section__text {
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: var(--size-large);
}
.cs-section__text .heading-body { width: 100%; }

@media (min-width: 744px) {
  .cs-hero__image { height: 600px; }
}

@media (min-width: 1280px) {
  /* Text column: 533px at laptop, a third of the screen once that is wider */
  .cs-hero {
    display: grid;
    grid-template-columns: minmax(533px, 1fr) 2fr;
    min-height: max(100svh, 640px); /* grows when the title, details and copy need more room */
  }
  /* Top padding clears the sticky header (36px padding + 43px name + 36px), so a long
     title never slides under "Joel Calvert". safe center stops tall text overflowing upward */
  .cs-hero__text {
    grid-column: 1;
    grid-row: 1;
    align-items: safe center;
    padding: calc(var(--size-large) * 3.2) var(--size-large) var(--size-large); /* 36 + 43 + 36 */
  }
  .cs-hero__image {
    grid-column: 2;
    grid-row: 1;
    height: auto;
  }

  .cs-sections { gap: 64px; padding: calc(64px + var(--size-large)) 0 0; } /* plus each section's 36px padding: 136px between sections, and from the hero image to the first section */

  .cs-section {
    display: grid;
    grid-template-columns: repeat(12, minmax(0, 1fr));
    grid-template-rows: repeat(2, minmax(0, 1fr));
    gap: var(--size-medium) var(--grid-gap); /* rows 24 apart so a caption sits with the image above it */
    min-height: 600px; /* grows if a text block needs more room */
    padding: var(--size-large);
  }
  .cs-section .image {
    aspect-ratio: auto;
    height: 100%;
    min-width: 0;
  }

  /* A: wide image, text right */
  .cs-section--wide > :is(.image, .cs-figure):nth-child(1) { grid-column: 1 / span 8; grid-row: 1 / span 2; }
  .cs-section--wide > .cs-section__text   { grid-column: 9 / span 4; grid-row: 1 / span 2; }

  /* H: lead + pair (2026-10-30, replaced the three-across layouts B, C and F). One large image
     with the text beside it, then two images at half width each, so every image has room.
     --lead-flip puts the text on the left. Images are square crops: lead 1 / 0.92, pair 4 / 3. */
  .cs-section.cs-section--lead { grid-template-rows: auto auto; min-height: 0; }
  .cs-section--lead > .cs-figure:nth-child(1) { grid-column: 1 / span 8; grid-row: 1; }
  .cs-section--lead > .cs-section__text      { grid-column: 9 / span 4; grid-row: 1; }
  .cs-section--lead > .cs-figure:nth-child(2) { grid-column: 1 / span 6; grid-row: 2; }
  .cs-section--lead > .cs-figure:nth-child(3) { grid-column: 7 / span 6; grid-row: 2; }
  .cs-section--lead-flip > .cs-figure:nth-child(1) { grid-column: 5 / span 8; }
  .cs-section--lead-flip > .cs-section__text      { grid-column: 1 / span 4; }
  .cs-section.cs-section--lead .cs-figure { height: auto; }
  .cs-section.cs-section--lead .cs-figure > .image { flex: none; height: auto; aspect-ratio: 4 / 3; }
  .cs-section.cs-section--lead .cs-figure:nth-child(1) > .image { aspect-ratio: 1 / 0.92; }

  /* D: two tall images, text right */
  .cs-section--pair > :is(.image, .cs-figure):nth-child(1) { grid-column: 1 / span 4; grid-row: 1 / span 2; }
  .cs-section--pair > :is(.image, .cs-figure):nth-child(2) { grid-column: 5 / span 4; grid-row: 1 / span 2; }
  .cs-section--pair > .cs-section__text   { grid-column: 9 / span 4; grid-row: 1 / span 2; }

  /* G: text only (a Result without a picture), centered in the grid */
  .cs-section--text { grid-template-rows: auto; min-height: 0; }
  .cs-section--text > .cs-section__text { grid-column: 5 / span 4; }

  /* E: gallery row, images only, split evenly (2 or 3 images) */
  .cs-section--gallery { grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); grid-template-columns: none; }
  .cs-section--gallery > :is(.image, .cs-figure) { grid-row: 1 / span 2; }

  /* Captioned images fill their grid cell; the caption takes its own height */
  .cs-section .cs-figure { height: 100%; min-height: 0; }
  .cs-section .cs-figure > :is(.image, .media) { flex: 1 1 0; min-height: 0; aspect-ratio: auto; }
  .cs-section .cs-figure > .media-frame { flex: 1 1 0; min-height: 0; }
  .cs-section .media-frame > .media { aspect-ratio: auto; }

  .cs-overview { padding: var(--size-large); }
  .cs-overview__content { grid-template-columns: repeat(12, minmax(0, 1fr)); }
  .cs-overview__content > :nth-child(1) { grid-column: 1 / span 5; }
  .cs-overview__content > :nth-child(2) { grid-column: 7 / span 5; }

  /* Color and Type panel: text in columns 1 to 4, panel in 5 to 12 */
  .cs-brand {
    display: grid;
    grid-template-columns: repeat(12, minmax(0, 1fr));
    gap: var(--grid-gap);
    padding: var(--size-large);
  }
  .cs-brand__panel { grid-column: 1 / -1; }
  .cs-brand__text { grid-column: 1 / -1; padding: var(--size-large) var(--size-large) 0; } /* no bottom padding: the gap below matches every other section */
}

/* Large screens (1680 and up, 2026-10-30): the text column stays put at the left edge of a
   centered 1600px area, so it lines up with the content, while the image runs from the text
   all the way to the right edge and the full height of the screen, as on laptops. (Tried
   first: the whole hero capped at 1600px with a rounded image; it felt underwhelming.) */
@media (min-width: 1680px) {
  .cs-hero { grid-template-columns: calc((100% - var(--hero-max)) / 2) minmax(533px, 600px) 1fr; }
  .cs-hero__text { grid-column: 2; }
  .cs-hero__image { grid-column: 3; }
}

@media (min-width: 1440px) {
  .cs-sections {
    max-width: var(--section-max);
    margin: 0 auto;
  }
}

/* ---------- Lightbox (not in Figma; built from existing tokens) ---------- */

.lightbox {
  width: 100vw;
  height: 100dvh;
  max-width: none;
  max-height: none;
  margin: 0;
  padding: 0;
  border: 0;
  background: var(--color-background-95);
  color: var(--color-text);
  overflow: hidden;
}
.lightbox::backdrop { background: var(--color-background); }

.lightbox[open] {
  display: grid;
  grid-template-rows: auto minmax(0, 1fr) auto auto;
}
.lightbox__caption {
  min-height: 1lh;
  padding: var(--size-regular) var(--size-regular) 0;
  text-align: center;
  opacity: 0.72;
}

.lightbox__bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--size-regular);
  padding: var(--size-regular);
}

.lightbox__stage {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 0;
  padding: 0 var(--size-regular);
  touch-action: pan-y pinch-zoom;
}

/* Images are transparent: the viewer shows them on their case study color.
   Width and height are set by js/site.js from the image's aspect ratio, so the
   display file and the full-resolution file render at exactly the same size. */
/* Animations and videos in the viewer: sized by js/site.js like images */
.lightbox__media {
  position: relative;
  border-radius: var(--radius-card);
  overflow: hidden;
  background: var(--cs-default);
}
.lightbox__media.media--lottie { padding: 6%; }
.lightbox__media.media--on-black { background: var(--dantes-black); }
.lightbox__media.media--video { padding: 6%; display: flex; align-items: center; justify-content: center; }
.lightbox__media video { display: block; width: 100%; height: 100%; object-fit: contain; }
.lightbox__img {
  display: block;
  max-width: 100%;
  max-height: 100%;
  border-radius: var(--radius-card);
  background: var(--cs-default);
  -webkit-user-select: none;
  user-select: none;
}

/* Moving between images: the old one slides a little and fades out (140ms),
   the new one fades in from the side it is coming from (260ms). */
@keyframes lightbox-open {
  from { opacity: 0; transform: scale(0.98); }
}
@media (prefers-reduced-motion: no-preference) {
  .lightbox[open] .lightbox__stage { animation: lightbox-open 220ms var(--ease); }
  .lightbox__img {
    transition: opacity 260ms var(--ease), transform 260ms var(--ease);
  }
  .lightbox__img.is-leaving {
    transition-duration: 140ms;
    opacity: 0;
    transform: translateX(-24px);
  }
  .lightbox__img.is-leaving[data-dir="prev"] { transform: translateX(24px); }
  .lightbox__img.is-entering {
    transition: none;
    opacity: 0;
    transform: translateX(24px);
  }
  .lightbox__img.is-entering[data-dir="prev"] { transform: translateX(-24px); }
}

.lightbox__controls {
  display: flex;
  justify-content: center;
  gap: var(--size-regular);
  padding: var(--size-regular);
}

@media (min-width: 1280px) {
  .lightbox__bar { padding: var(--size-large) var(--size-large) var(--size-regular); }

  .lightbox[open] {
    grid-template-rows: auto minmax(0, 1fr) auto;
  }
  .lightbox__caption { padding-bottom: var(--size-large); }
  .lightbox__stage {
    padding: 0 calc(var(--size-large) * 2 + 34px) var(--size-large);
  }
  .lightbox__controls {
    position: absolute;
    inset: 0;
    justify-content: space-between;
    align-items: center;
    padding: 0 var(--size-large);
    pointer-events: none;
  }
  .lightbox__controls .button-link { pointer-events: auto; }
}
