/* ============================================================
   Natan Finol — Portfolio
   Editorial, typography-led, black & white, off-grid.
   ============================================================ */

@import url('https://fonts.googleapis.com/css2?family=Funnel+Display:wght@400;500;600;700&family=Zalando+Sans:ital,wght@0,300;0,400;0,500;0,600;1,400&display=swap');

:root {
  --black: #0a0a0a;
  --white: #ffffff;
  --hairline: #dcdcdc;
  --hairline-dark: #2b2b2b;
  --gray: #6f6f6f;

  --font-display: 'Funnel Display', 'Zalando Sans', sans-serif;
  --font-body: 'Zalando Sans', -apple-system, BlinkMacSystemFont, sans-serif;

  --ease-out: cubic-bezier(0.23, 1, 0.32, 1);
  --ease-in-out: cubic-bezier(0.77, 0, 0.175, 1);

  --container-pad: clamp(24px, 9vw, 160px);
  --carousel-pad: clamp(20px, 5vw, 64px);
}

* { box-sizing: border-box; }

html {
  scroll-behavior: auto;
}

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

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

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

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

p { margin: 0; }

.container {
  padding-left: var(--container-pad);
  padding-right: var(--container-pad);
}

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

/* ---------- Reveal-on-scroll base state ---------- */
.reveal {
  opacity: 0;
  transform: translateY(24px);
}
.reveal.is-ready {
  transition: opacity 700ms var(--ease-out), transform 700ms var(--ease-out);
}
.reveal.is-visible {
  opacity: 1;
  transform: translateY(0);
}

@media (prefers-reduced-motion: reduce) {
  .reveal { transform: none; }
  .reveal.is-ready { transition: opacity 400ms ease; }
}

/* ============================================================
   Header
   ============================================================ */
.site-header {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 22px var(--container-pad);
  mix-blend-mode: difference;
  color: var(--white);
  pointer-events: none;
}
.site-header a,
.site-header nav { pointer-events: auto; }

.site-header .logotype {
  font-family: var(--font-display);
  font-size: 15px;
  font-weight: 600;
  letter-spacing: 0.02em;
}

