/* ============================================================
   v2 DESIGN SYSTEM  ("Portfolio 02 202607" redesign)
   Figma: file 1qy7wBTv6PsRhMEquAUyLN,
          page "Design v2 — as built (15 Aug 2026)"

   Loaded by case-studies/*.html, about.html and services.html, on top
   of styles.css, and every rule is scoped to body[data-design="v2"].
   Only index.html still renders off styles.css alone — the Figma file
   has no homepage frame to redesign it against.

   All values measured from the 1440px Figma canvas and reproduced 1:1 at
   a 1440px viewport, scaling fluidly below that.
   ============================================================ */

body[data-design="v2"] {
  /* ---- Type ------------------------------------------------
     Neue Haas Grotesk Text 500  — headings, labels, stat headlines
     Albiona 300                 — body copy and the 50px description
     Both served by the existing Adobe kit oqs3zvb. */
  --v2-font-head: "neue-haas-grotesk-text", Helvetica, Arial, sans-serif;
  --v2-font-body: "albiona", Georgia, "Times New Roman", serif;

  /* vw factors are <figma px> / 1440 — each token hits its Figma value
     exactly at a 1440px viewport. */
  --v2-h1:  clamp(32px, 4.4444vw, 64px);   /* Header 1   64 / 73 */
  --v2-sub: clamp(17px, 1.8750vw, 27px);   /* Subheader  27 / 30 */
  --v2-h2:  clamp(26px, 3.4722vw, 50px);   /* Header 02  50 / 60 */
  --v2-b1:  clamp(18px, 1.9444vw, 28px);   /* Body 01    28 / 34 */

  /* One step down, for the nav and the supporting sections. Derived from the
     scale rather than invented: ×0.78 is the same step the system already uses
     from Header 1 (64) to Header 02 (50), and 21/22 are the --b1/--b2 values
     already in styles.css. Floors are set for phone readability — the ratio
     alone would give 13px and 14px. */
  --v2-sub-sm: clamp(16px, 1.4583vw, 21px);   /* Subheader 02  21 / 23 — nav, Next project name */
  --v2-label:  clamp(15px, 1.2500vw, 18px);   /* Section label 18 / 20 — row labels, stats, process */
  --v2-b2:     clamp(16px, 1.3194vw, 19px);   /* Body 02       19 / 23 — row text, Next summary */

  --v2-lh-h1:  1.1406;   /* 73/64 */
  --v2-lh-sub: 1.1111;   /* 30/27 */
  --v2-lh-h2:  1.2000;   /* 60/50 */
  --v2-lh-b1:  1.2143;   /* 34/28 */

  --v2-track: -0.03em;
  /* Body 02 only. The -3% the rest of the scale uses reads too tight at 19px
     in Albiona Light, so this step opens up to -2%. Applied to the row text
     and the Next-project summary — every other size keeps --v2-track. */
  --v2-track-b2: -0.02em;

  /* Stops a single word being left alone on a closing line. The browser
     re-flows the last few lines; older browsers just wrap as before. */
  text-wrap: pretty;

  /* ---- Layout ---------------------------------------------- */
  --v2-pad:       48px;   /* side padding on TEXT only — images are edge to edge */
  --v2-card-gap:  44px;   /* vertical gap between cards, measured from the frames */
  --v2-block-gap: 70px;   /* gap inside hero / overview / idea cards */
  --v2-label-w:   clamp(120px, 14.653vw, 211px);   /* 211px at 1440 */
  --v2-text-w:    664px;
  --v2-col-gap:   16px;
  --v2-media-ar:  1440 / 899;

  /* --bg/--fg, not the literals: the case studies resolve these to
     off-white on dark exactly as before, and About can carry the blue
     theme through without a second set of overrides. */
  background-color: var(--bg);
  color: var(--fg);
}


/* ============================================================
   NAV — takes the case study's accent colour while it sits over
   the hero block, then the page background once scrolled past it.
   `--accent` is set inline on <body> by build.py so the fixed nav,
   which lives outside .csv2-accent, can read the same value.
   Class toggled by js/nav.js.

   A solid background here is also what keeps the expanded mobile
   menu legible — its links are children of #site-nav, so with a
   transparent nav they sat directly on the case study text.
   ============================================================ */
