/* ============================================================
   Ivy Chen — Portfolio Homepage
   Reference: Figma 1280px @ icrNgZSGgleZo6WyuusbB5
   Spacing & text metrics are taken directly from Figma frames.
   ============================================================ */

/* ------------------------------------------------------------
   Self-hosted fonts (OFL). Geist replaces Circular Std,
   Geist Mono replaces SF Mono. Variable woff2 — one file per
   family covers all weights (100–900).
   License: assets/fonts/Geist-LICENSE.txt
   ------------------------------------------------------------ */
@font-face {
  font-family: "Geist";
  src: url("assets/fonts/Geist-Variable.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Geist Mono";
  src: url("assets/fonts/GeistMono-Variable.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

:root {
  /* Colors */
  --bg-page:          #000000;
  --bg-header:        #070707;
  --bg-ending:        #ffffff;
  --color-text:       #ffffff;
  --color-text-mute:  #837e7e;
  --color-accent:     #ccff00;
  --color-yellow:     #ffe600;
  --color-work-text:  #422307;
  --border-soft:      rgba(255,255,255,0.15);
  --border-med:       rgba(255,255,255,0.20);
  --border-stronger:  rgba(255,255,255,0.30);

  /* Layout grid (Figma reference @ 1280px) */
  --space-page:       90px;          /* px-[90px]  */
  --max-content:      1100px;        /* w-[1100px] inner frames */
  --section-radius:   24px;          /* rounded-[24px] */

  /* Spacing scale — every page uses these (overridden at ≤900 / ≤600 below).
     Visible gaps, not paddings: a section gap is split half/half between neighbours. */
  --page-top:    180px;   /* page top → first content */
  --gap-section: 160px;   /* between major sections (and into "Other Projects") */
  --gap-module:  var(--gap-section);   /* between modules on project pages; roomier on iPad + phones (below) */
  --gap-ending:  80px;    /* last content → ending block edge (+ ending's own 120 = 200 to its title) */
  --gap-head:    40px;    /* section heading (title + subtitle) → its content */
  --gap-title:   16px;    /* section title → its subtitle */
  --gap-block:   64px;    /* between content blocks inside one section */
  --gap-card:    12px;    /* card / sub-heading → its text or media */
  --gap-item:    160px;   /* between items of a list (homepage projects) — scales harder than sections on small screens */

  /* Corner radius scale — three sizes, each steps down at ≤900 / ≤600. */
  --radius-lg:   24px;    /* large media: project covers, portrait, full-width images, big cards */
  --radius-md:   20px;    /* cards and mid-size media */
  --radius-sm:   10px;    /* thumbnails, small images */

  /* Type scale — 8 levels, each steps down at ≤900 / ≤600. Weights: 400 text,
     600 headings, 700 big numbers, 500 mono labels. Geist for headings and text,
     Geist Mono for labels / meta (and the homepage greeting + "Featured Work"). */
  --fs-display: 56px;     /* page titles: Hi I'm Ivy Chen, About name, case-study titles */
  --fs-h2:      40px;     /* section titles */
  --fs-h3:      24px;     /* card / module titles, buttons */
  --fs-lead:    20px;     /* subtitles, intro paragraphs */
  --fs-body:    16px;     /* paragraphs, descriptions */
  --fs-small:   14px;     /* secondary text, captions, stat labels */
  --fs-label:   12px;     /* mono eyebrows, meta lines, tags */
  --fs-stat:    48px;     /* big numbers */

  /* Hairline around project covers: just enough to hold dark-edged covers'
     shape on the black page. Steps down with the card size; brightens on hover. */
  --cover-stroke-w:     1px;    /* card/frame stroke width; 0.5px on phones */
  --cover-stroke:       rgba(255, 255, 255, 0.12);
  --cover-stroke-hover: rgba(255, 255, 255, 0.24);

  /* Font stacks */
  --font-display: "Geist", -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
  --font-mono:    "Geist Mono", ui-monospace, Menlo, Monaco, Consolas, monospace;

  /* Unified module-subtitle color (dark sections) — used across all 4 case pages */
  --sub-color:       #b9b9b9;
  --sub-color-light: #555;   /* on white-background sections */
}

* { box-sizing: border-box; }

html, body { margin: 0; padding: 0; }
html       { -webkit-text-size-adjust: 100%; }

body {
  background: var(--bg-page);
  color: var(--color-text);
  font-family: var(--font-display);
  font-weight: 400;
  line-height: 1.5;
  /* Font-rendering hints applied to every page (about, case studies, home). */
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
}

/* no grey tap rectangle on phones: the nav shapes, icons and buttons have their own press feedback */
html { -webkit-tap-highlight-color: transparent; }

img { display: block; max-width: 100%; height: auto; }
/* loading placeholder: same box and corners as the image, 6% white, until it loads (contact-actions.js) */
img.is-loading, video.is-loading { background-color: rgba(255, 255, 255, 0.06); }
a   { color: inherit; text-decoration: none; }

/* ============================================================
   Floating top navigation
   Figma: px-80 py-22 on the nav bar; items 88×88 (enlarged to fit "Resume")
   ============================================================ */
.site-nav {
  position: fixed;
  top: 22px;
  right: 80px;            /* matches px-[80px] on the nav bar */
  z-index: 100;
}

.nav-pill {
  display: flex;
  gap: 4px;               /* Ivy: +4px between Work and Resume */
}

/* Back link — top-left of project detail pages, aligned with the nav band */
.nav-back {
  position: fixed;
  top: 22px;
  left: 80px;
  height: 88px;           /* = nav item height, so Back stays centred on the nav band */
  display: inline-flex;
  align-items: center;
  gap: 8px;
  z-index: 100;
  color: #fff;
  /* Knockout: invert against whatever scrolls behind (visible on white & black) */
  mix-blend-mode: difference;
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: 16px;
  line-height: 1.3;
  text-decoration: none;
  transition: opacity .2s ease, transform .2s ease;
}
.nav-back__arrow { width: 20px; height: 20px; display: block; }
.nav-back-chip { display: none; }   /* phones + iPads only (below) */
.nav-back:hover { opacity: 0.75; }
.nav-back:hover .nav-back__arrow { transform: translateX(-3px); }
.nav-back:focus-visible { outline: 2px solid rgba(255,255,255,0.6); outline-offset: 4px; }
@media (max-width: 900px) { .nav-back { top: 16px; left: 40px; } }
@media (max-width: 600px) {
  .nav-back { top: 12px; left: 20px; height: 68px; font-size: 15px; gap: 6px; }   /* left = the 20px phone gutter; 15 = iOS nav text */
  .nav-back__arrow { width: 16px; height: 16px; }
}

.nav-item {
  position: relative;
  width: 88px;
  height: 88px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: 16px;
  line-height: 1.3;       /* Paragraph 4 token */
  text-align: center;
  letter-spacing: 0;
  cursor: pointer;
  /* Spring easings reused on hover & click for a playful feel */
  --ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
}

.nav-item__shape,
.nav-item__circle {
  position: absolute;
  inset: 0;
  pointer-events: none;
  transition:
    transform .55s var(--ease-spring),
    filter    .35s ease;
  transform-origin: 50% 50%;
  will-change: transform;
}
/* Work shape — 4-petal cloverleaf in lime green (#CCFF00) */
.nav-item__shape {
  background-color: transparent;
  background-image: url("assets/nav-work-shape.svg");
  background-repeat: no-repeat;
  background-position: center;
  background-size: 100% 100%;
}
.nav-item__circle { background: var(--color-yellow); border-radius: 999px; }

.nav-item__label {
  position: relative;
  z-index: 1;
  display: inline-block;
  transition: transform .25s var(--ease-spring);
}
.nav-item__label--work    { color: var(--color-work-text); }
.nav-item__label--about   { color: #000; }

/* ----- Hover ----- */
/* Cloverleaf: one full in-place spin per hover engagement — mouse only
   (touch "hover" after a tap would spin it on phones). Never on click. */
@media (hover: hover) and (pointer: fine) {
  .nav-item--work:hover .nav-item__shape {
    animation: nav-clover-spin-1 0.85s cubic-bezier(0.45, 0, 0.35, 1) 1 both;
  }
}
@keyframes nav-clover-spin-1 {
  from { transform: rotate(0deg);   }
  to   { transform: rotate(360deg); }
}
.nav-item--work:hover .nav-item__label {
  transform: translateY(-2px);
}

/* About circle: gentle scale up on hover (no glow, no halo ring).
   Capped at 1.07 so the circle never extends far enough left to overlap
   the Work cloverleaf sitting flush beside it. */
.nav-item--about .nav-item__circle {
  transform: scale(1);
  transition: transform .35s var(--ease-spring);
}
.nav-item--about:hover .nav-item__circle {
  transform: scale(1.07);
}
.nav-item--about:hover .nav-item__label {
  transform: translateY(-2px);
}

/* ----- Click / activation ----- */
/* About: click is a bigger scale-up "pop" (not a squeeze). Peak 1.10 stays
   inside the ~4px of empty space the cloverleaf's curved right side leaves
   between the two boxes, so the circle never visually overlaps Work.
   Triggered by .is-punching from JS so the bounce plays even after release. */
.nav-item--about.is-punching .nav-item__circle {
  animation: nav-about-punch 0.45s cubic-bezier(0.34, 1.56, 0.64, 1) 1 both;
}
@keyframes nav-about-punch {
  0%   { transform: scale(1);    }
  35%  { transform: scale(1.10); }   /* peak */
  100% { transform: scale(1.07); }   /* settle into hover state */
}
.nav-item:active .nav-item__label {
  transform: translateY(0);
  transition-duration: .1s;
}

/* ----- Keyboard focus parity ----- */
.nav-item:focus-visible { outline: none; }
.nav-item--about:focus-visible .nav-item__circle {
  transform: scale(1.07);
}

/* Reduced motion — disable spin and punch animations (static scale is fine) */
@media (prefers-reduced-motion: reduce) {
  .nav-item--work:hover .nav-item__shape,
  .nav-item--about.is-punching .nav-item__circle { animation: none; }
}

/* ============================================================
   Section paddings shared
   ============================================================ */
.hero,
.work,
.ending { padding-left: var(--space-page); padding-right: var(--space-page); }

/* ============================================================
   Hero / About — Header section
   Figma:  py-[180px] px-[90px]
   Inner Frame 34: column, gap-[22px], width 1100
   ============================================================ */
.hero {
  background: var(--bg-header);
  padding-top:    var(--page-top);
  padding-bottom: 30px;    /* visible last bio line → "Featured Work" = contact icons → stats (96 @1440) */
}

.hero__inner {
  max-width: var(--max-content);
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  gap: 22px;              /* Frame 34 gap-[22px] */
}

/* Intro group — Frame 43: column items-center, gap-[10px]
   Inside: 175 avatar  →  Frame 44 (py-[30px], gap-[24px]) */
.hero__intro {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 10px;              /* Frame 43 gap-[10px] */
}

.hero__intro > .avatar { margin-bottom: 30px; }  /* Frame 44 py-30 top */

.hero__title {
  font-family: var(--font-mono);
  font-weight: 600;
  font-size: var(--fs-display);
  line-height: 1.1;        /* leading-[normal] */
  letter-spacing: 0;
  margin: 0 0 24px;        /* Frame 44 gap-[24px] */
  color: var(--color-text);
}

.hero__subtitle {
  font-family: var(--font-display); /* unified subtitle font */
  font-weight: 400;        /* unified subtitle weight */
  font-size: var(--fs-lead);         /* unified subtitle size */
  line-height: 1.4;        /* unified subtitle line-height */
  letter-spacing: 0;
  margin: 0 0 24px;        /* Frame 44 gap-[24px] */
}

.hero__contact {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 20px;               /* Frame 42 gap-[20px] */
  height: 36px;            /* item height */
  margin-bottom: 30px;     /* Frame 44 py-30 bottom */
}

.icon-btn {
  width: 36px;
  height: 36px;
  border-radius: 50%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  flex-shrink: 0;
  transition: transform .15s ease;
}
.icon-btn:hover { transform: translateY(-1px); }
.icon-btn img,
.icon-btn .icon-btn__svg { width: 100%; height: 100%; display: block; }
/* shape-rendering: geometricPrecision keeps the round outer circle smooth
   on retina; the default 'auto' can produce mild aliasing on small icons. */
.icon-btn .icon-btn__svg { shape-rendering: geometricPrecision; }

.hero__contact-text {
  font-family: var(--font-display);
  font-weight: 400;        /* Circular Std Light */
  font-size: var(--fs-lead);
  line-height: 36px;       /* leading-[36px] */
  letter-spacing: 0;
  color: var(--color-text-mute);
}

/* Avatar */
.avatar {
  border-radius: 50%;
  overflow: hidden;
  border: 1px solid var(--border-soft);
  flex-shrink: 0;
}
.avatar img       { width: 100%; height: 100%; object-fit: cover; }
.avatar--lg       { width: 175px; height: 175px; }   /* Frame 22 size-[175] */
.avatar--sm       { width: 120px; height: 120px; }   /* Ending Frame 22 size-[120] */

/* Self-introduction — sections with bold headings + body paragraphs.
   Figma: heading 18px bold, body 18px regular, line-height ~30px.
   Section gap (heading → first paragraph) tighter than paragraph-to-paragraph. */
.hero__bio {
  display: flex;
  flex-direction: column;
  gap: 12px;               /* base gap; finer rules below */
  padding: 20px 0;         /* py-[20px] on Self introduction */
  text-align: left;
}

.bio-heading {
  font-family: var(--font-display);
  font-weight: 600;        /* Circular Std Bold */
  font-size: var(--fs-lead);
  line-height: 1.4;
  letter-spacing: 0;
  margin: 0;
  color: var(--color-text);
}

/* Tighten the gap between a heading and its body paragraph; restore a
   larger gap before the *next* heading so each section reads as a unit. */
.hero__bio .bio-heading + .bio-text { margin-top: -4px; }
.hero__bio .bio-text + .bio-heading { margin-top: 14px; }

.bio-text {
  font-family: var(--font-display);
  font-weight: 400;        /* Circular Std Light */
  font-size: var(--fs-lead);
  line-height: 1.6;        /* ~30px */
  letter-spacing: 0;
  margin: 0;
  color: rgba(255, 255, 255, 0.6);   /* body copy at 60% white on every screen (Ivy) */
}
.bio-text u { text-decoration: underline; text-underline-offset: 3px; }

/* Closing line ("Explore our music design team's operations below.") sits
   in its own block with extra top margin so it visually separates from the
   AI Frontier paragraph. */
.bio-text--lead { margin-top: 14px; }

/* Tighten bio → "More about me": the visible text-to-text gap was 60px on desktop
   (bio padding 20 + hero gap 22 + the link's centering inside its 60px frame).
   Halved to 30px; the 60px Figma link height is kept. */

/* Bio stat callout strip (homepage intro) */
.bio-stats {
  display: flex;
  align-items: stretch;
  flex-wrap: wrap;
  margin: 24px 0 14px;     /* 14 + .hero__bio gap 12 = 26px → same as the gap before the 2nd heading */
  border-top: 1px solid rgba(255, 255, 255, 0.10);
  border-bottom: 1px solid rgba(255, 255, 255, 0.10);
}
.bio-stat { flex: 1 1 0; min-width: 120px; padding: 18px 24px 18px 0; }
.bio-stat + .bio-stat { padding-left: 24px; border-left: 1px solid rgba(255, 255, 255, 0.10); }
.bio-stat__num {
  display: block;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: var(--fs-stat);
  line-height: 1;
  letter-spacing: -0.02em;
  color: #fff;
}
.bio-stat__num span { font-size: var(--fs-lead); font-weight: 600; color: #9a9a9a; margin-left: 2px; }
.bio-stat__label {
  display: block;
  margin-top: 10px;
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: var(--fs-label);
  letter-spacing: 0.12em;
  /* Title Case is written in the HTML ("Years of Experience" — capitalize would also capitalize "of") */
  color: var(--sub-color);
  line-height: 1.4;
}
@media (max-width: 680px) {
  .bio-stats { border: 0; }
  .bio-stat { flex: 1 1 45%; padding: 14px 0; border-top: 1px solid rgba(255, 255, 255, 0.10); }
  .bio-stat + .bio-stat { padding-left: 0; border-left: 0; }
}

/* Team photo — Rectangle 8: 1098×820, rounded-24, border 30% white */
.hero__team-figure {
  margin: 0;
  /* Per design: 1px white stroke at 20% opacity.
     The previous version had this stroke duplicated — the Figma export
     itself contained a baked-in 1px white-20% stroke, so the CSS border
     drew a second one 1px outside it. The image is now cropped INSIDE
     that baked stroke, so this CSS border is the only one visible. */
  border: var(--cover-stroke-w) solid var(--cover-stroke);
  border-radius: var(--radius-lg);
  overflow: hidden;
  /* Matches the re-exported "Our products & service" art (1098 × 872). */
  aspect-ratio: 1098 / 872;
  width: 100%;
  background: #111;
}
.hero__team-figure img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* "More about me" — Frame 38: h-[60px] gap-[10px], text 24px */

/* ============================================================
   Featured Work
   Sections at 1280 wide. First: py-[60px], px-[90px], gap-[40px]
   Subsequent: py-[80px] gap-[40px]
   Last: pt-[80px] pb-[200px] gap-[40px]
   Project title gap-[10px] to subtitle
   ============================================================ */
.work {
  background: var(--bg-page);
  /* No outer padding — each section handles its own */
  padding-left: 0;
  padding-right: 0;
}

.work__inner {
  max-width: 1280px;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
}

.work__title-wrap,
.project {
  padding-left: var(--space-page);
  padding-right: var(--space-page);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 40px;               /* gap-[40px] between cover & text */
}

.project--link {
  text-decoration: none;
  color: inherit;
  cursor: pointer;
  transition: transform .25s ease;
}
.project--link:hover { transform: translateY(-2px); }
.project--link:hover .project__cover { box-shadow: 0 12px 36px rgba(204,255,0,0.18); border-color: var(--cover-stroke-hover); }

.work__title {
  font-family: var(--font-mono); /* SF Mono */
  font-weight: 600;        /* unified main-title weight */
  font-size: var(--fs-display);         /* same size as .hero__title ("Hi, I'm Ivy Chen") at every breakpoint */
  line-height: 1.1;       /* unified main-title line-height */
  letter-spacing: 0;
  text-align: center;
  color: var(--color-text);
  margin: 0;
}

/* Section padding wrappers (replicate per-section py values) */
.project              { padding-top: calc(var(--gap-item) / 2); padding-bottom: calc(var(--gap-item) / 2); }
.project:first-of-type{ padding-top: 70px; }  /* "Featured Work" → first card (Ivy-tuned: +20, then +10) */
.project:last-of-type { padding-bottom: var(--gap-item); }  /* last card → ending block = the gap between cards (Ivy) */
@media (min-width: 901px) {
  .project + .project { padding-top: calc(var(--gap-item) / 2 - 10px); }   /* web: card text → next card 150 (−10, Ivy) */
}

.work__title-wrap { padding-top: 30px; padding-bottom: 0; }   /* was 60 — halved with .hero padding-bottom */

.project__cover {
  position: relative;
  width: 100%;
  max-width: var(--max-content);
  aspect-ratio: 1100 / 600;
  border: var(--cover-stroke-w) solid var(--cover-stroke);
  border-radius: var(--radius-lg);
  overflow: hidden;
  background: #0d0d0d;
  transition: border-color .25s ease, box-shadow .25s ease;
}
.project__cover img,
.project__cover video      { width: 100%; height: 100%; object-fit: cover; display: block; }
.project__cover--video     { background: #000; }

.project__text {
  width: 100%;
  max-width: var(--max-content);
  display: flex;
  flex-direction: column;
  gap: var(--gap-title);   /* title → subtitle (was a fixed 10px) */
  padding: 0 4px;          /* text sits 4px inside the cover's edges (2px on phones) */
}

.project__title {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: var(--fs-h2);
  line-height: 1.25;        /* leading-[normal] */
  letter-spacing: 0;
  margin: 0;
  color: var(--color-text);
}

.project__subtitle {
  font-family: var(--font-display);
  font-weight: 400;        /* unified subtitle weight */
  font-size: var(--fs-lead);
  line-height: 1.4;        /* unified subtitle line-height */
  letter-spacing: 0;
  margin: 0;
  color: var(--color-text);
}

/* ============================================================
   Other projects — shown at the bottom of every case-study page,
   immediately above the ending CTA. Three cards in a row, each
   reusing the homepage's cover image / title (no subtitle).
   ============================================================ */
.other-projects {
  background: var(--bg-page);
  color: var(--color-text);
  padding: var(--gap-module) var(--space-page) var(--gap-ending);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--gap-head);
}

/* The gap into "Other projects" is its own padding-top (--gap-module), so the
   preceding section's bottom padding must not add to it. */
.case-page section:has(+ .other-projects) { padding-bottom: 0; }

.other-projects__title {
  font-family: var(--font-display);   /* Circular Std */
  font-weight: 600;                   /* unified main-title weight */
  font-size: var(--fs-h2);
  line-height: 1.25;
  letter-spacing: 0;
  margin: -0.27em 0 0;     /* trims the line box above the caps, like .case-section__title */
  text-align: center;
}

.other-projects__grid {
  width: 100%;
  max-width: 1280px;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 32px;
}

.other-project {
  display: flex;
  flex-direction: column;
  gap: 18px;
  text-decoration: none;
  color: inherit;
  transition: transform .25s ease;
}
.other-project:hover { transform: translateY(-2px); }
.other-project:hover .other-project__cover {
  box-shadow: 0 12px 36px rgba(204, 255, 0, 0.18);   /* same lime glow as homepage */
  border-color: var(--cover-stroke-hover);
}

/* Cover — same 1100:600 aspect, same border + bg as the homepage cards,
   but with the explicit 10px radius the user requested. */
.other-project__cover {
  position: relative;
  width: 100%;
  aspect-ratio: 1100 / 600;
  border: var(--cover-stroke-w) solid var(--cover-stroke);
  border-radius: var(--radius-sm);
  overflow: hidden;
  background: #0d0d0d;
  transition: border-color .25s ease, box-shadow .25s ease;
}
.other-project__cover--video { background: #000; }
.other-project__cover img,
.other-project__cover video {
  width: 100%; height: 100%; object-fit: cover; display: block;
}

/* Card title only — no subtitle. H4 = 20px Circular Std Regular. */
.other-project__title {
  font-family: var(--font-display);
  font-weight: 400;        /* Regular */
  font-size: var(--fs-lead);
  line-height: 1.4;
  letter-spacing: 0;
  margin: 0;
  color: var(--color-text);
}

/* ============================================================
   Ending CTA
   Figma: py-[120px], gap-[40px], avatar 120, CTA gap-[10px]
   Button group: py-[40px] gap-[50px], buttons h-[70px]
   ============================================================ */
.ending {
  /* Fallback / default = clear sunny sky (same as .ending--day-clear).
     JS overrides for actual weather and post-sunset meteor mode. */
  background:
    radial-gradient(circle at 88% 12%, rgba(255, 224, 130, 0.55) 0%, rgba(255, 224, 130, 0) 28%),
    linear-gradient(180deg, #cfe6ff 0%, #eaf5ff 60%, #fff9e8 100%);
  color: #000;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 40px;               /* gap-[40px] avatar→CTA */
  padding-top:    120px;
  padding-bottom: 120px;
  position: relative;
  overflow: hidden;
}
.ending > * { position: relative; z-index: 1; }

.ending .avatar { border-color: rgba(0,0,0,0.1); }

/* ----- Starry night mode (active 10 PM – 3 AM SF time) ----- */
.ending--night {
  background: radial-gradient(ellipse at top, #1a2347 0%, #0a0e2a 40%, #04061a 100%);
  color: #fff;
  transition: background 1.2s ease;
}
.ending--night .ending__title    { color: #ffffff; }
.ending--night .ending__widget   { color: #8e9bd0; }
.ending--night .ending__widget-sep { color: #6975a8; }
.ending--night .avatar { border-color: rgba(255,255,255,0.18); }

.ending--night .btn--primary {
  background: #ffffff;
  color: #000000;
}
/* Envelope PNG is white pixels; on the white night-mode pill it would
   disappear without inversion. invert(1) flips white → black. */
.ending--night .btn--primary .btn__icon { filter: invert(1); }
/* Chat on LinkedIn pill — in night mode the bg 5%-black would be invisible
   on the deep blue sky, so use 8%-white instead. Icon flips white. */
.ending--night .btn--outline {
  background: rgba(255, 255, 255, 0.08);
  color: #ffffff;
  border: 0;
}
.ending--night .btn--outline .btn__icon { filter: invert(1); }

/* ============================================================
   Daytime weather themes — backgrounds reflect San Francisco's
   current weather. All themes keep light backgrounds so the
   existing dark title / mono subtitle / button styles stay
   crisply legible. Smooth cross-fade when the theme changes.
   ============================================================ */
.ending { transition: background 1.2s ease, color 0.6s ease; }

/* Clear sunny day — pale-blue sky with a warm sun glow in the corner */
.ending--day-clear {
  background:
    radial-gradient(circle at 88% 12%, rgba(255, 224, 130, 0.55) 0%, rgba(255, 224, 130, 0) 28%),
    linear-gradient(180deg, #cfe6ff 0%, #eaf5ff 60%, #fff9e8 100%);
}

/* Partly cloudy / overcast — soft cool gray-blue */
.ending--day-cloudy {
  background: linear-gradient(180deg, #d4dde6 0%, #e6ecf2 100%);
}

/* Fog (the SF classic) — pale luminous gray with a low diffuse glow */
.ending--day-fog {
  background:
    radial-gradient(ellipse at 50% 110%, rgba(255, 255, 255, 0.8) 0%, rgba(255, 255, 255, 0) 55%),
    linear-gradient(180deg, #dde2e6 0%, #eef1f4 100%);
}

/* Rain — cool blue-gray. Animated drops are added via .rainfield (JS) */
.ending--day-rain {
  background: linear-gradient(180deg, #aebcca 0%, #c4cfdb 70%, #d6dee7 100%);
}

/* Snow (rare in SF) — pale icy blue */
.ending--day-snow {
  background: linear-gradient(180deg, #e2edf4 0%, #f3f8fb 100%);
}

/* Thunderstorm — darker; flip text/button colors to stay readable */
.ending--day-storm {
  background: linear-gradient(180deg, #4b5563 0%, #6b7280 100%);
  color: #fff;
}
.ending--day-storm .ending__title    { color: #ffffff; }
.ending--day-storm .ending__widget   { color: #d1d5db; }
.ending--day-storm .ending__widget-sep { color: #9ca3af; }
.ending--day-storm .avatar { border-color: rgba(255,255,255,0.25); }
.ending--day-storm .btn--primary { background: #fff; color: #000; }
.ending--day-storm .btn--primary .btn__icon { filter: invert(1); }
.ending--day-storm .btn--outline { background: rgba(255,255,255,0.12); color: #fff; }
.ending--day-storm .btn--outline .btn__icon { filter: invert(1); }

/* Rainfield — JS-spawned vertical lines that fall and repeat */
.rainfield {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 0;
  overflow: hidden;
}
.rainfield .raindrop {
  position: absolute;
  top: -10%;
  width: 1px;
  height: 18px;
  background: linear-gradient(to bottom, rgba(255,255,255,0) 0%, rgba(255,255,255,0.55) 100%);
  animation: rain-fall linear infinite;
  will-change: transform, opacity;
}
@keyframes rain-fall {
  0%   { transform: translateY(0);      opacity: 0; }
  10%  { opacity: 0.8; }
  100% { transform: translateY(120vh);  opacity: 0.6; }
}

/* Fog drift — soft blurred blobs that meander horizontally */
.fogfield {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 0;
  overflow: hidden;
}
.fogfield .fogblob {
  position: absolute;
  width: 60%;
  height: 40%;
  border-radius: 50%;
  background: radial-gradient(ellipse, rgba(255,255,255,0.55) 0%, rgba(255,255,255,0) 70%);
  filter: blur(20px);
  animation: fog-drift linear infinite;
  will-change: transform;
}
@keyframes fog-drift {
  0%   { transform: translateX(-30%); }
  100% { transform: translateX(130%); }
}

@media (prefers-reduced-motion: reduce) {
  .rainfield .raindrop,
  .fogfield  .fogblob { animation: none; }
}

/* Starfield sits behind content (z-index 0) */
.starfield {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 0;
  overflow: hidden;
}
.starfield .star {
  position: absolute;
  background: #ffffff;
  border-radius: 50%;
  opacity: 0;
  animation: star-twinkle 4s ease-in-out infinite;
  will-change: opacity, transform;
}
@keyframes star-twinkle {
  0%, 100% { opacity: 0.18; transform: scale(0.85); }
  50%      { opacity: 1;    transform: scale(1.15); }
}

/* Shooting star — a small bright head with a fading tail */
.shooting-star {
  position: absolute;
  width: 3px;
  height: 3px;
  background: #ffffff;
  border-radius: 50%;
  pointer-events: none;
  box-shadow: 0 0 10px 2px rgba(255,255,255,0.65),
              0 0 20px 4px rgba(180,210,255,0.35);
  opacity: 0;
  animation: shoot 1.5s ease-out forwards;
}
.shooting-star::after {
  content: "";
  position: absolute;
  top: 50%;
  right: 100%;
  width: 110px;
  height: 1.5px;
  margin-top: -0.75px;
  background: linear-gradient(to left, rgba(255,255,255,0.9), rgba(255,255,255,0));
  border-radius: 2px;
}
@keyframes shoot {
  0%   { opacity: 0; transform: translate(0, 0)        rotate(22deg); }
  12%  { opacity: 1; }
  85%  { opacity: 0.9; }
  100% { opacity: 0; transform: translate(360px, 146px) rotate(22deg); }
}

@media (prefers-reduced-motion: reduce) {
  .starfield .star,
  .shooting-star { animation: none; }
}

.ending__cta {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;               /* gap-[10px] inside CTA */
  width: 100%;
  max-width: 1280px;
  text-align: center;
  padding: 0;              /* .ending already has the page gutter — a second one squeezed the title on phones */
}

.ending__title {
  font-family: var(--font-display);
  font-weight: 600;        /* unified main-title weight */
  font-size: var(--fs-h2);         /* unified main-title size */
  line-height: 1.25;       /* unified main-title line-height */
  letter-spacing: 0;
  margin: 0;
  color: #000;
  text-wrap: balance;      /* even line lengths when the question wraps */
}
.ending__title span { white-space: nowrap; }   /* break only between phrases */


/* Figma 2014:743 — Button Group: flex, gap-30, py-40, px-330 on a 1280 frame.
   Two fixed-width pills (300×70). Auto-stacks vertically when the row
   can't fit both pills + the 30px gap. */
.ending__buttons {
  display: flex;
  flex-wrap: wrap;         /* enables auto vertical stacking when too narrow */
  column-gap: 30px;
  row-gap: 16px;
  justify-content: center;
  align-items: center;
  padding: 40px 0;
  width: 100%;
  max-width: 630px;        /* 300 + 30 + 300 — matches Figma exactly */
  margin: 0 auto;
}
/* Each pill is a fixed 300px per Figma. flex: 0 0 300px → no growing, no
   shrinking. Once the row drops below 630, the second pill wraps below. */
.ending__buttons .btn { flex: 0 0 300px; }

/* Figma 2014:746 / 2016:2254 — Button: 300×70, rounded-pill, px-30 py-8,
   content centered, label never wraps. */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;               /* Email Ivy: Content gap-[10px] */
  width: 300px;
  height: 70px;
  padding: 8px 30px;
  border-radius: 999px;
  font-family: var(--font-display);
  font-weight: 600;        /* Circular Std Medium */
  font-size: var(--fs-h3);         /* Ivy H4 */
  line-height: 1;          /* leading-[100%] */
  letter-spacing: 0;
  white-space: nowrap;     /* label never wraps */
  transition: transform .15s ease, box-shadow .15s ease;
}
.btn > span        { white-space: nowrap; }
.btn:hover         { transform: translateY(-2px); box-shadow: 0 8px 24px rgba(0,0,0,0.15); }
.btn__icon         { width: 24px; height: 24px; flex-shrink: 0; object-fit: contain; }
/* Envelope PNG is wider than tall (~78×60), so at 24×24 it renders ~22×17
   — about 25% shorter than the square chat bubble. Bump just its render
   box so the two icons read as the same optical size. */
.btn__icon[src*="envelope"] { width: 29px; height: 29px; }

.btn--primary      { background: #000; color: #fff; }
/* Figma 2016:2254 — Chat on LinkedIn: bg rgba(0,0,0,0.05), no border,
   black text, tighter icon↔label gap (4px instead of 10px). */
.btn--outline      {
  background: rgba(0, 0, 0, 0.05);
  color: #000;
  border: 0;
  /* gap inherits the base .btn value of 10px — same as the Email Ivy pill */
}

/* Toast — used when copying email to clipboard. Translucent glass look. */
.toast {
  position: fixed;
  left: 50%;
  bottom: 36px;
  transform: translateX(-50%) translateY(20px);
  background: rgba(20, 20, 20, 0.55);
  -webkit-backdrop-filter: blur(18px) saturate(180%);
  backdrop-filter: blur(18px) saturate(180%);
  border: 1px solid rgba(255, 255, 255, 0.08);
  color: #fff;
  padding: 14px 22px;
  border-radius: 999px;
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 15px;
  line-height: 1.2;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  opacity: 0;
  pointer-events: none;
  transition: opacity .25s ease, transform .25s ease;
  z-index: 1000;
  box-shadow: 0 12px 36px rgba(0, 0, 0, 0.28);
  max-width: calc(100vw - 32px);
  white-space: nowrap;
}
.toast--show {
  opacity: 1;
  transform: translateX(-50%) translateY(0);
}
/* About: the email sits near the top of the page, so its toast drops in from the top. */
body[data-toast="top"] .toast { top: 36px; bottom: auto; transform: translateX(-50%) translateY(-20px); }
body[data-toast="top"] .toast--show { transform: translateX(-50%) translateY(0); }
@media (max-width: 900px) { body[data-toast="top"] .toast { top: 96px; } }   /* clear the floating Work / About nav */
.toast__emoji { font-size: 18px; line-height: 1; }

/* Ending CTA — live weather + SF time widget (sits under the buttons) */
.ending__widget {
  margin-top: 28px;
  display: inline-flex;
  align-items: center;
  gap: 10px;
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: var(--fs-small);
  line-height: 1;
  color: #777;
  letter-spacing: 0.02em;
}
.ending__widget-icon { font-size: 18px; line-height: 1; }
.ending__widget-sep  { opacity: 0.45; }
.ending__widget-loc,
.ending__widget-temp,
.ending__widget-time { white-space: nowrap; }

/* ============================================================
   Responsive — proportionally scale spacing & type down
   We keep the same ratios as Figma; sizes shrink with viewport.
   ============================================================ */

@media (max-width: 1200px) {
  :root { --space-page: 60px; }




}

@media (max-width: 900px) {
  :root {
    /* more breathing room on iPad (Ivy, 2026-10): small gaps stay tight, larger ones open up */
    --space-page: 48px;
    --page-top: 144px; --gap-section: 120px; --gap-ending: 80px;
    --gap-module: 200px;   /* project pages: ~190 visible between modules */
    --gap-head: 40px;  --gap-title: 16px;    --gap-block: 56px; --gap-card: 12px;
    --gap-item: 120px;
    --radius-lg: 20px; --radius-md: 16px; --radius-sm: 10px;
    --fs-display: 44px; --fs-h2: 32px; --fs-h3: 22px; --fs-lead: 18px;
    --fs-body: 16px;    --fs-small: 14px; --fs-label: 12px; --fs-stat: 40px;
    --cover-stroke: rgba(255, 255, 255, 0.10);
  }

  .site-nav           { top: 16px; right: 40px; }

  .hero               { padding-bottom: 29px; }   /* visible last bio line → "Featured Work" = contact icons → stats */
  .hero__inner        { gap: 22px; }
  .hero__title        {margin-bottom: 20px; }
  .hero__subtitle     {margin-bottom: 20px; }
  .hero__contact      { gap: 16px; margin-bottom: 20px; }
  .hero__contact-text {line-height: 28px; }
  .hero__intro > .avatar { margin-bottom: 24px; }
  .bio-stats          { margin: 2px 0 52px; }   /* iPad: icons → stats = stats → first heading ≈ 64 (breathing room, Ivy) */


  .work__title-wrap   { padding-top: 24px; }   /* was 48 */
  .project            { gap: 32px; }              /* paddings come from the spacing tokens */
  .project:first-of-type { padding-top: 42px; }   /* "Featured Work" → first card (+10, Ivy) */

  .ending             { padding-top: 90px; padding-bottom: 90px; gap: 32px; }
  .ending__buttons    { padding: 32px 0; }
  /* Keep btn dimensions at Figma values (300×70). Auto-stacking is
     handled by flex-wrap on the base .ending__buttons rule. */

  .other-projects__grid  { gap: 20px; }
  .other-project         { gap: 14px; }
}

@media (max-width: 600px) {
  :root {
    --space-page: 24px;   /* Medium: 24px screen edge */
    /* phones follow iOS: 8pt spacing grid, Dynamic Type sizes (Large Title 34 · Title1 28 · Title3 20 · Body 17 · Subhead 15 · Caption1 12) */
    /* more breathing room on phones (Ivy, 2026-10): spacing tiers 12 / 32 / 104 ≈ 1 : 2.5 : 8 */
    --page-top: 112px; --gap-section: 56px; --gap-ending: 56px;
    --gap-module: 104px;   /* project pages: ~96 visible between modules */
    --gap-head: 28px;  --gap-title: 12px;   --gap-block: 32px; --gap-card: 12px;   /* heading → media 28, media ↕ 32, card → its caption 12 */
    --gap-item: 72px;
    --radius-lg: 16px; --radius-md: 12px; --radius-sm: 8px;
    --fs-display: 34px; --fs-h2: 28px; --fs-h3: 20px; --fs-lead: 18px;
    --fs-body: 18px;   /* Medium mobile body 18 / 1.58 */    --fs-small: 15px; --fs-label: 12px; --fs-stat: 34px;
    --cover-stroke: rgba(255, 255, 255, 0.08);
    --cover-stroke-w: 0.5px;   /* hairline card strokes on phones (Ivy) */
  }

  .site-nav           { top: 12px; right: 16px; }
  .nav-item           { width: 68px; height: 68px; font-size: 15px; transition: width .25s ease, height .25s ease; }   /* fits "Resume" at 15px */
  .nav-item__shape    { border-radius: 12px; }

  .hero               { padding-bottom: 11px; }   /* visible last bio line → "Featured Work" = 58 (+10, Ivy; split 5 / 5 around the hero's edge) */
  .hero__inner        { gap: 18px; }
  .hero__intro        { gap: 8px; }
  .hero__intro > .avatar { margin-bottom: 6px; }   /* + 8 gap = 14 to the title (halved, Ivy) */
  .avatar--lg         { width: 140px; height: 140px; }
  .avatar--sm         { width: 96px; height: 96px; }

  .hero__title        { margin-bottom: 4px; }   /* + 8 gap = 12 to the subtitle (halved, Ivy) */
  .hero__subtitle     {margin-bottom: 16px;}
  .hero__contact      {
    gap: 18px;   /* email ↔ LinkedIn +4 (Ivy) */
    height: auto;
    flex-wrap: wrap;
    margin-bottom: 16px;
  }
  .hero__contact-text { display: none; }   /* phones: icons only (Ivy) */
  .hero__seg          { display: block; }   /* subtitle: one phrase per line, no "|" */
  .hero__sep          { display: none; }
  .icon-btn           { width: 44px; height: 44px; }   /* iOS minimum tap target */
  .bio-stat__label    { letter-spacing: 0.06em; }
  .project__title     { font-size: 22px; font-weight: 700; }   /* card titles one step up: iOS Title 2, bold (Ivy) */
  .project__subtitle  { font-size: 16px; }                      /* one step down: iOS Callout */
  .hero__title, .work__title { font-family: var(--font-display); font-weight: 700; letter-spacing: -0.02em; }   /* same family as the body copy on phones (Ivy) */



  .work__title-wrap   { padding-top: 25px; }   /* +5 (Ivy), +4 breathing room */
  .project            { gap: 20px; }              /* paddings come from the spacing tokens (was 36 → 72 gap) */
  .project__text      { padding: 0 2px; }
  .project:first-of-type { padding-top: 42px; }   /* "Featured Work" → first card (+10, Ivy; +8 breathing room) */

  .ending             { padding-top: 60px; padding-bottom: 60px; gap: 24px; }
  /* On very small screens (<600px), drop pill width so a 300px pill fits
     comfortably with side margin. Stacking happens via flex-wrap. */
  .ending__buttons    { padding: 20px 0; row-gap: 14px; max-width: 360px; }
  .btn                { height: 50px; padding: 8px 24px; width: 100%; font-size: 17px; }   /* iOS large button */
  .ending__buttons .btn { flex: 0 0 100%; }
  .btn__icon          { width: 20px; height: 20px; }
  .btn__icon[src*="envelope"] { width: 24px; height: 24px; }  /* same optical bump */

  .other-projects__grid  { grid-template-columns: 1fr; gap: 28px; }
  .other-project         { gap: 12px; }
}

/* ============================================================
   Multi-page: nav variants
   ============================================================ */

/* On light pages we tint the floating nav with a subtle shadow */

/* ============================================================
   ABOUT PAGE
   Layout: white hero with name + role + bio box (left-aligned, no portrait),
   then a 2-column resume (EXPERIENCE | EDUCATION/SKILLS/LANGUAGE),
   reusing the dark ending CTA at the bottom.
   ============================================================ */
.about-page { background: #FAF7F2; color: #1A1A1A; }   /* warm paper + soft black (Ivy) */

.about-hero {
  background: #FAF7F2;
  padding: calc(var(--page-top) - 10px) var(--space-page) calc(var(--gap-section) / 2);   /* page top − 10 (Ivy) */
}
.about-hero__inner {
  max-width: var(--max-content);
  margin: 0 auto;
}
.about-hero__content { display: flex; flex-direction: column; container-type: inline-size; }
/* Homepage hero water ripple (hero-ripple.js): the filter is set inline only while
   waves are alive; the glint layer brightens the crests over the dark hero. */
.hero__inner { position: relative; }
.hero-ripple__glint {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  mix-blend-mode: screen;
  opacity: 0;
  transition: opacity 0.3s ease;
}
.hero-ripple__glint.is-on { opacity: 1; }

.about-name {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: var(--fs-display);
  line-height: 1.1;
  letter-spacing: -0.02em;
  margin: 0;
  color: #1A1A1A;
}
/* Meta line in the site's mono voice (matches .resume__meta). */
.about-role {
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: var(--fs-label);      /* mono meta line → Label level */
  line-height: 1.5;
  letter-spacing: 0.02em;
  color: #666;
  margin: 16px 0 28px;
}
.about-role__sep { margin: 0 8px; color: #b5b5b5; }
.about-role__seg { white-space: nowrap; }   /* wrap only between phrases, never inside one */
/* The spans have no break opportunity between them, so when the text column is
   narrower than the one-line width (~630px at 12px) stack the three phrases instead. */
@container (max-width: 640px) {
  .about-role { display: flex; flex-direction: column; gap: 4px; }
  .about-role__sep { display: none; }
}
/* iPad + phones: the role line in the body font at 15px, colour unchanged (Ivy) */
@media (max-width: 1024px), (hover: none) and (pointer: coarse) {
  .about-hero { padding-top: calc(var(--page-top) + 10px); }   /* Resume page top: +10, then +10 more on iPad + phones (Ivy) */
  .about-role { font-family: var(--font-display); font-weight: 400; letter-spacing: 0; font-size: 15px; line-height: 1.4; }   /* 15 = iOS Subhead: name 34 → role 15 grey → body 18 (Ivy) */
  .about-role { gap: 2px; margin-bottom: 19px; }   /* the three lines stay one group; role → bio = name → role ≈ 31 visible (Ivy) */
}
/* Bio sits directly on the page (no card) at a readable measure. */
.about-bio-card { max-width: none; }   /* full content width, same as the resume below (Ivy) */
.about-bio {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: var(--fs-body);
  line-height: 1.6;
  color: #1a1a1a;
  margin: 0 0 28px;
}
.about-bio strong { font-weight: 600; color: #1A1A1A; }   /* emphasis, not fake links */
/* Contact row: LinkedIn · Email · address … Download PDF pinned to the right edge. */
.about-contact {
  display: flex;
  align-items: center;
  gap: 10px;
  font-family: var(--font-mono);
  font-size: var(--fs-small);
  line-height: 1.5;
  color: #666;
  font-weight: 400;
}
.about-contact a { color: inherit; text-decoration: none; }
.about-contact__email { margin-left: 6px; }
.about-contact__linkedin { display: none; }   /* PDF only: LinkedIn name next to its icon */
.about-contact__break,
.about-contact__site-icon,
.about-contact__site { display: none; }   /* PDF only: Portfolio row under the email */
.about-cv { margin-left: auto; display: inline-flex; align-items: center; gap: 4px;
  font-family: var(--font-display); font-size: var(--fs-body); font-weight: 600; line-height: 1.5; }   /* body font, weight of the bold bio phrases (Ivy) */
.about-contact .about-cv { color: #1A1A1A; }   /* beats .about-contact a { color: inherit } */
.about-cv svg { width: 1.5em; height: 1.5em; flex: none; display: block; }   /* icon box = the label's line height (24px web, 27px phone); its stroke scales up with it (Ivy) */
.about-contact__email:hover { color: #1A1A1A; }
.about-cv { transition: opacity .15s ease; }
.about-cv:hover { opacity: .6; }   /* already black: hover dims icon + label together */
/* On arrival: the download icon hops a few times, each hop lower (the label stays put),
   to show readers where the PDF is — plays once (Ivy) */
.about-cv svg { animation: cv-hint-hop 1.3s cubic-bezier(.3, 0, .3, 1) .7s both; }
@keyframes cv-hint-hop {
  0%, 30%, 56%, 78%, 100% { transform: translateY(0); }
  15% { transform: translateY(-7px); }
  43% { transform: translateY(-4px); }
  67% { transform: translateY(-2px); }
}
@media (prefers-reduced-motion: reduce) { .about-cv svg { animation: none; } }
@media (max-width: 600px) {
  .about-contact .about-cv { font-size: 16px; }   /* one step below the 18px phone body text (Ivy); the icon scales with it */
}

/* Resume grid */
.resume {
  background: #FAF7F2;
  color: #1A1A1A;
  padding: max(0px, calc(var(--gap-section) / 2 - 40px)) var(--space-page) var(--gap-ending);   /* hero → Experience: section gap − 40 (Ivy: −10 ×4) */
}
/* Right column has no numbered group headings, so its item titles (degrees,
   Design / Leadership) act as headings: same size as "01. ByteDance" (Ivy). */
.resume__col--right .resume__role { font-size: var(--fs-lead); line-height: 1.3; }
/* No trailing margins inside the resume, so the gap into the ending is exactly --gap-ending. */
.resume__col > :last-child,
.resume__group > :last-child,
.resume__item > :last-child { margin-bottom: 0; }
.resume__inner {
  max-width: var(--max-content);
  margin: 0 auto;
  display: grid;
  grid-template-columns: minmax(0, 1.5fr) minmax(0, 1fr);
  gap: 80px;
  position: relative;
}
.resume__col {
  position: relative;
}
/* Section label (Experience / Education / Skills / Language): mono caps with a
   rule under it, sitting above the section's content. */
.resume__col-label {
  display: block;
  width: fit-content;
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: var(--fs-label);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: #1A1A1A;
  padding: 0 4px 8px 0;
  border-bottom: 1px solid #111;
  line-height: 1.4;
  margin-bottom: 24px;
}
.resume__group + .resume__col-label { margin-top: 24px; }   /* a new section starts a little further down */

.resume__group {
  margin-bottom: 40px;
}
.resume__group-title {
  font-family: var(--font-mono);
  font-weight: 600;
  font-size: var(--fs-lead);
  line-height: 1.3;
  margin: 0 0 16px;
  color: #1A1A1A;
}
.resume__item {
  margin-bottom: 24px;
}
.resume__role {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: var(--fs-body);
  line-height: 1.4;
  margin: 0 0 4px;
  color: #1A1A1A;
}
.resume__meta {
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: var(--fs-label);
  color: #666;
  margin: 0 0 8px;
  letter-spacing: 0.02em;
  line-height: 1.4;
}
.resume__desc {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: var(--fs-small);
  line-height: 1.5;
  margin: 0 0 10px;
  color: #1a1a1a;
}
.resume__tags {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.resume__tag {
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  font-weight: 500;
  padding: 4px 10px;
  border-radius: 999px;
  background: #efefe9;
  color: #1a1a1a;
  line-height: 1.4;
  letter-spacing: 0.02em;
}
.resume__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.resume__list li {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: var(--fs-small);
  line-height: 1.5;
  color: #1a1a1a;
}

/* ============================================================
   ARTIST CAMPAIGN PAGE
   Dark theme, full-bleed sections; each section's body uses a
   1280-reference content frame from Figma rendered as an image.
   ============================================================ */
.case-page  { background: #000; color: #fff; }

.case-hero {
  padding: var(--page-top) 0 0;   /* same page-top as every other page (was 280 to match the old baked-image pages) */
  background: #000;
  text-align: center;
}
.case-hero__brand {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0;
  padding: 0 var(--space-page);
}
.case-hero__logo {
  height: 58px;
  width: auto;
  margin-bottom: 26px;
}
.case-hero__title {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: var(--fs-display);
  line-height: 1.1;
  letter-spacing: -0.02em;
  margin: 0 0 20px;
  color: #fff;
}
.case-hero__year {
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: var(--fs-label);
  line-height: 1.4;
  letter-spacing: 0.12em;
  color: #fff;
  margin: 0;
  text-transform: uppercase;   /* YEAR … on every case study, like the other mono labels */
}
/* Option A — editorial minimal: muted, wide-tracked uppercase year.
   Scoped via the adjacent title so SoundOn's shared __year is untouched. */
.case-hero__title + .case-hero__year {
  
  color: #8d8d80;
  
  text-transform: uppercase;
}
.case-hero__image {
  margin: 60px auto 0;
  border-radius: var(--radius-lg);
  border: var(--cover-stroke-w) solid var(--cover-stroke);   /* 1px white @40% frame */
  box-sizing: border-box;                        /* keep the 1px inside the aspect-ratio box */
  overflow: hidden;
  max-width: var(--max-content);  /* unified narrow tier (was 1152) */
  aspect-ratio: 1152 / 648;
  background: #000;
  position: relative;
}
/* Limit to the *direct* media child so nested icons (volume button etc.)
   aren't forced to display:block — which would beat the toggle rules. */
.case-hero__image > img,
.case-hero__image > video,
.case-hero__image > .video-player__video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* ---------- Custom video player ---------- */
.video-player {
  cursor: pointer;
}
.video-player__video {
  position: absolute;
  inset: 0;
}

/* Centered play overlay (paused state) */
.video-player__play-btn {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 88px;
  height: 88px;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.55);
  border: 1px solid rgba(255, 255, 255, 0.1);
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  padding: 0;
  transition: transform .2s ease, background .2s ease, opacity .25s ease, border-color .2s ease;
  z-index: 2;
}
.video-player__play-btn:hover {
  background: rgba(0, 0, 0, 0.75);
  border-color: rgba(255, 255, 255, 0.25);
  transform: translate(-50%, -50%) scale(1.05);
}
.video-player__play-btn svg {
  width: 50px;   /* 72 × 0.7 */
  height: 50px;
  fill: #fff;
  /* The SVG path already shifts the triangle so its centroid sits at
     viewBox center (x ≈ 12 of 24) — no margin-left needed for optical
     centering against the round button. */
}
.video-player[data-state="playing"] .video-player__play-btn {
  opacity: 0;
  pointer-events: none;
}

/* Bottom controls bar (revealed on hover while playing) */
.video-player__controls {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  padding: 18px 20px 16px;
  display: flex;
  align-items: center;              /* centers pause / center-stack / volume */
  gap: 14px;
  background: linear-gradient(to top, rgba(0,0,0,0.7) 0%, rgba(0,0,0,0) 100%);
  opacity: 0;
  transform: translateY(8px);
  transition: opacity .2s ease, transform .2s ease;
  pointer-events: none;
  z-index: 2;
}
/* shown by the page script (.is-controls) for 3s after play / mouse movement, or while a
   control has keyboard focus — not on :hover, which sticks after a tap on touch screens */
.video-player[data-state="playing"].is-controls .video-player__controls,
.video-player[data-state="playing"]:has(:focus-visible) .video-player__controls {
  opacity: 1;
  transform: translateY(0);
  pointer-events: auto;
}

/* Time + progress bar stacked as one unit; the row's align-items:center then
   vertically centers this whole stack against the side icons. */
.video-player__center {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;                         /* time ↔ progress-bar spacing (current +4px) */
}
.video-player__time {
  align-self: flex-start;           /* left-aligned, above the bar */
  margin: 0;
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: 13px;
  line-height: 1;
  color: rgba(255, 255, 255, 0.92);
  letter-spacing: 0.02em;
  text-shadow: 0 1px 2px rgba(0,0,0,0.4);
}

/* Shared circular control button (pause / volume) — matches the centered
   play overlay: translucent dark disc with a white icon, so it reads on top
   of the video without the solid-white button blocking the frame. */
.video-player__round-btn {
  width: 36px;
  height: 36px;
  border-radius: 50%;
  border: 1px solid rgba(255, 255, 255, 0.1);
  background: rgba(0, 0, 0, 0.55);
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  padding: 0;
  flex-shrink: 0;
  transition: background .2s ease, border-color .2s ease, transform .2s ease;
}
.video-player__round-btn:hover {
  background: rgba(0, 0, 0, 0.75);
  border-color: rgba(255, 255, 255, 0.25);
  transform: scale(1.05);
}
.video-player__round-btn svg,
.video-player__round-btn img {
  width: 18px;
  height: 18px;
  fill: #fff;
}
/* Volume icon is an <img> SVG — force it white to match the white pause icon. */
.video-player__round-btn img { filter: brightness(0) invert(1); }

/* Volume icon swap based on muted state */
.video-player__volume-icon--off { display: none; }
.video-player[data-muted="true"]  .video-player__volume-icon--on  { display: none; }
.video-player[data-muted="true"]  .video-player__volume-icon--off { display: block; }

.video-player__progress {
  flex: 1 1 auto;
  height: 4px;
  background: rgba(255, 255, 255, 0.3);
  border-radius: 999px;
  cursor: pointer;
  position: relative;
}
.video-player__progress-fill {
  height: 100%;
  width: 0%;
  background: #fff;
  border-radius: inherit;
  transition: width .1s linear;
}
/* Draggable scrub handle, centred on the current playback position. */
.video-player__progress-thumb {
  position: absolute;
  top: 50%;
  left: 0;
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: #fff;
  transform: translate(-50%, -50%);
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.45);
  pointer-events: none;            /* the bar handles the drag */
  transition: left .1s linear;
}
.video-player__progress:focus { outline: none; }
.video-player__progress:focus-visible { outline: 2px solid rgba(255, 255, 255, 0.6); outline-offset: 4px; }

.case-meta {
  padding: 60px var(--space-page) calc(var(--gap-module) / 2);
  background: #000;
}
.case-meta__inner {
  max-width: var(--max-content);  /* unified narrow tier (was 1120) */
  margin: 0 auto;
  display: grid;
  grid-template-columns: 280px 1fr;
  gap: 60px;
  align-items: start;
}
.case-meta__row { margin-bottom: 24px; }
.case-meta__row:last-child { margin-bottom: 0; }   /* was adding 24px to the gap below the meta block */
.case-meta__label {
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: var(--fs-label);
  letter-spacing: 0.12em;
  color: #999;
  margin: 0 0 8px;
  text-transform: uppercase;
  line-height: 1.4;
}
.case-meta__value {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: var(--fs-lead);
  line-height: 1.4;
  color: #fff;
  margin: 0;
}
.case-meta__about {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: var(--fs-body);
  line-height: 1.6;
  color: #d4d4d4;
  margin: 0;
}
.case-meta__about-title {
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: var(--fs-label);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: #999;
  margin: 0 0 8px;
  line-height: 1.4;
}

/* Section header used across the case study */
.case-section {
  padding: calc(var(--gap-module) / 2) var(--space-page);
  background: #000;
  color: #fff;
}
.case-section--light {
  background: #FAF7F2;
  color: #1A1A1A;
}
/* Continuation blocks (no heading) that belong to the section above:
   block gap above the first one, stacked flush with each other. */
.case-section:has(+ .case-section--cont) { padding-bottom: var(--gap-block); }
.case-section--cont { padding-top: 0; }
.case-section--cont:has(+ .case-section--cont) { padding-bottom: 0; }
.case-section--light .case-section__title { color: #1A1A1A; }
.case-section--light .case-section__sub   { color: var(--sub-color-light); }

.case-section__head {
  text-align: center;
  margin: 0 auto calc(var(--gap-head) + 6px);   /* section subtitle → its content: +6 (Ivy) */
  max-width: 1098px;        /* caps title/subtitle width to match module media/video width */
}
.case-section__title {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: var(--fs-h2);
  line-height: 1.25;
  /* -0.27em trims the line box above the cap height, so a section gap measures
     to the visible top of the title (equal halves at a dark → light boundary). */
  margin: -0.27em 0 var(--gap-title);
  color: #fff;
}
.case-section__sub {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: var(--fs-lead);          /* unified with melor/soundon module subtitles (was 18px) */
  line-height: 1.4;
  color: var(--sub-color);
  margin: 0;
}

.case-section__media {
  display: block;
  margin: 0 auto;
  /* Unified module width = 1152px (caps the wider exports: data-viz 1259,
     strategy 1278, image breaks 1280 → 1152). width:auto still prevents any
     upscaling, so narrower exports (album/gameplays at 1100) keep their size
     and are centered rather than stretched. */
  max-width: min(100%, 1152px);
  width: auto;
  border-radius: 0;
}
.case-section__media--rounded { border-radius: var(--radius-lg); overflow: hidden; }

/* Game mechanics — 4 gameplay cards: 20px radius, 30px gaps, 1px white @30% inner stroke. */
.ac-gameplays { max-width: min(100%, 1100px); margin: 0 auto; display: flex; flex-direction: column; gap: 30px; }
.ac-gp { position: relative; border-radius: var(--radius-md); overflow: hidden; }
.ac-gp img { display: block; width: 100%; }
.ac-gp::after { content: ""; position: absolute; inset: 0; border: var(--cover-stroke-w) solid var(--cover-stroke); border-radius: var(--radius-md); pointer-events: none; }

/* ---------- Business strategy diagram + Option-A Before/After ---------- */
/* Enlarged to 1.5× (1152 → 1728) and sourced from the 2× export for sharpness. */
.bizstrat {
  position: relative;
  width: 100%;
  max-width: 1728px;
  margin: 0 auto;
  container-type: inline-size;
}
.bizstrat > picture { display: block; }
.bizstrat .case-section__media { display: block; width: 100%; max-width: none; margin: 0; }
/* Two crisp SVG diagrams side by side */
.bizstrat--svg {
  max-width: 1280px;
  display: flex;
  gap: 2.5%;
  align-items: flex-start;
  justify-content: center;
}
.bizstrat__svg { width: 48.75%; height: auto; display: block; }
@media (max-width: 700px) {
  .bizstrat--svg { flex-direction: column; align-items: center; gap: 32px; }
  .bizstrat__svg { width: 86%; }
}
/* Animated Before → After arrow between the two diagrams */
.ba-arrow {
  position: absolute; top: 5%; left: 50%; z-index: 3;
  width: 4.2cqw; height: 0.16cqw; border-radius: 2px;
  background: #D9D9D9;   /* same grey as the diagrams' curved arrows (was a grey→black gradient) */
  transform: translate(-50%, -50%);
  animation: baSlide 1.6s ease-in-out infinite;
}
.ba-arrow::after {
  content: ""; position: absolute; right: -0.05cqw; top: 50%;
  width: 0.7cqw; height: 0.7cqw;
  border-top: 0.16cqw solid #D9D9D9; border-right: 0.16cqw solid #D9D9D9;
  transform: translateY(-50%) rotate(45deg);
}
@keyframes baSlide {
  0%, 100% { transform: translate(-50%, -50%); }
  50%      { transform: translate(calc(-50% + 0.5cqw), -50%); }
}
@media (prefers-reduced-motion: reduce) { .ba-arrow { animation: none; } }

/* ---------- Campaign type cards (Option A) ---------- */
.cc-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 24px;
  max-width: 1152px;
  margin: 0 auto;
}
.cc-card {
  position: relative;
  background: #fff;
  border: 1px solid #ececf0;
  border-radius: var(--radius-md);
  padding: 16px 16px 22px;
  overflow: hidden;
  transition: transform .3s ease, box-shadow .3s ease;
}
.cc-card:hover { transform: translateY(-8px); box-shadow: 0 26px 50px rgba(20, 20, 30, .14); }
.cc-card__ghost {
  position: absolute; top: 6px; right: 16px; z-index: 0;
  font-family: var(--font-display); font-weight: 800; font-size: 64px;
  line-height: 1; color: #f1f1f4; pointer-events: none;
}
.cc-card__ph { display: block; width: 100%; border-radius: calc(var(--radius-md) - 6px); }
.cc-card__body { position: relative; z-index: 1; padding: 14px 6px 0; }
.cc-card__title {
  font-family: var(--font-display); font-weight: 600;
  font-size: var(--fs-h3); line-height: 1.25; margin: 0 0 var(--gap-card); color: #1A1A1A;
}
.cc-card__desc {
  font-family: var(--font-mono); font-size: var(--fs-small); line-height: 1.5;
  color: #8a8a92; margin: 0;
  font-weight: 400;
}
.cc-card__bar {
  display: block; height: 3px; width: 0; background: #b9b9b9;
  border-radius: 2px; margin-top: 14px; transition: width .35s ease;
}
.cc-card:hover .cc-card__bar { width: 48px; }
/* Narrow screens: the three cards become a horizontal swipe track that bleeds to
   the screen edges (the next card peeks in as the cue). */
@media (max-width: 720px) {
  .cc-grid {
    grid-template-columns: none;
    grid-auto-flow: column;
    grid-auto-columns: min(78%, 300px);
    gap: 16px;
    max-width: none;
    margin: 0 calc(-1 * var(--space-page));
    padding: 0 var(--space-page);
    overflow-x: auto;
    overscroll-behavior-x: contain;
    scroll-snap-type: x mandatory;
    scroll-padding-inline: var(--space-page);
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
  }
  .cc-grid::-webkit-scrollbar { display: none; }
  .cc-card { scroll-snap-align: start; }
  .cc-card:hover { transform: none; box-shadow: none; }   /* no lift inside the clipped track */
}

/* ---------- Click-to-zoom lightbox ---------- */
.zoomable { cursor: zoom-in; }
.lightbox {
  position: fixed; inset: 0; z-index: 2000;
  background: rgba(8, 8, 10, 0.93);
  display: none; align-items: center; justify-content: center;
  overflow: hidden; padding: 5vh 4vw;
  -webkit-overflow-scrolling: touch;
}
/* Elegant entrance: backdrop eases in, image lifts + scales up gently. */
.lightbox.is-open { display: flex; animation: lb-fade .30s ease both; }
.lightbox.is-open .lightbox__img  { animation: lb-zoom .46s cubic-bezier(.16,.84,.34,1) both; }
.lightbox.is-open .lightbox__close,
.lightbox.is-open .lightbox__hint  { animation: lb-fade .4s ease .18s both; }
@keyframes lb-fade { from { opacity: 0; } to { opacity: 1; } }
@keyframes lb-zoom {
  from { opacity: 0; transform: scale(.94) translateY(8px); }
  to   { opacity: 1; transform: scale(1)   translateY(0); }
}
@media (prefers-reduced-motion: reduce) {
  .lightbox.is-open,
  .lightbox.is-open .lightbox__img,
  .lightbox.is-open .lightbox__close,
  .lightbox.is-open .lightbox__hint { animation: none; }
}
/* Default: fit the whole image to the viewport (no scrolling) */
.lightbox__img {
  display: block; margin: auto;
  max-width: 100%; max-height: 90vh; width: auto; height: auto;
  border-radius: 12px; box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);
}
/* Kept for compatibility — same as the default now */
.lightbox--fit { align-items: center; overflow: hidden; padding: 5vh 4vw; }
.lightbox--fit .lightbox__img { max-width: 100%; max-height: 90vh; width: auto; height: auto; }
.lightbox__close {
  position: fixed; top: 18px; right: 22px; width: 44px; height: 44px;
  border-radius: 50%; border: 1px solid rgba(255, 255, 255, 0.25);
  background: rgba(0, 0, 0, 0.5); color: #fff; font-size: 24px; line-height: 1;
  display: flex; align-items: center; justify-content: center; cursor: pointer; z-index: 2001;
  transition: background .15s ease;
}
.lightbox__close:hover { background: rgba(0, 0, 0, 0.8); }
.lightbox__hint {
  position: fixed; bottom: 18px; left: 0; right: 0; text-align: center;
  color: rgba(255, 255, 255, 0.55); font-size: var(--fs-small); font-family: var(--font-display);
  pointer-events: none;
  font-weight: 400;
  line-height: 1.5;
}

/* ============================================================
   Responsive — about & case pages
   ============================================================ */
@media (max-width: 900px) {
  .about-bio-card { max-width: none; }
  .about-contact { flex-wrap: wrap; }

  .resume__inner {
    grid-template-columns: 1fr;
    gap: 48px;
  }

  .case-hero__image { margin-left: var(--space-page); margin-right: var(--space-page); }
  .case-meta { padding-top: 48px; }
  .case-meta__inner {
    grid-template-columns: 1fr;
    gap: 24px;
  }
}

@media (max-width: 600px) {
  .about-role {margin: 12px 0 12px; }   /* role → bio = name → role ≈ 24 visible (Ivy) */
  .about-contact__email { display: none; }   /* phones: icons + Download resume fit on one row (email icon still copies) */
  .resume { padding-top: 6px; }   /* contact row → Experience +6 on phones (Ivy) */

  .resume__group { margin-bottom: 28px; }

  .case-hero__brand { gap: 0; }
  .case-hero__logo { height: 48px; }
  .case-hero__image { margin: 40px var(--space-page) 0; }
  .case-meta { padding-top: 32px; }
}

/* ---------- SoundOn modular case study helpers ---------- */
/* Hero banner — no outer stroke, width aligned to the Figma banner (1192px). */
.case-hero__media {
  display: block;
  margin: 60px auto 0;
  width: min(100% - 2 * var(--space-page), 1192px);
  height: auto;
  border-radius: var(--radius-lg);
}
.case-hero__logo--wordmark { height: auto; width: min(360px, 56%); margin-bottom: 22px; }
.case-hero__tagline {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: var(--fs-lead);
  line-height: 1.4;
  color: var(--sub-color);
  max-width: 640px;
  text-align: center;
  margin: 0 0 16px;
}
/* Stacked media inside a single module (rebranding, website, cross-platform) */
.so-stack { display: flex; flex-direction: column; align-items: center; gap: 30px; }
.so-stack > img { display: block; margin: 0; max-width: min(100%, 1152px); width: auto; }
.so-stack__video { display: block; margin: 0; width: 100%; height: auto; max-width: min(100%, 1152px); border-radius: var(--radius-lg); }

/* Cross-platform — Before / After upload + 4-step process (HTML) */
.so-cross2 {
  max-width: min(100%, 1120px);
  margin: 0;
  display: grid;
  grid-template-columns: 1fr 1fr 230px;
  gap: 26px;
  align-items: start;
}
.so-cross2__shot { margin: 0; }
.so-cross2__cap {
  font-family: var(--font-mono); font-weight: 500; font-size: var(--fs-label);
  line-height: 1;
  letter-spacing: 0.12em; text-transform: uppercase; color: var(--sub-color);
  margin: 0 0 10px;
}
.so-cross2__cap--after { color: #ccff00; }
.so-cross2__shot img,
.so-cross2__shot video {
  display: block; width: 100%; aspect-ratio: 870 / 610; object-fit: cover;
  border-radius: var(--radius-md);
  border: var(--cover-stroke-w) solid var(--cover-stroke); box-sizing: border-box;
}
/* Top of steps aligns with the top of the screenshot (caption 11px + 10px margin) */
.so-cross2__steps { list-style: none; margin: 21px 0 0; padding: 0; display: flex; flex-direction: column; gap: 18px; }
.so-cross2__steps li { display: flex; flex-direction: column; gap: 5px; }
.so-cross2__steps b { font-family: var(--font-display); font-weight: 600; font-size: var(--fs-small); line-height: 1.3; color: #fff; }
.so-cross2__steps span { font-family: var(--font-display); font-weight: 400; font-size: var(--fs-small); color: var(--sub-color); line-height: 1.5; }
@media (max-width: 760px) {
  .so-cross2 { grid-template-columns: 1fr 1fr; }
  .so-cross2__steps { grid-column: 1 / -1; flex-direction: row; flex-wrap: wrap; gap: 16px 28px; }
}

/* WWA 3.0 — four phone cards (HTML; phone mockups from Figma) */
.wwa-grid {
  max-width: min(100%, 1098px);
  margin: 20px auto 0;
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 20px;
}
.wwa-card {
  background: #181818;
  border-radius: var(--radius-md);
  padding: 28px 16px 26px;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
}
.wwa-card__tag {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: var(--fs-small);
  color: var(--sub-color);
  margin: 0 0 8px;
  line-height: 1.5;
}
.wwa-card__title {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: var(--fs-h3);
  line-height: 1.25;
  color: #fff;
  margin: 0 0 26px;
  min-height: 2.5em;            /* room for two lines, so phones line up when one title wraps */
  display: flex; align-items: center; justify-content: center;
}
.wwa-card__phone { width: 72%; max-width: 200px; margin: 0 auto; }
.wwa-card__phone img { width: 100%; display: block; }
.wwa-card__stat {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: var(--fs-small);
  line-height: 1.5;
  color: var(--sub-color);
  margin: auto 0 0;
  padding-top: 24px;
}
@media (max-width: 760px) {
  .wwa-grid { grid-template-columns: 1fr 1fr; gap: 14px; }
}

/* Rebranding video (converted from GIF) — fixed sizing + 1px white@30% stroke */
.so-rebrand {
  margin: 0 auto;
  width: 100%;
  max-width: min(100%, 1152px);
  aspect-ratio: 2304 / 1286;
  border-radius: var(--radius-lg);
  overflow: hidden;
  border: var(--cover-stroke-w) solid var(--cover-stroke);
  box-sizing: border-box;
  background: #000;
}
.so-rebrand-video {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Official website redesign — Before / After blocks */
.so-web { display: flex; flex-direction: column; align-items: center; gap: 16px; }
.so-web__label {
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: var(--fs-label);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--sub-color);
  margin: 0;
  line-height: 1.4;
}
.so-web__label--after { margin-top: 22px; color: #ccff00; }
.so-web__shot { display: block; width: 100%; height: auto; max-width: min(100%, 1120px); margin: 0 auto; }
.so-web__frame {
  position: relative;
  overflow: hidden;
  border-radius: var(--radius-lg);
  border: var(--cover-stroke-w) solid var(--cover-stroke);
  box-sizing: border-box;
  background: #000;
}
.so-web__frame > video { display: block; width: 100%; height: 100%; object-fit: cover; }
.so-web__shot--before { max-width: min(100%, 680px); }
/* WWA inline video + its caption */
.so-label {
  text-align: center;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: var(--fs-body);
  color: #ccff00;
  margin: 0 0 20px;
  line-height: 1.6;
}
/* WWA video — custom player frame (controls/styles shared with Artist Campaign) */
.so-video-player {
  position: relative;
  margin: 0 auto 30px;
  width: 100%;
  max-width: min(100%, 1098px);
  aspect-ratio: 16 / 9;
  border-radius: var(--radius-md);
  overflow: hidden;
  border: var(--cover-stroke-w) solid var(--cover-stroke);
  box-sizing: border-box;
  background: #000;
}
.so-video-player > .video-player__video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
/* Onboarded-artists showcase */
.so-artists {
  max-width: min(100%, 1152px);
  margin: 0 auto;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 16px;
}
.so-artist {
  position: relative;
  margin: 0;
  border-radius: var(--radius-md);
  overflow: hidden;
  aspect-ratio: 620 / 757;
  background: #111;
}
.so-artist::after {
  content: "";
  position: absolute;
  inset: 0;
  border: var(--cover-stroke-w) solid var(--cover-stroke);
  border-radius: var(--radius-md);
  pointer-events: none;
}
.so-artist > img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* Data board — two figures and a divider straight on the page (no panel) */
.so-recap {
  max-width: min(100%, 660px);
  margin: var(--gap-block) auto 0;
  display: grid;
  grid-template-columns: 1fr 1px 1fr;
  align-items: start;              /* figures stay level even when a label wraps */
}
.so-recap__item {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
  text-align: center;
  padding: 2px 18px;
}
.so-recap__num {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: var(--fs-stat);
  line-height: 1;
  letter-spacing: -0.015em;
  background: linear-gradient(180deg, #ffffff 0%, #c7cace 100%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
/* "1.5 M" — figure and unit at the same size, so the M is as tall as the digits */
.so-recap__big,
.so-recap__unit {
  display: inline-block;
  vertical-align: baseline;        /* sit on the same baseline so caps align */
  line-height: 1;
  letter-spacing: -0.015em;
  background: linear-gradient(180deg, #ffffff 0%, #c7cace 100%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
.so-recap__big  { font-size: var(--fs-stat); }
.so-recap__unit { font-size: var(--fs-stat); margin-left: 2px; }
.so-recap__count {
  background: linear-gradient(180deg, #ffffff 0%, #c7cace 100%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
/* Reveal + count-up animation */
.so-recap { transition: opacity .6s ease, transform .6s ease; }
.so-recap.is-pending { opacity: 0; transform: translateY(14px); }
.so-recap__label {
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: var(--fs-label);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--sub-color);
  line-height: 1.4;
}
.so-recap__div {
  width: 1px;
  height: 64px;
  align-self: center;
  background: linear-gradient(180deg, rgba(255,255,255,0) 0%, rgba(255,255,255,0.24) 50%, rgba(255,255,255,0) 100%);
}

/* AI tools — two captioned screenshots side by side */
.so-ai-grid {
  max-width: min(100%, 1152px);
  margin: 0 auto;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 30px;
}
.so-ai-item { margin: 0; }
.so-ai-cap { font-family: var(--font-display); font-weight: 400; font-size: var(--fs-small); color: var(--sub-color); margin: 0 0 12px; line-height: 1.5; }
.so-ai-item > img { display: block; width: 100%; border-radius: var(--radius-md); border: var(--cover-stroke-w) solid var(--cover-stroke); box-sizing: border-box; }

/* ---------- Why-onboard module — pure HTML cards (no image) ---------- */
.why-grid {
  max-width: min(100%, 1120px);
  margin: 0 auto;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 36px;
}
.why-card {
  position: relative;
  overflow: hidden;
  border-radius: var(--radius-lg);
  padding: 36px;
  min-height: 360px;
  display: flex;
  flex-direction: column;
}
.why-card__title {
  position: relative;
  z-index: 2;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: var(--fs-h3);
  line-height: 1.25;
  letter-spacing: -0.01em;
  margin: 0 0 16px;          /* minimum gap when the desc (pinned to the bottom) runs up to the title */
  max-width: 64%;
}
.why-card__desc {
  position: relative;
  z-index: 2;
  margin: auto 0 0;
  max-width: 60%;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: var(--fs-body);
  line-height: 1.6;
}
.why-card--light { background: #fff; }
.why-card--light .why-card__title { color: #0a0a0a; }
.why-card--light .why-card__desc  { color: #4a4a4a; }
.why-card--red, .why-card--cyan { color: #fff; }
.why-card--red .why-card__title, .why-card--cyan .why-card__title { color: #fff; }
.why-card--red .why-card__desc,  .why-card--cyan .why-card__desc  { color: rgba(255, 255, 255, 0.92); }
/* Concentric-ring card backgrounds (exported SVG from Figma) */
.why-card--red  { background: #fe2c55 url("assets/so-why-card2.svg") center / cover no-repeat; }
.why-card--cyan { background: #00bce0 url("assets/so-why-card3.svg") center / cover no-repeat; }
/* TikTok app-icon tile (card 1) */
.why-icon {
  position: absolute;
  z-index: 2;
  right: 24px;
  bottom: 32px;
  width: 144px;
  height: 144px;
  display: block;
}
/* Pill tags (card 4) — exported SVG from Figma */
.why-tags-img {
  position: absolute;
  z-index: 2;
  right: 28px;
  bottom: 44px;
  width: 190px;
  height: auto;
  display: block;
}

@media (max-width: 760px) {
  .case-hero__logo--wordmark { width: min(260px, 70%); }
  .so-recap__item { padding: 2px 8px; gap: 12px; }
  .so-recap__div { height: 52px; }
  .so-ai-grid { grid-template-columns: 1fr; gap: 24px; }
  .why-grid { grid-template-columns: 1fr; gap: 20px; }
  .why-card { min-height: 300px; padding: 28px; }
  .why-card__title {max-width: 70%; }
  .why-card__desc { max-width: 72%; }
  .why-icon { width: 104px; height: 104px; right: 20px; bottom: 24px; }
  /* Narrow cards: the absolute decorations covered the description — put them in the
     flow between title and description, right-aligned. */
  .why-icon,
  .why-tags-img { position: static; order: 1; align-self: flex-end; margin: 16px 0; }
  .why-tags-img { width: 170px; }
  .why-card__desc { order: 2; }
}

/* Reduced motion */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    transition: none !important;
    animation: none !important;
  }
}

/* ============================================================
   Print / "Download PDF" — the About page exported as a clean CV
   (assets/Ivy-Chen-CV.pdf is generated from this; see deploy/make-cv-pdf.mjs)
   ============================================================ */

/* ============================================================
   Phones — Medium reading rhythm (Ivy, via Muse)
   edge 24 · media ↕ 20 · small items 16 · paragraph ↕ 29 · before H2 48–56 · after H2 16
   body 18 / 1.58 · caption 14 / 1.5 grey, 8–12 from its image · quote: 3px left rule + 12px
   ============================================================ */
@media (max-width: 600px) {
  .bio-text, .about-bio, .case-meta__about, .case-section__sub,
  .md-basic__sub, .why-card__desc, .cc-card__desc { line-height: 1.58; }

  /* homepage bio: heading → its text 16, text → next heading 48 (hero__bio gap is 12) */
  /* bio headings + paragraphs match the project cards below: 22/700 title, 16/400 text, 8px apart (Ivy) */
  .bio-heading { font-size: 22px; font-weight: 700; line-height: 1.25; }
  .hero__bio .bio-text, .hero__subtitle { font-size: 16px; }   /* subtitle under the name = body size too (Ivy) */
  .hero__bio .bio-heading + .bio-text { margin-top: -4px; }
  .hero__bio .bio-text + .bio-heading { margin-top: 27px; }   /* visible paragraph → heading = 40, same as "Featured Work" → first cover */
  /* stats as two row cards, 6px apart (Ivy): each row is one card — 2% white fill,
     0.5px 3% outline, 12px corners — with its two numbers side by side, no divider */
  .hero__contact { margin-bottom: 0; }
  .bio-stats { display: grid; grid-template-columns: 1fr 1fr; gap: 6px 0; margin: 18px 0 39px; background: none; border: 0; border-radius: 0; }   /* visible gaps: icons → cards 56, cards → first heading 56 */
  .bio-stat {
    padding: 12px;
    background: rgba(255, 255, 255, 0.02);
    border: 0.5px solid rgba(255, 255, 255, 0.03);
    border-radius: 0;
  }
  .bio-stat:nth-child(odd)  { border-left: 0.5px solid rgba(255, 255, 255, 0.03); border-right: 0; border-radius: 12px 0 0 12px; padding-left: 12px; }   /* left half of a row card */
  .bio-stat:nth-child(even) { border-left: 0;  border-radius: 0 12px 12px 0; padding-left: 12px; }   /* right half */
  /* labels always on one line: 12px at 390 wide, shrinking a touch on narrower phones */
  .bio-stat__label { letter-spacing: 0; white-space: nowrap; font-size: min(12px, 2.95vw); }

  .about-name { font-weight: 700; }   /* same weight as the homepage name on phones (Ivy) */
  /* phones: icon discs at 8% white, glyphs white (Ivy) */
  .hero__contact .icon-btn__svg [fill="#ffffff"] { fill-opacity: 0.08; }   /* 8% white disc (Ivy) */
  .hero__contact .icon-btn__svg [fill="#272727"] { fill: #fff; }
  .hero__contact .icon-btn { outline: 0.5px solid rgba(255, 255, 255, 0.03); outline-offset: -0.5px; }   /* same hairline as the stat cards */

  /* homepage body copy at 60% white: secondary to headings and titles (Ivy) */
  .bio-text, .project__subtitle, .hero__subtitle { color: rgba(255, 255, 255, 0.6); }

  /* consecutive paragraphs: 29px */
  .case-meta .case-meta__about + .case-meta__about { margin-top: 29px; }

  /* captions */
  .so-ai-cap, .mel-fb .mel-fb__foot { font-size: 14px; line-height: 1.5; color: #8a8a8a; }
  .so-ai-cap { margin-bottom: 10px; }

  /* user quotes (Melor feedback): a plain list with grey dots instead of bubbles */
  .mel-fb .mel-fb__list { gap: 16px; justify-content: flex-start; }   /* melor.css loads later: extra class wins */
  .mel-fb .mel-fb__list li { position: relative; background: none; border-radius: 0; padding: 0 0 0 16px; font-size: var(--fs-body); line-height: 1.58; }
  .mel-fb .mel-fb__list li::before {   /* small grey dot on the first line instead of a black rule (Ivy) */
    content: ""; position: absolute; left: 2px; top: calc(0.79em - 3px);
    width: 6px; height: 6px; border-radius: 50%; background: #b3b3b3;
  }
}

/* ============================================================
   Project pages on phones + iPads: Back only (no Work / Resume);
   also the iPad nav size while scrolling (contact-actions.js sets html.nav-compact)
   ============================================================ */
@media (max-width: 1024px), (hover: none) and (pointer: coarse) {
  .case-page:has(.nav-back) .site-nav { display: none; }
  /* Back: plain white by default; once content scrolls under it, a chip fades in behind it
     (contact-actions.js sizes .nav-back-chip to Back + 6px), styled like the video player's
     icon buttons: 55% black fill, 1px 10% white outline (Ivy). The chip is a separate layer so Back keeps its difference-blend text.
     Back stays centred where it always sat (the nav band); desktop keeps the plain link. */
  .nav-back { height: auto; translate: 0 -50%; top: 60px; }   /* 16 + 88/2 */
  .nav-back { mix-blend-mode: normal; }   /* arrow + label always white, not inverted over light sections (Ivy) */
  .nav-back .nav-back__arrow { width: 24px; height: 24px; }   /* +4 (Ivy) */
  .nav-back:hover { opacity: 1; }   /* no hover dim / arrow nudge on touch screens (Ivy) */
  .nav-back:hover .nav-back__arrow { transform: none; }
  .nav-back-chip {
    display: block; position: fixed; z-index: 99; pointer-events: none;
    background: rgba(0, 0, 0, 0.55);           /* same as the video player's round icon buttons */
    border: 1px solid rgba(255, 255, 255, 0.1);
    border-radius: 999px;
    opacity: 0; transition: opacity .2s ease;
  }
  .nav-back-chip.is-on { opacity: 1; }
  .nav-back::after { content: ""; position: absolute; inset: -10px; }   /* keeps a finger-sized tap area around the small chip */
  /* iPad nav: shrinks to 88% while scrolling down (88 → 77), back to 88 on scroll up (Ivy) */
  .nav-item { transition: width .25s ease, height .25s ease; }
  html.nav-compact .nav-item { width: 77px; height: 77px; }
}
/* Full-colour modules (blue "Basic experience", light sections): their colour already marks
   the edge, so they get more room inside instead — 80px on iPad, 48px on phones (Ivy) */
/* Video play button: 88px is for desktop; iPad 64, phones 56 (Ivy) */
@media (max-width: 900px) {
  .video-player__play-btn { width: 64px; height: 64px; }
  .video-player__play-btn svg { width: 36px; height: 36px; }
}
@media (max-width: 600px) {
  .video-player__play-btn { width: 56px; height: 56px; }
  .video-player__play-btn svg { width: 30px; height: 30px; }
}
@media (max-width: 900px) {
  .case-section.md-basic, .case-section--light { padding-top: 80px; padding-bottom: 80px; }
}
@media (max-width: 600px) {
  .case-section.md-basic, .case-section--light { padding-top: 48px; padding-bottom: 48px; }
}
/* Music Discovery: the Full Map phones → blue "Basic experience" band had too little air —
   +6 on both sides of the band's edge, iPad + phones (Ivy) */
@media (max-width: 900px) {
  .case-section:has(+ .md-basic) { padding-bottom: calc(var(--gap-module) / 2 + 6px); }
  .case-section.md-basic { padding-top: 86px; }
}
@media (max-width: 600px) {
  .case-section.md-basic { padding-top: 54px; }
}
@media (max-width: 600px) {
  html.nav-compact .nav-item { width: 60px; height: 60px; }   /* phones: 68 → 60 while scrolling down, label size unchanged (Ivy) */
  /* phones: Back is just the chevron in a 44 × 44 circle (the label stays for screen readers
     via aria-label); the circle's fill is the scroll chip below, sized to this box (Ivy) */
  .nav-back { top: 46px; width: 44px; height: 44px; justify-content: center; gap: 0; }   /* 12 + 68/2 */
  .nav-back span { display: none; }
  .nav-back .nav-back__arrow { width: 24px; height: 24px; }   /* 20 + 4 (Ivy) */
  .nav-back::after { inset: 0; }
  .nav-back-chip { box-sizing: border-box; }   /* outline inside the 44px circle */
}

@media print {
  @page { size: A4; margin: 0; }   /* margins come from html padding (set per scale by make-cv-pdf.mjs) so the paper colour fills the page */
  html, body { background: #FAF7F2 !important; }   /* PDF paper matches the web page (Ivy) */
  .site-nav, .nav-back, .skeleton-screen, .ending, .toast, .ripple-canvas,
  .lightbox, .about-cv { display: none !important; }
  a { text-decoration: none; }

  /* Everything keeps the desktop web styles and spacing — only the arrangement changes. */
  /* paper margins replace the page gutter + nav clearance; 5px of the header gap moves up here */
  .about-hero { padding-top: 13px; padding-left: 0; padding-right: 0; }   /* balances top/bottom paper margins */
  .about-role { display: none; }   /* role line dropped from the PDF; portfolio URL lives in the contact rows */
  .about-name { margin: 0 0 28px; }   /* name → bio = bio → contact (28px) */
  /* header → Experience 10px tighter than the web (−10 more); the 10px goes to the top (5) and bottom (5) margins */
  .resume { padding-top: calc(var(--gap-section) / 2 - 50px); padding-left: 0; padding-right: 0; padding-bottom: 0; }   /* PDF: header → Experience, −10 more (Ivy) */

  /* Contact row in the PDF: [mail] churingbb@gmail.com   [in] Chaoyu (Ivy) Chen (links to LinkedIn) — both are live links */
  .about-contact > .icon-btn[href^="mailto"] { order: 1; }
  .about-contact__email { order: 2; }
  .about-contact > .icon-btn[href*="linkedin"] { order: 3; margin-left: 24px; }
  .about-contact__linkedin { display: inline !important; order: 4; margin-left: 6px; color: inherit; }   /* [hidden] on the web, shown only in the PDF */
  /* second row: [yellow dot] ivychendesign.com — same size/type as the email row */
  .about-contact { flex-wrap: wrap; }
  .about-contact__break { display: block !important; order: 5; flex-basis: 100%; height: 0; }   /* + the 10px row gap on each side = 20px between rows */
  .about-contact > .about-contact__site-icon { display: inline-flex !important; order: 6; }
  .about-contact__site { display: inline !important; order: 7; margin-left: 6px; color: inherit; }

  /* Page 1 — all work experience, flowing in two columns (web column gap) */
  .resume__inner { display: block; }
  .resume__col--exp { column-count: 2; column-gap: 80px; }
  .resume__col--exp > .resume__col-label { column-span: all; }
  .resume__item { break-inside: avoid; }
  .resume__group-title,
  .resume__col-label,
  .resume__role { break-after: avoid; }

  /* Below experience, same two columns: Education + Language left, Skills right.
     64px above = the web rhythm between sections (group margin 40 + label offset 24). */
  .resume__col--right {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    column-gap: 80px;
    align-items: start;
    margin-top: 64px;
    break-inside: avoid;
  }
  .resume__col--right > :nth-child(1) { grid-column: 1; grid-row: 1; }               /* EDUCATION */
  .resume__col--right > :nth-child(2) { grid-column: 1; grid-row: 2; }
  .resume__col--right > :nth-child(3) { grid-column: 2; grid-row: 1; margin-top: 0; } /* SKILLS */
  .resume__col--right > :nth-child(4) { grid-column: 2; grid-row: 2 / span 3; }
  .resume__col--right > :nth-child(5) { grid-column: 1; grid-row: 3; }               /* LANGUAGE */
  .resume__col--right > :nth-child(6) { grid-column: 1; grid-row: 4; }
}