.site-nav {
  display: flex;
  gap: clamp(16px, 3vw, 36px);
  font-size: 13px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.site-nav a {
  position: relative;
  padding-bottom: 2px;
}
.site-nav a::after {
  content: '';
  position: absolute;
  left: 0; bottom: 0;
  width: 100%;
  height: 1px;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: right;
  transition: transform 200ms var(--ease-out);
}
@media (hover: hover) and (pointer: fine) {
  .site-nav a:hover::after {
    transform: scaleX(1);
    transform-origin: left;
  }
}

/* ============================================================
   Hero
   ============================================================ */
.hero {
  position: relative;
  min-height: 100svh;
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  align-items: end;
  /* Rows stack at their own content height instead of stretching to fill
     100svh — that stretch used to be what pinned .hero-foot to the
     viewport's bottom edge regardless of the photo. Now the photo (see
     below, spanning both rows) is what .hero-foot anchors to instead. */
  align-content: start;
  padding-top: clamp(130px, 17vh, 200px);
  padding-bottom: clamp(28px, 5vh, 56px);
  gap: clamp(14px, 2vh, 24px);
}

/* Hero type is sized off viewport HEIGHT (not width) so the whole section
   comfortably fits a 1920x1080 screen without scrolling. Below 900px the
   headline/photo/foot side-by-side composition doesn't have the width to
   work, so they stack instead — see the grid-row assignments on each. */
.hero-headline {
  grid-column: 1 / -1;
  grid-row: 1;
  align-self: start;
  font-size: clamp(1.7rem, 5.1vh, 3.8rem);
  line-height: 1.08;
  letter-spacing: -0.01em;
  text-transform: uppercase;
  font-weight: 700;
}

@media (min-width: 900px) {
  .hero-headline { grid-column: 1 / 9; }
}

.hero-headline .dim {
  color: #9a9a9a;
}

/* Load-in reveal: each line is masked and slides up into place, staggered.
   Rare, first-paint moment — the one place on this site the delight budget
   is spent. transform-only, so it stays cheap even while fonts/images load. */
.hero-headline .line-mask {
  display: block;
  overflow: hidden;
}
.hero-headline .line {
  display: block;
  transform: translateY(112%);
  opacity: 0;
  transition: transform 900ms var(--ease-out), opacity 700ms var(--ease-out);
}
.hero-headline.is-loaded .line { transform: translateY(0); opacity: 1; }
.hero-headline.is-loaded .line-mask:nth-child(1) .line { transition-delay: 60ms; }
.hero-headline.is-loaded .line-mask:nth-child(2) .line { transition-delay: 160ms; }
.hero-headline.is-loaded .line-mask:nth-child(3) .line { transition-delay: 260ms; }

@media (prefers-reduced-motion: reduce) {
  .hero-headline .line {
    transform: none;
    transition: opacity 400ms ease;
  }
}

/* Description + CTA fade in on the same page-load trigger as the headline
   (not scroll-triggered) — they live in the first screen, so "on scroll
   into view" could fail to fire at all depending on exactly where they
   land vertically. This fires unconditionally on load instead. */
.hero-reveal {
  opacity: 0;
  transform: translateY(16px);
  transition: opacity 700ms var(--ease-out), transform 700ms var(--ease-out);
}
.hero-reveal.is-loaded { opacity: 1; transform: translateY(0); }
.hero-desc.is-loaded { transition-delay: 340ms; }
.hero-cta-wrap.is-loaded { transition-delay: 420ms; }

@media (prefers-reduced-motion: reduce) {
  .hero-reveal { transform: none; transition: opacity 400ms ease; }
}

.hero-photo {
  grid-column: 1 / -1;
  /* Stacked below the headline on mobile; sits beside it from 900px up.
     No longer spans into the foot row — .hero-foot now flows independently
     below, spaced out with its own margin, not anchored to the photo. */
  grid-row: 2;
  justify-self: start;
  align-self: start;
  margin-top: clamp(20px, 4vh, 32px);
  width: clamp(220px, 44vw, 430px);
}

@media (min-width: 900px) {
  .hero-photo {
    grid-column: 9 / 13;
    grid-row: 1;
    justify-self: end;
    margin-top: 0;
    width: clamp(220px, 24vw, 430px);
  }
}

.hero-photo img {
  width: 100%;
  height: auto;
  display: block;
}

.hero-foot {
  grid-column: 1 / -1;
  /* Row 3 on mobile, row 2 on desktop — either way its own row, independent
     of the photo. It flows freely below, spaced out by margin-top alone;
     "View My Work" aligns to the description's bottom (via the flex
     alignment inside .hero-cta-wrap below), never to the photo. */
  grid-row: 3;
  margin-top: clamp(-24px, -0.8vh, -8px);
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  gap: clamp(16px, 2vh, 24px);
}

.hero-desc {
  grid-column: 1 / -1;
  font-size: clamp(0.82rem, 1.5vh, 0.95rem);
  line-height: 1.5;
  color: #262626;
  max-width: 42ch;
}

@media (min-width: 900px) {
  .hero-foot { grid-row: 2; }
  .hero-desc { grid-column: 1 / 7; }
}

.hero-cta-wrap {
  grid-column: 1 / -1;
  margin-top: 20px;
  display: flex;
}

@media (min-width: 900px) {
  .hero-cta-wrap { grid-column: 8 / 13; justify-content: flex-end; align-items: flex-end; margin-top: 0; }
}

.btn-link {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 10px;
  font-family: var(--font-body);
  font-size: 12.5px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  padding-bottom: 6px;
  border: none;
  background: none;
  cursor: pointer;
  color: var(--black);
  transition: transform 160ms var(--ease-out);
}
.btn-link:active { transform: scale(0.97); }
.btn-link::after {
  content: '';
  position: absolute;
  left: 0; bottom: 0;
  width: 100%; height: 1px;
  background: currentColor;
  transform: scaleX(1);
  transform-origin: left;
  transition: transform 250ms var(--ease-out);
}
@media (hover: hover) and (pointer: fine) {
  .btn-link:hover::after { transform: scaleX(0.4); transform-origin: right; }
}
.btn-link .arrow { transition: transform 250ms var(--ease-out); }
@media (hover: hover) and (pointer: fine) {
  .btn-link:hover .arrow { transform: translateX(4px); }
}

/* ============================================================
   Video section
   ============================================================ */
.video-section {
  /* Longhand on purpose: a shorthand `padding: V 0` would zero the
     left/right that .container contributes, breaking nav alignment. */
  padding-top: clamp(72px, 13vh, 136px);
  padding-bottom: clamp(56px, 10vh, 104px);
}

.video-inner {
  display: flex;
  flex-direction: column;
  align-items: center;
}

/* Medium, centered — not full-bleed. */
.video-frame {
  position: relative;
  width: min(100%, 760px);
  aspect-ratio: 16 / 9;
  background: #000;
  overflow: hidden;
}

.video-frame iframe {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
}

.video-caption {
  margin-top: 18px;
  font-size: 12px;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  text-align: center;
  color: var(--gray);
}

/* ============================================================
   Projects — horizontal carousel
   ============================================================ */
.work-section {
  padding-top: clamp(32px, 6vh, 64px);
  padding-bottom: clamp(16px, 3vh, 32px);
}

.section-heading {
  padding-bottom: clamp(18px, 3.5vh, 32px);
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 16px;
}

.section-heading h2 {
  font-size: clamp(1.3rem, 2.4vw, 1.9rem);
}

.section-heading .section-index {
  font-size: 13px;
  letter-spacing: 0.05em;
  color: var(--gray);
  text-transform: uppercase;
  white-space: nowrap;
}

/* The viewport: clips the rail, which is twice as wide as what's visible. */
.carousel-track {
  overflow: hidden;
  padding-bottom: 8px;
  padding-left: var(--carousel-pad);
  padding-right: var(--carousel-pad);
  /* Let the page still scroll vertically on a vertical touch drag; only
     horizontal drags get captured by main.js for the carousel itself. */
  touch-action: pan-y;
}

/* The mover: a plain CSS animation, not JS/rAF — this is "constant motion"
   territory (marquee), which runs smoother and more reliably off the main
   thread than anything hand-rolled with requestAnimationFrame. Duplicated
   content (see the markup) + translateX(-50%) is what makes it loop
   seamlessly with no JS measuring anything. */
.carousel-rail {
  display: flex;
  width: max-content;
  gap: clamp(12px, 2vw, 22px);
  animation: carousel-scroll 36s linear infinite;
  cursor: grab;
}

/* Added by main.js the moment a drag crosses the threshold — permanently
   swaps the marquee for manual control (animation: none beats the base
   rule by specificity, no !important needed), and main.js takes over
   `transform` directly from there on. Card links stay clickable normally;
   main.js only swallows the click that immediately follows an actual drag. */
.carousel-rail.is-manual {
  animation: none;
}
.carousel-rail.is-dragging {
  cursor: grabbing;
  user-select: none;
}

@keyframes carousel-scroll {
  to { transform: translateX(-50%); }
}

/* Pauses only while a real card (not its aria-hidden twin, which is
   pointer-events:none and so can never match :hover) is under the cursor —
   not the gaps between them. Pure CSS, so there is no JS state to get stuck. */
@media (hover: hover) and (pointer: fine) {
  .carousel-rail:has(.project-card:hover) {
    animation-play-state: paused;
  }
}

@media (prefers-reduced-motion: reduce) {
  /* Gentler, not stopped — see the a11y note in SKILL.md. */
  .carousel-rail { animation-duration: 108s; }
}

.project-card {
  flex: 0 0 clamp(220px, 24vw, 380px);
  display: block;
  text-decoration: none;
  color: inherit;
  /* Browsers treat links (and images) as natively draggable by default —
     that native "drag this link out" gesture hijacks the pointer sequence
     before main.js ever sees it, which is why dragging only used to work
     from the gaps between cards. draggable="false" in the markup is the
     reliable fix; this is the CSS-side backup for engines that key off it. */
  -webkit-user-drag: none;
  user-drag: none;
}

/* The duplicate lap (aria-hidden + tabindex="-1", so screen readers and
   keyboard tabbing only ever see the real set once) stays fully clickable
   for the mouse — a sighted visitor sees an ordinary card and expects it
   to work no matter which pass of the loop it's currently on. */

/* Square covers — cover, title, and description read together on one screen. */
.project-visual {
  position: relative;
  width: 100%;
  aspect-ratio: 1 / 1;
  overflow: hidden;
  background: #fff;
}

.project-visual .plate {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  transition: transform 500ms var(--ease-out);
}
/* The rotation never pauses on hover — hovering only signals "clickable":
   the cover scales up slightly and the title becomes the active/selected state. */
@media (hover: hover) and (pointer: fine) {
  .project-card:hover .plate { transform: scale(1.07); }
}

.project-visual .project-num {
  position: absolute;
  top: 12px; left: 12px;
  font-family: var(--font-display);
  font-size: 12px;
  color: var(--white);
  letter-spacing: 0.04em;
  z-index: 2;
}

/* Marks a personal favorite among the case studies — white fill so it
   reads on the dark cover photos, same corner logic as .project-num but
   mirrored to the opposite side so the two never collide. */
.project-visual .project-favorite {
  position: absolute;
  top: 12px; right: 12px;
  z-index: 2;
  width: 16px;
  height: 16px;
  color: var(--white);
}
.project-visual .project-favorite svg {
  width: 100%;
  height: 100%;
  display: block;
}

.project-meta {
  padding-top: 12px;
  border-top: 1px solid var(--hairline);
  margin-top: 12px;
  display: flex;
  justify-content: space-between;
  gap: 12px;
}

.project-meta .project-title {
  position: relative;
  display: inline-block;
  padding-bottom: 3px;
  font-family: var(--font-display);
  font-size: 0.95rem;
  font-weight: 600;
  color: #3a3a3a;
  transition: color 200ms ease;
}
.project-meta .project-title::after {
  content: '';
  position: absolute;
  left: 0; bottom: 0;
  width: 100%; height: 1px;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 250ms var(--ease-out);
}
@media (hover: hover) and (pointer: fine) {
  .project-card:hover .project-title { color: var(--black); }
  .project-card:hover .project-title::after { transform: scaleX(1); }
}

.project-meta .project-desc {
  font-size: 0.75rem;
  color: var(--gray);
  line-height: 1.4;
  max-width: 22ch;
  text-align: right;
}

/* Placeholder abstract plates, grayscale-only, no stock imagery */
.plate-01 { background: repeating-linear-gradient(115deg, #1a1a1a 0 2px, #050505 2px 34px); }
.plate-02 { background: radial-gradient(circle at 30% 30%, #3a3a3a 0%, #050505 62%); }
.plate-03 { background:
  linear-gradient(180deg, transparent 49.5%, #fff 49.5% 50.5%, transparent 50.5%),
  linear-gradient(90deg, transparent 49.5%, #fff 49.5% 50.5%, transparent 50.5%),
  #0d0d0d; }
.plate-04 { background: repeating-radial-gradient(circle at 20% 80%, #2a2a2a 0 3px, #050505 3px 26px); }
.plate-05 { background: linear-gradient(135deg, #111 0%, #111 48%, #fff 48%, #fff 52%, #111 52%, #111 100%); }

/* ============================================================
   Statement
   ============================================================ */
/* Its own dedicated beat between the carousel and the highlights —
   fixed height, generous separation on both sides, split into two
   asymmetric statements instead of one centered paragraph. */
.statement-section {
  min-height: 660px;
  display: flex;
  align-items: center;
  margin: clamp(32px, 6vh, 72px) 0;
}

.statement-grid {
  width: 100%;
  display: grid;
  grid-template-columns: repeat(12, 1fr);
}

.statement-a {
  grid-column: 1 / 9;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: clamp(1.7rem, 4.4vw, 3.4rem);
  line-height: 1.18;
  letter-spacing: -0.015em;
}

@media (min-width: 900px) {
  .statement-a { grid-column: 1 / 8; }
}

.statement-b {
  grid-column: 1 / -1;
  justify-self: end;
  text-align: right;
  max-width: 34ch;
  margin-top: clamp(28px, 8vh, 96px);
  font-size: clamp(1rem, 1.7vw, 1.3rem);
  line-height: 1.5;
  color: #9a9a9a;
}

@media (min-width: 900px) {
  .statement-b { grid-column: 6 / 13; }
}

/* ============================================================
   Highlights
   ============================================================ */
.highlights-section {
  /* Longhand on purpose — see the note on .video-section. */
  padding-top: clamp(32px, 6vh, 64px);
  padding-bottom: clamp(72px, 11vh, 128px);
  border-top: 1px solid var(--hairline);
}

.highlights-heading {
  padding-bottom: clamp(20px, 4vh, 40px);
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 16px;
}

.highlights-heading h2 {
  font-size: clamp(1.3rem, 2.4vw, 1.9rem);
}

.highlights-heading .section-index {
  font-size: 12px;
  letter-spacing: 0.05em;
  color: var(--gray);
  text-transform: uppercase;
  white-space: nowrap;
}

/* Inset beyond the container padding, so the rows and their hairlines
   read flush with the nav bar and every other section — same container padding, no extra inset. */
.highlight-row {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  gap: 16px;
  align-items: baseline;
  padding: clamp(14px, 2.6vh, 22px) 0;
  border-bottom: 1px solid var(--hairline);
}

.highlight-row:first-of-type { border-top: 1px solid var(--hairline); }

.highlight-index {
  grid-column: span 2;
  font-family: var(--font-display);
  font-size: 12px;
  color: var(--gray);
}

@media (min-width: 900px) { .highlight-index { grid-column: span 1; } }

.highlight-label {
  grid-column: span 10;
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(0.95rem, 2vw, 1.3rem);
  letter-spacing: -0.01em;
}

@media (min-width: 900px) { .highlight-label { grid-column: 2 / 9; } }

.highlight-tag {
  display: none;
  font-size: 12px;
  color: var(--gray);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  text-align: right;
}

@media (min-width: 900px) {
  .highlight-tag { display: block; grid-column: 9 / 13; }
}

/* ============================================================
   Footer
   ============================================================ */
.site-footer {
  background: var(--black);
  color: var(--white);
  min-height: 300px;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  /* Longhand on purpose — see the note on .video-section. */
  padding-top: clamp(28px, 5vh, 48px);
  padding-bottom: 24px;
}

.footer-top {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  gap: 24px;
  align-items: start;
}

.footer-label {
  grid-column: 1 / -1;
  font-size: 12px;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: #8a8a8a;
  margin-bottom: 12px;
}

@media (min-width: 900px) { .footer-label { grid-column: 1 / 4; margin-bottom: 0; } }

/* Right-aligned, smaller — a quiet signature rather than a headline. */
.footer-email {
  grid-column: 1 / -1;
  justify-self: start;
  font-family: var(--font-display);
  font-size: clamp(1.1rem, 2.2vw, 1.6rem);
  font-weight: 500;
  letter-spacing: -0.01em;
  word-break: break-word;
  position: relative;
  width: fit-content;
}

@media (min-width: 900px) {
  .footer-email { grid-column: 4 / 13; justify-self: end; text-align: right; }
}

.footer-email::after {
  content: '';
  position: absolute;
  left: 0; bottom: 0.06em;
  height: 2px; width: 100%;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 300ms var(--ease-out);
}
@media (hover: hover) and (pointer: fine) {
  .footer-email:hover::after { transform: scaleX(1); }
}

.footer-bottom {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  gap: 24px;
  align-items: end;
  border-top: 1px solid var(--hairline-dark);
  padding-top: 24px;
}

.footer-links {
  grid-column: 1 / -1;
  display: flex;
  gap: 24px;
  font-size: 12px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

@media (min-width: 900px) { .footer-links { grid-column: 1 / 6; } }

.footer-links a { position: relative; padding-bottom: 3px; }
.footer-links a::after {
  content: '';
  position: absolute;
  left: 0; bottom: 0; width: 100%; height: 1px;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: right;
  transition: transform 200ms var(--ease-out);
}
@media (hover: hover) and (pointer: fine) {
  .footer-links a:hover::after { transform: scaleX(1); transform-origin: left; }
}

.footer-meta {
  grid-column: 1 / -1;
  margin-top: 16px;
  font-size: 12px;
  color: #6a6a6a;
}

@media (min-width: 900px) {
  .footer-meta { grid-column: 7 / 13; text-align: right; margin-top: 0; font-size: 11px; }
}

/* ============================================================
   Case study page
   ============================================================ */
.case-hero {
  padding-top: 140px;
  padding-bottom: clamp(40px, 8vw, 80px);
}

.case-back {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--gray);
  margin-bottom: clamp(40px, 8vw, 88px);
}
.case-back .arrow { transition: transform 200ms var(--ease-out); }
@media (hover: hover) and (pointer: fine) {
  .case-back:hover { color: var(--black); }
  .case-back:hover .arrow { transform: translateX(-4px); }
}

.case-title {
  font-size: clamp(2.4rem, 7vw, 6rem);
  line-height: 1.02;
  letter-spacing: -0.02em;
  max-width: 16ch;
}

.case-meta-row {
  margin-top: clamp(28px, 5vw, 56px);
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  gap: 20px;
  border-top: 1px solid var(--hairline);
  padding-top: 20px;
}

.case-meta-item { grid-column: span 6; }
@media (min-width: 700px) { .case-meta-item { grid-column: span 3; } }

.case-meta-item .k {
  font-size: 12px;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--gray);
  margin-bottom: 6px;
}
.case-meta-item .v { font-size: 0.95rem; }

.case-cover {
  width: 100%;
  aspect-ratio: 16 / 9;
  background: #0d0d0d;
  overflow: hidden;
}

.case-cover img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.case-body {
  /* Longhand on purpose — see the note on .video-section. */
  padding-top: clamp(60px, 10vw, 120px);
  padding-bottom: clamp(60px, 10vw, 120px);
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  gap: 24px 24px;
}

.case-body .lede {
  grid-column: 1 / -1;
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(1.3rem, 3vw, 2.1rem);
  line-height: 1.3;
  margin-bottom: clamp(40px, 6vw, 72px);
}
@media (min-width: 900px) { .case-body .lede { grid-column: 1 / 10; } }

.case-body .copy {
  grid-column: 1 / -1;
  font-size: 1rem;
  line-height: 1.65;
  color: #262626;
}
@media (min-width: 900px) { .case-body .copy { grid-column: 1 / 7; } }

.case-body .process {
  grid-column: 1 / -1;
  margin-top: clamp(16px, 3vw, 0px);
}
@media (min-width: 900px) { .case-body .process { grid-column: 8 / 13; } }

.process-item { padding: 16px 0; border-top: 1px solid var(--hairline); }
.process-item:last-child { border-bottom: 1px solid var(--hairline); }
.process-item .n { font-size: 12px; color: var(--gray); }
.process-item .t { font-family: var(--font-display); font-weight: 500; font-size: 1rem; margin-top: 4px; }

.case-gallery {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  gap: clamp(16px, 3vw, 28px);
  padding-bottom: clamp(60px, 10vw, 120px);
  /* Without this, grid's default stretch forces every item to the height
     of the tallest one in its row — leaving empty background behind
     shorter images instead of letting each box hug its own image. */
  align-items: start;
}

.case-gallery .g {
  grid-column: 1 / -1;
  /* White, not black — a sub-pixel rounding gap between a grid column's
     width and its image's rendered width is common, and on a dark fallback
     that sliver reads as a thin outline around the photo. White is
     invisible against the page in the same situation. */
  background: #fff;
}

/* Two-image layout (older case studies still on placeholder plates) —
   fixed aspect-ratio boxes with a cropping fill, since those are plain
   CSS gradients with no real photo to preserve. */
.case-gallery .g-a,
.case-gallery .g-b {
  aspect-ratio: 4 / 3;
  overflow: hidden;
}
.case-gallery .g-a img,
.case-gallery .g-b img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
@media (min-width: 700px) {
  .case-gallery .g-a { grid-column: 1 / 8; }
  .case-gallery .g-b { grid-column: 8 / 13; aspect-ratio: 3 / 4; }
}

/* Avant-garde collage — positions and sizes below are measured pixel-for-
   pixel from the client's own reference board (rudus-collage-reference),
   converted to percentages of its content width. No cropping: every image
   keeps its exact native proportions. Named for what they hold rather than
   upload order, since the two don't match. */
.case-gallery .g-symbols img,
.case-gallery .g-card img,
.case-gallery .g-screen img,
.case-gallery .g-sign img,
.case-gallery .g-tshirt img,
.case-gallery .g-blueprint img,
.case-gallery .g-cb-hero img,
.case-gallery .g-cb-phone img,
.case-gallery .g-cb-poster img,
.case-gallery .g-cb-screen img,
.case-gallery .g-cb-gif img,
.case-gallery .g-dr-symbols img,
.case-gallery .g-dr-hoodie img,
.case-gallery .g-dr-poster img,
.case-gallery .g-dr-card img,
.case-gallery .g-dr-stage img,
.case-gallery .g-ch-phone img,
.case-gallery .g-ch-laptop img,
.case-gallery .g-ch-card img,
.case-gallery .g-ch-tablet img,
.case-gallery .g-an-hoodie img,
.case-gallery .g-an-billboard img,
.case-gallery .g-an-cards img {
  width: 100%;
  height: auto;
  display: block;
}

/* Transparent-background art (symbol sketches, blueprint line-work) must
   not sit on the shared #111 fallback — that's meant to be a loading
   placeholder for opaque photos, not a permanent backdrop. These two let
   the page's own white show through, same as the hero photo. */
.case-gallery .g-symbols,
.case-gallery .g-blueprint {
  background: transparent;
}

/* Small context label above a gallery image — same quiet, uppercase,
   gray-label language as .footer-label / .case-meta-item .k elsewhere. */
.g-caption {
  display: block;
  font-size: 12px;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--gray);
  margin-bottom: 18px;
}

/* The compound shot is a pre-composed board (its own gray backdrop is part
   of the asset). object-fit: contain — never cover — so nothing crops;
   the reference's measured 722:488 envelope is reproduced by sizing the
   box itself, with a matching gray so any rounding is invisible. */
.case-gallery .g-compound {
  background: #d0d0d0;
  aspect-ratio: 722 / 488;
}
.case-gallery .g-compound img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  display: block;
}

@media (min-width: 700px) {
  /* Row 1 — small symbol grid (x 4.6%, w 29.9%), card offset down+right
     within the row (x 45.3%, w 52.0%, +38px) — this offset is an internal
     stagger, not one of the four rhythm gaps below, so it's untouched. */
  .case-gallery .g-symbols { grid-column: 1 / 5; margin-top: clamp(-220px, -14vw, -170px); }
  .case-gallery .g-card { grid-column: 6 / 13; margin-top: clamp(20px, 3.5vw, 40px); }

  /* Row 2 — the compound board, nearly full width (measured 97.3%). */
  .case-gallery .g-compound { grid-column: 1 / 13; }

  /* Row 3 — screen and sign start the same row (no relative offset). */
  .case-gallery .g-screen { grid-column: 1 / 8; }
  .case-gallery .g-sign { grid-column: 8 / 13; }

  /* Row 4 — apparel shot indented off the left margin (x 13.2%). Blueprint
     shares its row but aligns to the row's BOTTOM instead of top, so it
     sits flush with the t-shirt's bottom edge rather than at a fixed
     offset from the top. */
  .case-gallery .g-tshirt { grid-column: 2 / 7; }
  .case-gallery .g-blueprint { grid-column: 8 / 13; align-self: end; }
}

/* Cardboard gallery — each shot is already a composed mockup photo (its own
   backdrop, shadow, and crop), so images run at native proportions with no
   cover-cropping. Row 1: phone (portrait) + gif (landscape 4:3) split evenly;
   the gif is shorter per its own aspect ratio, so align-self: end bottom-
   flushes it against the phone shot instead of hanging from the row's top,
   matching the client's reference. Hero runs full width unchanged. Row 3:
   screen + poster, sized to their measured reference-board ratio (~58/42). */
@media (min-width: 700px) {
  .case-gallery .g-cb-phone { grid-column: 1 / 7; }
  .case-gallery .g-cb-gif { grid-column: 7 / 13; align-self: end; }
  .case-gallery .g-cb-screen { grid-column: 1 / 8; }
  .case-gallery .g-cb-poster { grid-column: 8 / 13; }
}

/* Dream Robotics gallery — column line 6 is shared by both top and middle
   rows (symbols/poster end there, hoodie/card start there), so the gap
   between every pair comes from the grid's own `gap` alone and lines up
   identically row to row. Hoodie stops at column 11, not 13, leaving the
   reference board's small blank margin on the right instead of running the
   photo flush to the edge. Card sits under the hoodie's left edge but runs
   wider, to column 13. Stage spans the full width, the largest shot.

   Symbols and card break the shared top-alignment on purpose, mirroring
   each other: symbols shrinks and lifts above hoodie's top edge (negative
   margin-top), card drops to flush with poster's bottom edge (align-self:
   end) instead of sharing poster's top.

   Card, bottom-flush to poster, sits far below hoodie's bottom edge —
   poster runs taller than card, so the gap there is naturally bigger than
   the standard rhythm. Client wants poster/card untouched and hoodie itself
   pulled down to close that gap to the standard 28px instead. margin-top
   shifts hoodie down; the equal-and-opposite margin-bottom cancels its
   contribution to row 1's auto height, so poster/card/stage below don't
   shift with it — hoodie overlaps into what was blank gap space. Value is
   the measured poster/card height difference at desktop width, ~8.5vw. */
@media (min-width: 700px) {
  .case-gallery .g-dr-symbols { grid-column: 1 / 5; margin-top: clamp(-140px, -10vw, -90px); }
  .case-gallery .g-dr-hoodie { grid-column: 6 / 11; margin-top: clamp(70px, 8.5vw, 115px); margin-bottom: clamp(-115px, -8.5vw, -70px); }
  .case-gallery .g-dr-poster { grid-column: 1 / 6; }
  .case-gallery .g-dr-card { grid-column: 6 / 13; align-self: end; }
  .case-gallery .g-dr-stage { grid-column: 1 / 13; }
}

/* Chestnut gallery — an intentionally asymmetric two-column cascade. Column
   line 7 is shared by all four shots, so every horizontal gap (phone↔laptop,
   card↔tablet) is the grid's own `gap`, automatically identical. Left
   column: phone sits indented, not flush to the margin; card below it is
   wider and IS flush to the left margin, right-aligned to match phone's
   right edge exactly (line 7). Right column: laptop is flush to the right
   margin (line 13); tablet below it is narrower and left-aligned to
   laptop's left edge (line 7), falling short of the margin. Net effect:
   phone's right edge and tablet's left edge — the two "inner" edges facing
   the page's center — sit close together, while laptop and card own the
   outer edges.

   Laptop is pushed ~150–200px below phone (client's spec), via margin-top
   cancelled by an equal negative margin-bottom so it doesn't drag card's
   position down with it — same overlap technique as Dream Robotics' hoodie.
   Tablet then gets its own margin-top so its gap below laptop's now-lower
   bottom edge matches the standard rhythm, rather than inheriting the
   default top-alignment shared with card. */
@media (min-width: 700px) {
  .case-gallery .g-ch-phone { grid-column: 3 / 7; }
  .case-gallery .g-ch-laptop { grid-column: 7 / 13; margin-top: clamp(130px, 13vw, 190px); margin-bottom: clamp(-190px, -13vw, -130px); }
  .case-gallery .g-ch-card { grid-column: 1 / 7; }
  .case-gallery .g-ch-tablet { grid-column: 7 / 11; margin-top: clamp(75px, 8.6vw, 120px); }
}

/* Angle gallery — the simple version: standard-size hoodie top-left,
   full-width billboard in the middle, standard-size cards right-aligned to
   the billboard's own right edge (shared column line 13, no offset math
   needed since nothing has to dodge a taller neighbor). */
@media (min-width: 700px) {
  .case-gallery .g-an-hoodie { grid-column: 1 / 7; }
  .case-gallery .g-an-billboard { grid-column: 1 / 13; }
  .case-gallery .g-an-cards { grid-column: 7 / 13; }
}

/* The four rhythm gaps the client wants equal — card↔compound (vertical),
   compound↔screens (vertical), screen↔sign (horizontal), screens↔tshirt
   (vertical) — are now ALL just the grid's own `gap` above (row-gap and
   column-gap are the same value), with no extra per-row margin added on
   top. That's what makes them match exactly instead of approximately. */

.case-next {
  border-top: 1px solid var(--hairline);
  /* Longhand on purpose — see the note on .video-section. */
  padding-top: clamp(40px, 8vw, 80px);
  padding-bottom: clamp(40px, 8vw, 80px);
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 16px;
}
.case-next .label { font-size: 13px; letter-spacing: 0.04em; text-transform: uppercase; color: var(--gray); }
.case-next .title { font-family: var(--font-display); font-weight: 600; font-size: clamp(1.4rem, 3vw, 2rem); }