body[data-design="v2"] #site-nav {
  background-color: var(--accent);
  padding-left: var(--v2-pad);
  padding-right: var(--v2-pad);
  transition: background-color 0.2s ease;
}

body[data-design="v2"] #site-nav.is-scrolled {
  background-color: var(--off-white);
}

/* 21px, matching the old site's nav exactly. Figma's navbar component draws
   27px in a 61px bar; matching the old nav is a deliberate departure. */
body[data-design="v2"] .nav-name,
body[data-design="v2"] .nav-links a {
  font-family: var(--v2-font-head);
  font-size: var(--v2-sub-sm);
  /* 1.25, not the Figma Subheader's 1.111 — this is what the old nav used, and
     it is what puts the bar back at exactly 74px. */
  line-height: 1.25;
  letter-spacing: var(--v2-track);
}

/* The hero supplies its own 164px top padding — no nav spacer needed. */
body[data-design="v2"] .page-top { padding-top: 0; }


/* ============================================================
   SHARED PIECES
   ============================================================ */

/* Text gutter. Images deliberately do NOT use this — in the Figma
   every image card is w-full at x=0, edge to edge. */
body[data-design="v2"] .csv2-pad {
  padding-left: var(--v2-pad);
  padding-right: var(--v2-pad);
}

/* Full-bleed media. Figma: aspect 1440/899, object-fit cover. */
body[data-design="v2"] .csv2-media {
  width: 100%;
  aspect-ratio: var(--v2-media-ar);
  overflow: hidden;
}

body[data-design="v2"] .csv2-media img,
body[data-design="v2"] .csv2-media video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* Opt-out: render at the image's own proportions instead of cropping.
   The <img> carries width/height attributes (emitted by build.py), so the
   browser derives the ratio and reserves the box before the file loads. */
body[data-design="v2"] .csv2-media--natural {
  aspect-ratio: auto;
  height: auto;
}
body[data-design="v2"] .csv2-media--natural img,
body[data-design="v2"] .csv2-media--natural video {
  height: auto;
  object-fit: unset;
}

/* Two-column label + text row — Overview, The idea, process elements.
   Figma: label 211px (max 270px), 16px gap, text 664px, 8px vertical
   padding on both columns. */
body[data-design="v2"] .csv2-row {
  display: flex;
  align-items: flex-start;
  gap: var(--v2-col-gap);
  padding-left: var(--v2-pad);
  padding-right: var(--v2-pad);
}

body[data-design="v2"] .csv2-row__label {
  flex: 0 0 auto;
  width: var(--v2-label-w);
  max-width: 270px;
  padding: 8px 0;
  font-family: var(--v2-font-head);
  font-weight: 500;
  font-size: var(--v2-label);
  line-height: var(--v2-lh-sub);
  letter-spacing: var(--v2-track);
}

body[data-design="v2"] .csv2-row__text {
  flex: 1 1 auto;
  max-width: var(--v2-text-w);
  padding: 8px 0;
  font-family: var(--v2-font-body);
  font-weight: 300;
  font-size: var(--v2-b2);
  line-height: var(--v2-lh-b1);
  letter-spacing: var(--v2-track-b2);
}

body[data-design="v2"] .csv2-row__text p + p { margin-top: calc(var(--v2-lh-b1) * 1em); }


/* ============================================================
   ACCENT BLOCK
   One unbroken colour field running from the very top of the page
   (behind the fixed nav) through the hero text, hero media and
   description, ending flush at the top edge of the overview image.
   --accent is set inline per case study by build.py.
   ============================================================ */
body[data-design="v2"] .csv2-accent {
  background-color: var(--accent);
  padding-top: 164px;          /* Figma pt-[164px] — also clears the nav */
  padding-bottom: var(--v2-block-gap);
  display: flex;
  flex-direction: column;
  gap: var(--v2-block-gap);
  /* No bottom margin: the overview image butts straight onto the colour. */
}

body[data-design="v2"] .csv2-hero {
  display: flex;
  flex-direction: column;
  gap: 24px;
  padding-left: var(--v2-pad);
  padding-right: var(--v2-pad);
}

body[data-design="v2"] .csv2-eyebrow {
  font-family: var(--v2-font-head);
  font-weight: 500;
  font-size: var(--v2-sub);
  line-height: var(--v2-lh-sub);
  letter-spacing: var(--v2-track);
}

body[data-design="v2"] .csv2-h1 {
  font-family: var(--v2-font-head);
  font-weight: 500;
  font-size: var(--v2-h1);
  line-height: var(--v2-lh-h1);
  letter-spacing: var(--v2-track);
  /* Even line lengths read better than `pretty` on a short display heading. */
  text-wrap: balance;
}

/* Hero media sits inside the accent block, so the colour meets it on
   both sides with no off-white showing. The accent also paints behind
   the video element, so an undecoded frame is never black. */
body[data-design="v2"] .csv2-accent .csv2-media {
  background-color: var(--accent);
}

body[data-design="v2"] .csv2-description,
body[data-design="v2"] .csv2-subhighlight {
  padding-left: var(--v2-pad);
  padding-right: var(--v2-pad);
  font-family: var(--v2-font-body);
  font-weight: 300;
  font-size: var(--v2-h2);
  line-height: var(--v2-lh-h2);
  letter-spacing: var(--v2-track);
  max-width: calc(1344px + (var(--v2-pad) * 2));
}

body[data-design="v2"] .csv2-description p + p,
body[data-design="v2"] .csv2-subhighlight p + p { margin-top: calc(var(--v2-lh-h2) * 1em); }


/* ============================================================
   OVERVIEW / IDEA / CREATIVE PROCESS
   ============================================================ */

/* Stat pairs: headline in Neue Haas Medium, description in Albiona,
   one blank line between pairs. */
body[data-design="v2"] .csv2-stat { margin-top: calc(var(--v2-lh-b1) * 1em); }

body[data-design="v2"] .csv2-stat__headline {
  font-family: var(--v2-font-head);
  font-weight: 500;
  font-size: var(--v2-label);
  line-height: var(--v2-lh-sub);
  letter-spacing: var(--v2-track);
}

/* "The creative process" label — full width, not a two-column row. */
body[data-design="v2"] .csv2-process-label {
  padding: 8px var(--v2-pad);
  font-family: var(--v2-font-head);
  font-weight: 500;
  font-size: var(--v2-label);
  line-height: var(--v2-lh-sub);
  letter-spacing: var(--v2-track);
}

/* Figma: 45px from the label to the first element, 44px card gap after. */
body[data-design="v2"] .csv2-process-label + .csv2-row { margin-top: 45px; }


/* ============================================================
   VERTICAL RHYTHM
   44px between every card, measured from the frames. The accent block
   is exempt — its bottom edge meets the overview image at zero.
   ============================================================ */
body[data-design="v2"] .csv2 > * + * { margin-top: var(--v2-card-gap); }
/* Only an IMAGE meets the accent block flush. LCF Shows has no overview image
   (hidden in Figma), so its Overview text row follows directly and must keep
   the normal gap rather than butting against the colour. */
body[data-design="v2"] .csv2 > .csv2-accent + .csv2-media { margin-top: 0; }


/* ============================================================
   GALLERIES — unchanged masonry from styles.css, widened to full bleed.
   Two CSS columns, natural image heights, nothing cropped.
   ============================================================ */
/* NB: no `margin` reset here. This rule sits after the vertical-rhythm rule
   at equal specificity, so setting `margin: 0` silently wiped the 44px gap
   above every gallery. `max-width: none` + `width: 100%` already makes the
   inherited `margin: 0 auto` a no-op. */
body[data-design="v2"] .gallery-wrap {
  max-width: none;
  width: 100%;
  padding-top: 0;
}

/* Two explicit columns instead of styles.css's `columns: 2`.
   CSS multi-column balances by HEIGHT, so which image landed in which column
   depended on the images themselves — that is why the order kept coming out
   wrong against the Figma 2x2. build.py now emits the left and right columns
   directly, so placement is exactly what the design says. */
body[data-design="v2"] .gallery-grid {
  columns: auto;
  column-count: initial;
  display: flex;
  align-items: flex-start;
  gap: var(--grid-gap);
}

body[data-design="v2"] .gallery-grid__col {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: var(--grid-gap);
}

/* The column supplies the spacing now. */
body[data-design="v2"] .gallery-grid__item { margin-bottom: 0; }

/* User-controlled video (Welcome's case study film) — full bleed, its own
   proportions, not autoplayed. */
body[data-design="v2"] .csv2-filmstrip video {
  width: 100%;
  height: auto;
  display: block;
  /* preload="none" means the file reports no intrinsic size until the viewer
     hits play, so the box needs an explicit ratio or it collapses. */
  aspect-ratio: 16 / 9;
  object-fit: cover;
  background-color: var(--dark);
}


/* ============================================================
   NEXT PROJECT CARD  (Figma 87:949)
   ============================================================ */
/* Next project is a .csv2-row — same label + text columns as Overview and
   The idea — so it reads as the last section of the system rather than a
   large standalone sign-off. */
body[data-design="v2"] .csv2-next { margin-top: 160px; }

body[data-design="v2"] .csv2-next__link {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  font-family: var(--v2-font-head);
  font-weight: 500;
  font-size: var(--v2-sub-sm);
  line-height: var(--v2-lh-sub);
  letter-spacing: var(--v2-track);
  transition: opacity 0.2s ease;
}

body[data-design="v2"] .csv2-next__link:hover { opacity: 0.5; }

body[data-design="v2"] .csv2-next__arrow {
  flex-shrink: 0;
  width: 1em;
  height: 1em;
}

body[data-design="v2"] .csv2-next__summary {
  margin-top: 8px;
  font-family: var(--v2-font-body);
  font-weight: 300;
  font-size: var(--v2-b2);
  line-height: var(--v2-lh-b1);
  letter-spacing: var(--v2-track-b2);
}


/* ============================================================
   LIGHTBOX ARROWS
   Same SVG as the Next project arrow (arrow_svg() in build.py), sized from
   the same Subheader 2 step, so both render at 21px with a 2px stroke.
   Position, colour and opacity still come from styles.css; the SVG strokes
   in currentColor. The mobile rule hiding these is unchanged.
   ============================================================ */
body[data-design="v2"] .lightbox-prev,
body[data-design="v2"] .lightbox-next { font-size: var(--v2-sub-sm); }

body[data-design="v2"] .lightbox-arrow {
  display: block;
  width: 1em;
  height: 1em;
}


/* ============================================================
   FOOTER CARD  (Figma 87:968)
   ============================================================ */
body[data-design="v2"] .csv2-footer {
  display: flex;
  flex-direction: column;
  /* 40px, not the Figma's 65px — that was drawn for 27px type and reads
     disproportionate at 18px. */
  gap: 40px;
  padding: var(--v2-pad);
  margin-top: 160px;
  font-family: var(--v2-font-head);
  font-weight: 500;
  font-size: var(--v2-label);
  line-height: var(--v2-lh-sub);
  letter-spacing: var(--v2-track);
}

body[data-design="v2"] .csv2-footer__links {
  display: flex;
  flex-direction: column;
}

body[data-design="v2"] .csv2-footer__links a { transition: opacity 0.2s ease; }
body[data-design="v2"] .csv2-footer__links a:hover { opacity: 0.5; }


/* ============================================================
   SIMPLE PAGES — About (Figma 129:1494), Services (135:461)
   Both are built from one component, "Text highlight" (132:447):
   a Header 1 statement, a 48px gap, then a Header 02 block. Neither
   has an accent block or a hero, so the page carries its own top
   offset for the fixed nav instead of inheriting the hero's 164px.
   ============================================================ */
body[data-design="v2"] .csv2-highlight {
  padding-left: var(--v2-pad);
  padding-right: var(--v2-pad);
  font-family: var(--v2-font-head);
  font-weight: 500;
  font-size: var(--v2-h1);
  line-height: var(--v2-lh-h1);
  letter-spacing: var(--v2-track);
}

/* Figma runs the two heading sentences as consecutive lines — the block
   measures 292px, exactly 4 x 73, so there is no gap between them. */
body[data-design="v2"] .csv2-highlight p + p { margin-top: 0; }

/* One value for Work, About and Services, so the first line of the hero lands
   at the same height on all three and moving between them doesn't jump. The
   Figma frames place their Text highlight at 205px (About) and 194px
   (Services); that 11px is placement drift on the canvas, not intent, so the
   pages share 205px. Deliberately NOT a per-page custom property — that is
   what let them drift apart. */
body[data-design="v2"] .csv2-page { padding-top: 205px; }

/* 116px, the gap both frames leave around the supporting sections
   (Services: 564 - 448 above the rows, 2439 - 2323 below them). */
body[data-design="v2"] .csv2-page > * + * { margin-top: 116px; }

/* 340 - 292 inside the component: the sub-highlight belongs to the
   heading, so it keeps its own 48px rather than taking the page rhythm.
   Deliberately one class more specific than the rule above, so it also
   survives the tighter gaps set in the media queries. */
body[data-design="v2"] .csv2-page > .csv2-subhighlight { margin-top: 48px; }

/* The collage keeps its own styles.css layout untouched, but its 160px bottom
   padding was drawn for the contact section that used to follow it. The page
   rhythm supplies the gap to the footer now, so the two would otherwise stack. */
body[data-design="v2"] .project-collage-wrap { padding-bottom: 0; }


/* On a page too short to fill the screen the footer drops to the bottom
   rather than sitting under the text — About's footer is at y=1779 in a
   1995px frame, and that gap is empty space, not a margin. */
body[data-design="v2"].csv2-simple {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}

body[data-design="v2"].csv2-simple > main { flex: 1 0 auto; }
body[data-design="v2"].csv2-simple .csv2-footer { margin-top: 116px; }


/* ============================================================
   RESPONSIVE
   ============================================================ */
@media (max-width: 900px) {
  body[data-design="v2"] {
    --v2-pad: 32px;
    --v2-card-gap: 32px;
    --v2-block-gap: 48px;
  }

  body[data-design="v2"] .csv2-accent { padding-top: 120px; }
  body[data-design="v2"] .csv2-page { padding-top: 140px; }
  body[data-design="v2"] .csv2-page > * + * { margin-top: 80px; }
  body[data-design="v2"] .csv2-next,
  body[data-design="v2"] .csv2-footer,
  body[data-design="v2"].csv2-simple .csv2-footer { margin-top: 100px; }
  body[data-design="v2"] .csv2-footer { gap: 40px; }
}

@media (max-width: 700px) {
  body[data-design="v2"] {
    --v2-pad: 24px;
    --v2-card-gap: 24px;
    --v2-block-gap: 32px;
  }

  /* Label stacks above its text. */
  body[data-design="v2"] .csv2-row {
    flex-direction: column;
    gap: 0;
  }
  body[data-design="v2"] .csv2-row__label {
    width: 100%;
    max-width: none;
    padding-bottom: 0;
  }
  body[data-design="v2"] .csv2-row__text { padding-top: 4px; }

  body[data-design="v2"] .csv2-accent { padding-top: 96px; }
  body[data-design="v2"] .csv2-page { padding-top: 108px; }
  body[data-design="v2"] .csv2-page > * + * { margin-top: 56px; }

  /* Portrait crop for the wide 1440/899 slots, so hero media doesn't
     become a thin strip on a phone. */
  body[data-design="v2"] .csv2-media { aspect-ratio: 4 / 3; }

  body[data-design="v2"] .csv2-process-label + .csv2-row { margin-top: 8px; }

  body[data-design="v2"] .csv2-next,
  body[data-design="v2"] .csv2-footer,
  body[data-design="v2"].csv2-simple .csv2-footer { margin-top: 72px; }
  body[data-design="v2"] .csv2-next__link { gap: 16px; }

  /* One column on a phone: stack the two gallery columns. */
  body[data-design="v2"] .gallery-grid { flex-direction: column; }
}
