/* ============================================================
   Vishal K. — Portfolio
   Hero section: 1:1 reconstruction of "Hero Section.svg"
   Design canvas: 1440 × 1216. Every value below is in design px
   and is scaled fluidly by --k. Do not "round" these numbers —
   they were derived from the exported vector outlines.
   ============================================================ */

/* ---------- Fonts ---------- */
/* Mobile-only copy: the phone frame rewords several blocks. Hidden here,
   revealed in mobile.css. */
.mob{display:none}
.s10__bio .para{display:inline}
.mob-br{display:none}
@font-face{font-family:'PP Neue Montreal';src:url('../assets/fonts/PPNeueMontreal-Book.otf') format('opentype');font-weight:350;font-style:normal;font-display:block}
@font-face{font-family:'PP Neue Montreal';src:url('../assets/fonts/PPNeueMontreal-Regular.otf') format('opentype');font-weight:400;font-style:normal;font-display:block}
@font-face{font-family:'PP Neue Montreal';src:url('../assets/fonts/PPNeueMontreal-Medium.otf') format('opentype');font-weight:500;font-style:normal;font-display:block}
@font-face{font-family:'PP Neue Montreal';src:url('../assets/fonts/PPNeueMontreal-Bold.otf') format('opentype');font-weight:700;font-style:normal;font-display:block}
@font-face{font-family:'PP Editorial New';src:url('../assets/fonts/PPEditorialNew-Regular.otf') format('opentype');font-weight:400;font-style:normal;font-display:block}
@font-face{font-family:'PP Editorial New';src:url('../assets/fonts/PPEditorialNew-Italic.otf') format('opentype');font-weight:400;font-style:italic;font-display:block}
@font-face{font-family:'PP Editorial New';src:url('../assets/fonts/PPEditorialNew-UltralightItalic.otf') format('opentype');font-weight:200;font-style:italic;font-display:block}

/* ---------- Reset ---------- */
*,*::before,*::after{box-sizing:border-box}
html,body{margin:0;padding:0}
html{background:var(--c-bg)}
body{
  /* black, not white — the hero paints its own white box, so anything that
     ever shows through elsewhere should match the dark sections */
  background:var(--c-bg);
  color:var(--c-ink);
  overflow-x:hidden;
  -webkit-font-smoothing:antialiased;
  -moz-osx-font-smoothing:grayscale;
  text-rendering:geometricPrecision;
}
ul{list-style:none;margin:0;padding:0}
a{color:inherit;text-decoration:none}
img{display:block;max-width:100%}
button{font:inherit;color:inherit;background:none;border:0;padding:0}

/* ---------- Fluid design-scale ----------
   --k is the artboard→viewport scale factor. It must be a UNITLESS number
   for transform:scale(), and CSS cannot divide two lengths, so it is set
   from documentElement.clientWidth by the inline script in <head> (which
   also excludes the scrollbar, unlike 100vw). 1 is the no-JS fallback. */
:root{
  --design-w: 1440;
  --k: 1;
  --stage-h: 1216;   /* updated by JS as sections are added */
}

.stage-wrap{
  position:relative;
  width:100%;
  overflow:hidden;
  height:calc(var(--stage-h) * 1px * var(--k));
}
.stage{
  position:absolute;
  top:0; left:0;
  width:1440px;
  height:calc(var(--stage-h) * 1px);
  transform-origin:0 0;
  transform:scale(var(--k));
}

/* ============================================================
   HERO  (0 → 1216)
   ============================================================ */
.hero{
  position:absolute;
  inset:0 0 auto 0;
  height:1216px;
  /* Literal, not a token: the hero is exempt from the theme switch. It
     keeps its white plane, its black plate and the type each carries in
     both modes. Everything from .s2 down follows --c-bg / --c-ink. */
  background:var(--c-ivory);
}

/* -- portrait: SVG rect x=290 y=0 w=1299 h=952 -- */
.hero__photo{
  position:absolute;
  left:290px; top:0;
  width:1299px; height:952px;
  overflow:hidden;
  will-change:transform;
}
.hero__photo img{
  width:1299px; height:952px;
  object-fit:fill;          /* source is 1712×1254 — identical aspect */
  will-change:transform;
}

/* -- tint band: SVG rect y=812 h=138 fill #272727 @ 10% -- */
.hero__band{
  position:absolute;
  left:0; top:812px;
  width:1440px; height:138px;
  background:rgba(39,39,39,.1);
  pointer-events:none;
}

/* -- black plate: SVG rect y=950, runs to the page bottom -- */
.hero__plate{
  position:absolute;
  left:0; top:950px;
  width:1440px; height:266px;
  background:var(--c-soft-black);
}

/* ============================================================
   THE SEAM — hero → Campaigns

   The one place on the page where soft black meets the light ground: the
   hero's plate runs to 1216 and Campaigns starts on --c-bg at 1217. In the
   dark theme both sides are the same colour and nothing happens; in the
   light theme that was a hard black-to-ivory line.

   The ramp lives ENTIRELY inside the hero and ends on --c-bg, in the 43.5px
   left under the body copy (which ends at 1172.5). That is the whole reason
   it does not straddle the boundary, which was the first attempt: .hero__veil
   washes the hero toward --c-bg as Campaigns rises, and it covers the hero
   half of a straddling ramp but not the Campaigns half — so the two ends drift
   apart on scroll and a dark band opens up just under the seam. Ending on
   --c-bg inside the hero makes the veil a no-op at the join (--c-bg painted
   over --c-bg is --c-bg), so the boundary is continuous at EVERY scroll
   position while the black above it dissolves.

   css/mobile.css re-solves the two numbers for the 390 frame, where body--b
   wraps to three lines ending at 909.56 and the plate ends at 938.

   Both artboards scale by --k / --km, so the ramp holds its proportion at
   every width.
   ============================================================ */
.hero__ramp{
  position:absolute;
  left:0; top:1172.5px;
  width:1440px; height:43.5px;
  background:linear-gradient(180deg, var(--c-soft-black) 0%, var(--c-bg) 100%);
  pointer-events:none;
}

/* -- veil: INERT. It used to dim the hero as Campaigns arrived, but fading
      soft black through a translucent ivory goes through grey, and mid-scroll
      the hero read as a grey slab with crisp type on it. js/main.js fades the
      TYPE over that range instead and leaves the plate its own colour. The
      element stays at opacity 0 because initNavContrast's correction term
      reads it — at 0 that term is a no-op. -- */
.hero__veil{
  position:absolute;
  inset:0;
  background:var(--c-bg);
  opacity:0;
  pointer-events:none;
  /* Above .hero__ramp on purpose. The veil washes the whole hero toward
     --c-bg, and the ramp's last stop is ALREADY --c-bg — so the veil cannot
     move the pixel the seam meets, and the boundary stays continuous while
     the black above it dissolves. Put the ramp on top instead and its own
     top edge becomes the hard line, against a plate the veil has lifted. */
  z-index:5;
  will-change:opacity;
}

/* -- headline: PP Editorial New Italic 35/45, #141313 -- */
.hero__headline{
  position:absolute;
  left:52px; top:311.5px;
  margin:0;
  font-family:'PP Editorial New',Georgia,serif;
  font-style:italic;
  font-weight:400;
  font-size:35px;
  line-height:45px;
  color:var(--c-charcoal);
  white-space:nowrap;
}
/* The mask must clip vertically (that is the reveal) but never horizontally —
   italic glyphs overhang the advance width at both ends (the final "T" of
   FROM FIRST THOUGHT sticks out 4.9px). Bleed sideways via padding, pulled
   back with a matching negative margin so the text origin is unmoved. */
.hero__headline .line{
  display:block;
  overflow:hidden;
  margin-left:-24px;
  padding-left:24px;
  padding-right:32px;
}
.hero__headline .line > span{display:block;will-change:transform}

/* -- meta, right side: PP Neue Montreal Regular 15/15, #000 -- */
.hero__meta{position:absolute;left:0;top:0;width:1440px;height:1px}
.hero__meta p{
  position:absolute;
  margin:0;
  font-family:'PP Neue Montreal',Helvetica,Arial,sans-serif;
  font-weight:400;
  font-size:15px;
  line-height:15px;
  /* literal, not a token: this pair sits on the portrait, which the
     inverted theme does not touch. */
  color:var(--c-charcoal);
  white-space:nowrap;
}
/* One line now — the role line above it repeated the wordmark's own tagline.
   These are the coordinates it already had (the pair was optically
   right-aligned, ending at 1161.35 + 209.62 = 1370.97); it keeps them rather
   than moving up into the vacated slot, so nothing else in the hero shifts. */
.hero__meta p{left:1161.35px;top:490px}

/* -- CTA: SVG rect x=52 y=567 w=187 h=54 r=11 -- */
.hero__cta{
  position:absolute;
  left:52px; top:567px;
  width:187px; height:54px;
  border-radius:11px;
  background:var(--c-accent);
  display:block;
  overflow:hidden;
  will-change:transform;
}
.hero__cta .label{
  position:absolute;
  left:18px;              /* 70 − 52 */
  top:19px;               /* 586 − 567 */
  font-family:'PP Neue Montreal',Helvetica,Arial,sans-serif;
  font-weight:400;
  font-size:15px;
  line-height:15px;
  color:var(--c-ivory);
  white-space:nowrap;
}
.hero__cta .arrow{
  position:absolute;
  left:153px;             /* 205 − 52 */
  top:22px;               /* 589 − 567 */
  width:10px; height:10px;
  overflow:visible;
}

/* -- categories: PP Editorial New Italic 15/15 -- */
.hero__cats{position:absolute;left:0;top:0;width:1440px;height:1px}
.hero__cats li{
  position:absolute;
  top:873.5px;
  font-family:'PP Editorial New',Georgia,serif;
  font-style:italic;
  font-weight:400;
  font-size:15px;
  line-height:15px;
  white-space:nowrap;
}
.hero__cats li:nth-child(1){left:130.02px;color:var(--c-charcoal)}
/* 2-4 are literal white: they sit on the portrait's dark half and the
   portrait is not inverted. Only the first, out on the ground at x=130,
   follows the theme. */
.hero__cats li:nth-child(2){left:503.51px;color:var(--c-ivory)}
.hero__cats li:nth-child(3){left:850.04px;color:var(--c-ivory)}
.hero__cats li:nth-child(4){left:1196.30px;color:var(--c-ivory)}

/* -- wordmark: PP Editorial New Italic 62/62, #fff -- */
.hero__wordmark{
  position:absolute;
  left:45px; top:1047px;
  margin:0;
  font-family:'PP Editorial New',Georgia,serif;
  font-style:italic;
  font-weight:400;
  font-size:62px;
  line-height:62px;
  color:var(--c-ivory);
  white-space:nowrap;
  overflow:hidden;
  margin-left:-24px;
  padding-left:24px;
  padding-right:32px;
  margin-top:-14px;       /* italic caps rise above the 62px line box */
  padding-top:14px;
}
.hero__wordmark > span{display:block;will-change:transform}

/* -- wordmark tagline: PP Neue Montreal Regular 11/11, ls 1.7244 -- */
.hero__tagline{
  position:absolute;
  left:53px; top:1113.5px;
  margin:0;
  font-family:'PP Neue Montreal',Helvetica,Arial,sans-serif;
  font-weight:400;
  font-size:11px;
  line-height:11px;
  letter-spacing:1.7244px;
  color:var(--c-ivory);
  white-space:nowrap;
}

/* -- body copy: PP Neue Montreal Regular 18/29, #fff -- */
.hero__body{
  position:absolute;
  top:1056.5px;
  margin:0;
  font-family:'PP Neue Montreal',Helvetica,Arial,sans-serif;
  font-weight:400;
  font-size:18px;
  line-height:29px;
  color:var(--c-ivory);
  white-space:nowrap;
}
.hero__body--a{left:448px}
.hero__body--b{left:960px}
.hero__body .line{
  display:block;
  overflow:hidden;
  margin-left:-16px;
  padding-left:16px;
  padding-right:24px;
}
.hero__body .line > span{display:block;will-change:transform}

/* ============================================================
   NAV — moved to css/nav.css, which every page loads. Keeping it
   here would have meant a second copy on the four pages that do not
   load this stylesheet, and two copies is how headers drift apart.
   ============================================================ */

/* ============================================================
   Load curtain
   ============================================================ */
.curtain{
  position:fixed; inset:0;
  background:var(--c-soft-black);
  z-index:150;
  pointer-events:none;
  transform-origin:50% 100%;
}
.js-ready .curtain{pointer-events:none}

/* ============================================================
   Motion opt-outs
   ============================================================ */
html[data-static="1"] .curtain{display:none}

@media (prefers-reduced-motion:reduce){
  .curtain{display:none}
  *{animation-duration:.001ms !important;transition-duration:.001ms !important}
}

/* ============================================================
   SECTION 2 — "Brand Identities"  (artboard 1440 × 993)

   This one is PINNED, so it is scaled to *fit* the viewport
   (contain) rather than to width like the hero. A 1440×993
   artboard is proportionally taller than any 16:9 screen, so
   width-scaling would crop the card on a 1080p display. The
   section and the page are both pure black, so the letterbox
   is invisible. --k2 is set by JS alongside --k.
   ============================================================ */
:root{ --k2: 1; }

.s2{
  position:relative;
  height:360vh;              /* 100vh visible + 260vh of pinned scroll */
  background:var(--c-bg);
}
.s2__sticky{
  position:sticky;
  top:0;
  height:100vh;
  overflow:hidden;
  background:var(--c-bg);
}
.s2__art{
  position:absolute;
  left:50%; top:50%;
  width:1440px; height:900px;
  margin:-450px 0 0 -720px;     /* centre the artboard */
  transform:scale(var(--k2));
  transform-origin:50% 50%;
  /* The artboard is the frame: slides travel beyond its edges during a
     transition and must vanish there. Without this they would keep drifting
     across the black letterbox margin on screens where --k2 is height-bound. */
  overflow:hidden;
}
/* separate layer so GSAP can animate the entry without clobbering --k2 */
.s2__inner{
  position:absolute;
  inset:0;
  will-change:transform;
}

/* -- shared heading -- */
.s2__heading{
  position:absolute;
  left:468px; top:76.5px;
  margin:0;
  font-family:'PP Editorial New',Georgia,serif;
  font-style:italic;
  font-weight:200;              /* Ultralight Italic */
  font-size:82px;
  line-height:82px;
  color:var(--c-ink);
  white-space:nowrap;
}

/* -- slides stack -- */
.s2__slide{
  position:absolute;
  inset:0;
  will-change:opacity;      /* own compositor layer → opacity costs nothing */
}
/* no-JS / pre-JS fallback — GSAP overrides the container with inline
   opacity:1 and animates the two column groups instead */
.s2__slide[aria-hidden="true"]{opacity:0;pointer-events:none}

/* the two transform groups: text enters from the left, card from the right.
   One transform per column rather than one per element.
   Sized to the column's actual content, NOT inset:0 — origin stays at 0,0 so
   the children's coordinates are untouched, but the area the compositor has
   to blur and composite drops from 1440×993 to 560×820. */
.s2__text{
  position:absolute;
  left:0; top:0;
  width:560px; height:820px;
  will-change:transform,opacity,filter;
}

/* -- card -- */
/* The card group now spans the whole artboard: each image carries its own
   .s2__frame with artboard coordinates and its own clipping, so this element
   is only the transform/opacity group the transition animates. Giving it the
   old 557,279 803x586 box offset every frame inside it by that amount and
   clipped the overflow. */
.s2__card{
  position:absolute;
  left:0; top:0;
  width:1440px; height:900px;
  will-change:transform,opacity,filter;
}
.s2__card img{
  position:absolute;
  max-width:none;
  will-change:transform;
}

/* -- left column -- */
.s2__title{
  position:absolute;               /* left/top inline per slide */
  margin:0;
  font-family:'PP Neue Montreal',Helvetica,Arial,sans-serif;
  font-weight:500;              /* Medium */
  font-size:50px;
  line-height:50px;
  color:var(--c-ink);
  white-space:nowrap;
}
.s2__tagline{
  position:absolute;               /* left/top inline per slide */
  margin:0;
  font-family:'PP Editorial New',Georgia,serif;
  font-style:italic;
  font-weight:200;
  font-size:18px;
  line-height:18px;
  color:var(--c-ink);
  white-space:nowrap;
}
.s2__body{
  position:absolute;               /* left/top inline per slide */
  margin:0;
  font-family:'PP Neue Montreal',Helvetica,Arial,sans-serif;
  font-weight:350;              /* Book */
  font-size:18px;
  line-height:29px;
  color:var(--c-ink);
  white-space:nowrap;
}
.s2__credits{
  position:absolute;               /* left/top inline per slide */
  margin:0;
  font-family:'PP Neue Montreal',Helvetica,Arial,sans-serif;
  font-weight:350;
  font-size:12px;
  line-height:18px;
  color:var(--c-ink);
  opacity:.5;                      /* slides 2-3: white @ 50% */
  white-space:nowrap;
}
/* slide 1 uses a flat grey fill instead — #727272 at full opacity */
.s2__slide--1 .s2__credits{ color:var(--c-secondary); opacity:1 }

/* per-slide overrides taken from the artboards */
.s2__slide--1 .s2__body{top:456.5px}          /* 4 lines, not 5 */
.s2__slide--3 .s2__tagline{left:423px;top:346px}
.s2__slide--3 .s2__cta{top:653px}

/* -- CTA -- */
/* Shared by sections 2 and 3 — the artboards use an identical pill, only the
   position differs. The SVG rect is 164.5×50.5 with a 0.5 stroke, and SVG
   strokes straddle the path, so the outer box is 165×51 and the radius grows
   by half the stroke (4.75 → 5). CSS borders sit inside the box. */
.cta{
  position:absolute;
  width:165px; height:51px;
  border:.5px solid var(--c-accent);
  /* the sweep's ring and cover are drawn from this, so a button that
     overrides its radius must set --cta-r, not border-radius */
  --cta-r:5px;
  border-radius:var(--cta-r);
  background:var(--c-accent);
  display:block;
  overflow:hidden;
  isolation:isolate;               /* keep the sweep from painting over neighbours */
}
/* Light travelling around the button border.
   A conic gradient sized to the BUTTON sweeps unevenly — it races through the
   short ends and crawls along the long sides. So the gradient is a large
   SQUARE centred on the button and clipped by the button's overflow:hidden;
   rotation is then angularly uniform and the light travels at a steady pace.
   ::after covers all of it but a 1.5px band just inside the button's edge —
   an ivory highlight travelling the rim of the solid orange fill. */
.cta::before{
  content:'';
  position:absolute;
  width:600px; height:600px;
  left:50%; top:50%;
  margin-left:-300px; margin-top:-300px;
  background:conic-gradient(
      transparent 0%,
      transparent 70%,
      var(--c-ivory-0) 75%,
      var(--c-ivory) 90%,
      var(--c-ivory-0) 100%);
  animation:cta-sweep 3.5s linear infinite;
  z-index:0;
  pointer-events:none;
}
/* covers the middle, leaving only the border band visible */
.cta::after{
  content:'';
  position:absolute;
  inset:1.5px;
  border-radius:calc(var(--cta-r) - 1px);
  background:var(--c-accent);
  z-index:1;
  pointer-events:none;
}
.cta .label,.cta .arrow{ z-index:2 }

@keyframes cta-sweep{ from{ transform:rotate(0deg) } to{ transform:rotate(360deg) } }

/* only run while the button is on screen — initCtaGlow adds the class */
.cta:not(.is-live)::before{ animation-play-state:paused }

@media (prefers-reduced-motion:reduce){
  .cta::before{ animation:none; opacity:0 }
}

/* Page transition fade-in */
body { opacity: 0; animation: page-fade-in 0.8s ease-out 0.2s forwards; }
@keyframes page-fade-in {
  from { opacity: 0; }
  to { opacity: 1; }
}
.cta .label{
  position:absolute;
  left:20.5px; top:18px;        /* label ink sits +21.12,+20.275 from the CTA
                                   border-box in every artboard; 17.5 put it
                                   half a pixel high in all nine buttons */
  font-family:'PP Neue Montreal',Helvetica,Arial,sans-serif;
  font-weight:400;
  font-size:15px;
  line-height:15px;
  color:var(--c-ivory);
  white-space:nowrap;
}
.cta .arrow{
  position:absolute;
  left:133.5px; top:21.5px;
  width:10px; height:10px;
  overflow:visible;
}
/* .s2__cta position is inline per slide */

/* -- reveal masks (vertical clip only, per the hero's rules) -- */
.s2 .line{display:block;overflow:hidden;margin-left:-16px;padding-left:16px;padding-right:26px}
.s2 .line > span{display:block;will-change:transform}

/* ============================================================
   SECTION 3 — "Campaigns"  (artboard 1440 x 834, REDESIGN)

   Rebuilt from ~/Downloads/campaign.svg. Not pinned: at 834 tall
   the artboard is proportionally shorter than section 2's, so it
   scales to WIDTH like the hero and simply scrolls past.

   The old layout (257x457 reel, column 2 = column 1 + 681) is GONE.
   The two columns are no longer a uniform offset of each other:
   the TEXT columns sit on a 738px pitch (426 -> 1164) but the media
   frames do NOT (46 -> 777, a 731 pitch), so every child carries an
   absolute artboard coordinate.

   Pendulo (LEFT, --1) runs 4-line body blocks and its title and
   subtitle sit exactly 21px HIGHER than Raffeta's (RIGHT, --2),
   which runs 3 lines. The columns were swapped at the user's
   request, so each project kept its own vertical rhythm and only
   the x coordinates changed. Both CTAs share y=664.

   No credits line in this export - the 13 paths are heading + 6 per
   column (title, subtitle, 2 body blocks, CTA label, arrow).
   ============================================================ */
.s3{
  position:relative;
  width:100%;
  height:calc(834px * var(--k));
  overflow:hidden;
  background:var(--c-bg);
}
.s3__art{
  position:absolute;
  left:0; top:0;
  width:1440px; height:834px;
  transform-origin:0 0;
  transform:scale(var(--k));
}

/* -- heading: PP Editorial New Ultralight Italic 82/82.
      Same left as the previous export; the redesign lifts it 46px. -- */
.s3__heading{
  position:absolute;
  left:542px; top:50.5px;
  margin:0;
  font-family:'PP Editorial New',Georgia,serif;
  font-style:italic;
  font-weight:200;
  font-size:82px;
  line-height:82px;
  color:var(--c-ink);
  white-space:nowrap;
}

/* -- columns are full-artboard groups; children are absolute.

      Both columns are inset:0 and FULLY OVERLAP (each computes to
      0,0,1440,834), so column 2 sits on top of column 1 and swallows
      every click aimed at column 1's "View case study" button — the
      same trap section 6's two blocks have. Neither column needs to be
      hit-testable itself; only its button does. -- */
.s3__col{position:absolute;inset:0;pointer-events:none}
.s3__col .cta{pointer-events:auto}

/* -- media: 352x627 r10. The pattern matrix puts the image at
      -0.34375,0 at 352.686x627 inside a 352x627 frame, which is
      exactly what object-fit:cover computes for a 0.5625 source
      in a 0.5614 box. Both videos are 9:16, so nothing is cropped
      horizontally beyond that 0.34px. -- */
.s3__media{
  position:absolute;
  top:170px;
  width:352px; height:627px;
  border-radius:10px;
  overflow:hidden;
  background:var(--c-swatch);
}
.s3__col--1 .s3__media{left:46px}
.s3__col--2 .s3__media{left:777px}
.s3__media video{
  width:100%; height:100%;
  object-fit:cover;
  object-position:center;
  display:block;
}

/* -- title: PP Neue Montreal Medium 40/40 -- */
.s3__title{
  position:absolute;
  margin:0;
  font-family:'PP Neue Montreal',Helvetica,Arial,sans-serif;
  font-weight:500;
  font-size:40px;
  line-height:40px;
  color:var(--c-ink);
  white-space:nowrap;
}
.s3__col--1 .s3__title{left:426px;  top:256px}
.s3__col--2 .s3__title{left:1164px; top:277px}

/* -- subtitle: PP Editorial New Ultralight Italic 18/18 -- */
.s3__subtitle{
  position:absolute;
  margin:0;
  font-family:'PP Editorial New',Georgia,serif;
  font-style:italic;
  font-weight:200;
  font-size:18px;
  line-height:18px;
  color:var(--c-ink);
  white-space:nowrap;
}
.s3__col--1 .s3__subtitle{left:426px;  top:311.5px}
.s3__col--2 .s3__subtitle{left:1164px; top:332.5px}

/* -- body: PP Neue Montreal Book 15/21.
      15px is confirmed by the ink HEIGHT landing on 0.000 for all
      four blocks; weight 400 runs 2-4px wide, so this is Book. -- */
.s3__body{
  position:absolute;
  margin:0;
  font-family:'PP Neue Montreal',Helvetica,Arial,sans-serif;
  font-weight:350;
  font-size:15px;
  line-height:21px;
  color:var(--c-ink);
  white-space:nowrap;
}
.s3__col--1 .s3__body--a{left:426px;  top:417.5px}
.s3__col--1 .s3__body--b{left:426px;  top:518.5px}
.s3__col--2 .s3__body--a{left:1164px; top:433.5px}
.s3__col--2 .s3__body--b{left:1164px; top:511.5px}

/* -- CTA: the shared .cta at its full 165x51 box.
      SVG rect 426.25,664.25 164.5x50.5 + 0.5 stroke from the
      centre = a 165x51 border-box at 426,664. -- */
.s3__col--1 .s3__cta{left:426px;  top:664px}
.s3__col--2 .s3__cta{left:1164px; top:664px}

/* -- reveal masks (vertical clip only) -- */
.s3 .line{display:block;overflow:hidden;margin-left:-14px;padding-left:14px;padding-right:24px;
  /* Vertical bleed. The 82/82 line box is 14.76px shorter than the font's
     own content area, so the mask crops the italic ink at BOTH ends —
     ~3px off the ascenders of C/V/T/l and ~2px off the p/g descenders.
     Open it 26 up and 12 down, pulled back by equal negative margins so
     the heading's artboard origin is unmoved. The reveal starts at
     yPercent 125 (main.js) so the 12px below the box cannot leak. */
  margin-top:-26px;padding-top:26px;margin-bottom:-12px;padding-bottom:12px}
.s3 .line > span{display:block;will-change:transform}


/* ============================================================
   SECTION 4 — "Social Media"  (artboard 1440 × 900, 4 slides)

   Pinned scroll-driven carousel, same converge/expand blurred
   cross-fade as section 2.

   The frosted panels are baked Figma exports, not backdrop-filter
   (see .s4__panel below). The panel is still a SIBLING of
   .s4__copy rather than a child — the cross-fade animates the
   copy's opacity and the panel is tweened alongside it.
   ============================================================ */
.s4{
  position:relative;
  height:400vh;              /* 100vh visible + 300vh of pinned scroll */
  background:var(--c-bg);
}
.s4__sticky{
  position:sticky;
  top:0;
  height:100vh;
  overflow:hidden;
  background:var(--c-bg);
}
.s4__art{
  position:absolute;
  left:50%; top:50%;
  width:1440px; height:900px;
  margin:-450px 0 0 -720px;
  transform:scale(var(--k4));
  transform-origin:50% 50%;
  overflow:hidden;
}
.s4__inner{position:absolute;inset:0}

/* -- shared heading + intro -- */
.s4__heading{
  position:absolute;
  /* "Social Media & Digital" measures 727.75 at 82px, still hung on the
     optical centre the original "Social Media" sat on (712.49), not the
     artboard's 720 — so 712.49 - 727.75/2. Every heading this section has
     carried has been centred on that same 712.49. */
  left:348.62px; top:53.5px;
  margin:0;
  font-family:'PP Editorial New',Georgia,serif;
  font-style:italic;
  font-weight:200;
  font-size:82px;
  line-height:82px;
  color:var(--c-ink);
  white-space:nowrap;
}
/* centred on the artboard centre (720.04 measured) */
/* 28 below the heading's 135.5 line box (it sat 5.5 under it), matching
   the subtitle gap on Campaigns and Brand Identities (Vishal, 2026-09-26).
   Still 50.5 clear of the frames at 239. */
.s4__intro{
  position:absolute;
  left:0; top:163.5px;
  width:1440px;
  margin:0;
  font-family:'PP Neue Montreal',Helvetica,Arial,sans-serif;
  font-weight:350;
  font-size:18px;
  line-height:25px;
  color:var(--c-ink);
  text-align:center;
  white-space:nowrap;
}

/* -- slides -- */
/* plain wrappers — the cube is gone; no perspective or preserve-3d here,
   both of which would force the blurred/faded groups onto a 3D compositing
   path they do not need. */
.s4__deck,.s4__stage{position:absolute;inset:0}
.s4__slide{
  position:absolute;
  inset:0;
  will-change:transform;     /* NOT opacity — see the note above */
}
/* no-JS / pre-JS fallback — GSAP overrides with inline opacity */
.s4__slide[aria-hidden="true"]{opacity:0;pointer-events:none}
.s4__media,.s4__copy{position:absolute;inset:0}

/* every image sits in a clipping frame, positioned in artboard px */
.s4__frame{position:absolute;overflow:hidden}
/* artboard paints the brand logo AFTER the panel — keep it on top */
.s4__logo{z-index:2}
.s4__frame img{position:absolute;max-width:none;display:block}

/* will-change only while something is actually moving — see initSlides4.
   (No backdrop-filter left to drop: the panels are baked images now.) */
.s4.is-moving .s4__media,
.s4.is-moving .s4__copy,
.s4.is-moving .s4__panel{ will-change:transform,opacity,filter; }
.s4:not(.is-moving) .s4__slide,
.s4:not(.is-moving) .s4__media,
.s4:not(.is-moving) .s4__copy{ will-change:auto; }
/* Frosted panels — the real Figma render, exported per slide.

   Figma's Glass material cannot be expressed in CSS, and backdrop-filter was
   only ever an approximation of it. Each panel sits over a FIXED backdrop
   (its own slide), so a baked export is not a compromise — it is exact, and it
   deletes the single most expensive thing on this section: a live
   backdrop-filter re-sampling a large region every scrubbed frame.

   It also retires the backdrop-root fragility entirely — no ancestor's opacity,
   filter or will-change can silently switch this off any more.

   The exports carry blur + tint only: scanning inward from every edge the
   luminance is flat, so there is no specular rim, and the rounded corners are
   baked into the alpha. The approximated 1px rim and inset "light from −45°"
   shadows that used to live here have been removed to match. */
.s4__panel{
  position:absolute;
  pointer-events:none;
  border-radius:inherit;
  backdrop-filter:blur(26px);
  -webkit-backdrop-filter:blur(26px);
}

/* -- body copy: PP Neue Montreal Regular 18/27 -- */
.s4__body{
  position:absolute;
  margin:0;
  font-family:'PP Neue Montreal',Helvetica,Arial,sans-serif;
  font-weight:350;
  font-size:15px;
  line-height:23px;
  /* Literal. This copy sits on the frosted panel, over the slide's own
     imagery — neither of which the theme touches — so it stays white in
     both modes, like the hero. */
  color:var(--c-ivory);
  white-space:nowrap;
}

/* -- slide 4's wordmark: PP Editorial New Italic 20 -- */
.s4__mark{
  position:absolute;
  margin:0;                        /* left/top set inline per slide */
  font-family:'PP Editorial New',Georgia,serif;
  font-style:normal;               /* upright on the new artboard, not italic */
  font-weight:400;
  font-size:15px;
  line-height:15px;
  color:var(--c-ivory);            /* on the panel — see .s4__body */
  white-space:nowrap;
}

/* ============================================================
   SECTION 5 — "Visual Exploration"  (artboard 1440 × 900)

   Not pinned. Like section 3 it scales to WIDTH and simply
   scrolls past, so there is no letterbox.
   Solved against the outlines, all on round values:
     heading  Editorial New Ultralight Italic 82/82  @ 422, 76.5
     intro    Neue Montreal Book 16/22              @ 259.31, 747.5
     button   the shared .cta, 134 wide instead of 165
   ============================================================ */
.s5{
  position:relative;
  width:100%;
  height:calc(900px * var(--k));
  overflow:hidden;
  background:var(--c-bg);
}
.s5__art{
  position:absolute;
  left:0; top:0;
  width:1440px; height:900px;
  transform-origin:0 0;
  transform:scale(var(--k));
}
.s5__heading{
  position:absolute;
  left:422px; top:76.5px;
  margin:0;
  font-family:'PP Editorial New',Georgia,serif;
  font-style:italic;
  font-weight:200;
  font-size:82px;
  line-height:82px;
  color:var(--c-ink);
  white-space:nowrap;
}
/* the artboard's translucent slab behind the three posters */
.s5__card{
  position:absolute;
  left:164px; top:189px;
  width:1112px; height:637px;
  border-radius:10px;
  background:var(--c-hair);   /* #494141 @ 20% */
}
.s5__frame{position:absolute;overflow:hidden}
.s5__frame img{position:absolute;max-width:none;display:block}
.s5__intro{
  position:absolute;
  left:259.31px; top:745.3px;
  margin:0;
  font-family:'PP Neue Montreal',Helvetica,Arial,sans-serif;
  font-weight:350;
  font-size:18px;
  line-height:25px;
  color:var(--c-ink);
  white-space:nowrap;
}
/* same component as sections 2/3, narrower box */
.s5__cta{ left:1062px; top:745px; width:134px }
.s5__cta .label{ left:19.5px; top:17.99px }
.s5__cta .arrow{ left:102.5px; top:21.5px }

/* reveal mask — vertical clip only. "Exploration" is italic and its final
   'n' overhangs the advance width, so bleed right or it gets sliced. */
.s5 .line{display:block;overflow:hidden;margin-left:-14px;padding-left:14px;padding-right:22px;
  /* same vertical bleed as .s3 .line — see the note there */
  margin-top:-26px;padding-top:26px;margin-bottom:-12px;padding-bottom:12px}
.s5 .line > span{display:block;will-change:transform}

/* ============================================================
   SECTION 6 — "Visual Exploration", continued  (artboard 1440 × 1800)

   Two stacked blocks on one artboard: a fanned deck of five 16:9
   frames, then four 9:16 reel thumbnails. Each block has its own
   copy line and "View more" button.

   Not pinned — scales to WIDTH like sections 3 and 5 and scrolls.
   Artboard shortened 1700 -> 1553: the export left ~287px of dead black
   between the deck copy (ends 595) and the reels strip (was 882). The whole
   reels block — strip, copy and button — moved up 147 so the break between
   the two blocks reads as 140, close to the 142 that closes the section.
   Type is identical to section 5 (same chapter), and both buttons
   are the shared .cta at 134 wide.
   ============================================================ */
.s6{
  position:relative;
  width:100%;
  height:calc(1465px * var(--k));
  overflow:hidden;
  background:var(--c-bg);
}
.s6__art{
  position:absolute;
  left:0; top:0;
  width:1440px; height:1465px;
  transform-origin:0 0;
  transform:scale(var(--k));
}
.s6__block{position:absolute;inset:0}
/* The section-6 heading was removed — it repeated section 5's "Visual
   Exploration" verbatim. Rule kept, unused, in case it comes back. */
.s6__heading{
  position:absolute;
  left:422px; top:76.5px;
  margin:0;
  font-family:'PP Editorial New',Georgia,serif;
  font-style:italic;
  font-weight:200;
  font-size:82px;
  line-height:82px;
  color:var(--c-ink);
  white-space:nowrap;
}
/* the drop shadows are inline per frame — Figma's feGaussianBlur
   stdDeviation is HALF the CSS blur radius, hence 4.4 → 8.8px.
   They sit on pure black, so they read as almost nothing; kept for
   fidelity rather than effect. */
.s6__frame{position:absolute;overflow:hidden}
.s6__frame img{position:absolute;max-width:none;display:block}
/* ---- reels row: right-to-left auto marquee ------------------------
   Replaces the fanned four-thumbnail arrangement with one continuous
   strip of uniform 9:16 tiles, per the user's new design.

   Seamlessness is geometry, not trickery: the track holds THREE copies
   of the 15-tile set and travels exactly ONE set width (15 x 319 =
   4785), so the frame at the end of the loop is identical to the frame
   at the start. Same recipe as the logofolio rows.

   The wrapper clips to the artboard so the strip bleeds off both edges
   instead of widening the page. */
.s6__reels{
  position:absolute;
  left:0;
  width:1440px; height:514px;
  overflow:hidden;
}
.s6__track{
  position:absolute;
  left:0; top:0;
  height:514px;
  display:flex;
  will-change:transform;
}
.s6__set{ display:flex; flex:0 0 auto }
.s6__set > .s6__tile{ margin-right:30px }      /* GAP - part of the 319 pitch */
.s6__tile{
  flex:0 0 auto;
  width:289px; height:514px;
  border-radius:10px;
  overflow:hidden;
  background:var(--c-panel-2);
}
.s6__tile img{
  width:100%; height:100%;
  object-fit:cover;
  display:block;
}

.s6__intro{
  position:absolute;
  left:251.31px;
  margin:0;
  font-family:'PP Neue Montreal',Helvetica,Arial,sans-serif;
  font-weight:350;
  font-size:18px;
  line-height:25px;
  color:var(--c-ink);
  white-space:nowrap;
}
/* Deck copy, redrawn: 20/28 centred on the artboard, no button beside it.
   Latest.svg ink box (422.79,545.70)-(1015.80,591.66); at 20px the Book cut
   measures 593.66 against the artboard's 593.01, and the ink centre lands on
   719.3 — i.e. the paragraph is simply centred on 720. Cap top 545.70 minus
   the 6.7 half-leading+ascent offset (same arithmetic as .s9__intro) = 539. */
.s6__intro--a{
  top:541.2px;
  left:0; width:1440px;
  text-align:center;
}
/* Centred on the artboard now that the reels block has no button beside it
   either. Horizontal only — left:0/width:1440 with text-align:center leaves
   top and the 16/22 line box exactly where they were. */
.s6__reels--1{ top:735px }
.s6__intro--b{
  top:1305px;
  left:0; width:1440px;
  text-align:center;
}
.s6__cta{ left:1054px; width:134px }
/* Both are unused: neither block carries a "View more" button any more.
   Kept, like .s6__heading, in case one comes back. */
.s6__cta--a{ top:604px }
.s6__cta--b{ top:1352px }
.s6__cta .label{ left:19.5px; top:17.99px }
.s6__cta .arrow{ left:102.5px; top:21.5px }

/* vertical clip only — the heading is italic and overhangs its advance */
.s6 .line{display:block;overflow:hidden;margin-left:-14px;padding-left:14px;padding-right:22px}
.s6 .line > span{display:block;will-change:transform}

/* The two section-6 blocks are both inset:0 and FULLY OVERLAP, so the reels
   block sits on top of the deck and swallows pointer events aimed at it —
   including clicks on the deck's own "View more" button. Neither block needs to
   be hit-testable itself; only its button does. */
.s6__block{position:absolute;inset:0;pointer-events:none}
.s6__cta{pointer-events:auto}

/* The artboard's own drop shadows, moved out of inline style earlier.
   Keyed to the SLOT, not the card: a promoted card takes over the slot's
   shadow rather than carrying its own across. data-slot is authored in the
   markup, so the resting state is correct with no JS.
   Only three of the seven slots carry one; Figma's stdDeviation is HALF the
   CSS blur radius, and its feMorphology dilate radius is the CSS spread. */
.s6__frame[data-slot="r3"]  { box-shadow:-11px 0 6.2px      rgba(0,0,0,.48) }
.s6__frame[data-slot="l1"]  { box-shadow:  8px 0 8.8px 5px  rgba(0,0,0,.25) }
.s6__frame[data-slot="hero"]{ box-shadow:  0 10px  10px 11px rgba(0,0,0,.53) }

/* On black these read as almost nothing, which is why they were kept. On the
   light ground they are a grey slab under the deck — a shadow the artboard
   never intended anyone to see. So the light theme drops them, and the dark
   theme keeps the artboard exactly as drawn. */
html.inv .s6__frame[data-slot="r3"],
html.inv .s6__frame[data-slot="l1"],
html.inv .s6__frame[data-slot="hero"]{ box-shadow:none }

/* ---- hover-to-promote, deck block only -----------------------------
   The resting state is the untouched artboard: no transform, no blur,
   nothing moves until the cursor reaches a card. Hovering a card swaps
   it with whatever holds the hero slot; leaving the deck resets it.
   Click and keyboard still work, for touch and for focus navigation.
   The block itself stays pointer-events:none (it overlaps the reels
   block); only the frames opt back in. */
.s6__block--deck .s6__frame{ pointer-events:auto; cursor:pointer }
.s6__block--deck .s6__frame:focus-visible{
  outline:2px solid var(--c-accent); outline-offset:3px;
}
/* only ever applied while a card is promoted */
.s6__frame--dim{ filter:blur(4px) }
.s6__block--deck .s6__frame{ transition:filter .6s cubic-bezier(.4,0,.2,1) }

@media (prefers-reduced-motion:reduce){
  .s6__block--deck .s6__frame{ transition:none }
}


/* section 2 card frames — one clipping box per layered image */
.s2__frame{position:absolute;overflow:hidden}
.s2__frame img,.s2__frame video{position:absolute;max-width:none;display:block}
/* The media box the user set in Figma: 995x645, centred on the 904x584 card
   ((904-995)/2 = -45.5, (584-645)/2 = -30.5) and clipped by the frame.
   These clips are 9:16, so covering a 1.54:1 box draws them 995x1768.89 and
   shows the middle 36% of the frame height. Chosen over `contain` (which fit
   the whole frame into a 328.5x584 strip) so the card reads full-bleed. */
.s2__frame video.s2__fill{
  position:absolute;
  left:-45.5px; top:-30.5px;
  width:995px; height:645px;
  object-fit:cover;
  object-position:center;
}

/* 5% film grain — Bloom & Body and Fire Island cards only */
.s2__frame--grain::after{
  content:'';
  position:absolute;
  inset:0;
  z-index:3;
  pointer-events:none;
  opacity:.05;
  mix-blend-mode:overlay;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  background-size:200px 200px;
  animation:s2-grain .6s steps(1) infinite;
}
@keyframes s2-grain{
  0%   { background-position:   0px   0px }
  12.5%{ background-position: -37px  22px }
  25%  { background-position:  61px -18px }
  37.5%{ background-position: -84px -49px }
  50%  { background-position:  29px  73px }
  62.5%{ background-position: -55px  41px }
  75%  { background-position:  92px -66px }
  87.5%{ background-position: -13px -91px }
}
@media (prefers-reduced-motion:reduce){
  .s2__frame--grain::after{ animation:none }
}



/* ============================================================
   SECTION 7 — "The Logofolio"  (artboard 1440 × 900)

   Two rows of 222×222 tiles at a 272px pitch, drifting in
   opposite directions. Not pinned — scales to WIDTH and scrolls.

   Each tile is a flattened render of the Figma tile: the artboard
   builds them from a base rect, a masked photo, an inner-shadow
   filter and a logo overlay, all at per-tile opacities. Rebuilding
   that stack in CSS would be guesswork; baking it is exact.
   ============================================================ */
.s7{
  position:relative;
  width:100%;
  height:calc(900px * var(--k));
  overflow:hidden;
  background:var(--c-bg);
}
.s7__art{
  position:absolute;
  left:0; top:0;
  width:1440px; height:900px;
  transform-origin:0 0;
  transform:scale(var(--k));
}
.s7__heading{
  position:absolute;
  left:513px; top:76.5px;
  margin:0;
  font-family:'PP Editorial New',Georgia,serif;
  font-style:italic;
  font-weight:200;
  font-size:82px;
  line-height:82px;
  color:var(--c-ink);
  white-space:nowrap;
}
.s7__intro{
  position:absolute;
  left:0; top:182px;
  width:1440px;
  margin:0;
  font-family:'PP Neue Montreal',Helvetica,Arial,sans-serif;
  font-weight:350;
  font-size:18px;
  line-height:25px;
  color:var(--c-ink);
  text-align:center;
  white-space:nowrap;
}

/* -- the two marquee rows -- */
.s7__row{
  position:absolute;
  left:0; width:1440px; height:222px;
  overflow:hidden;                 /* the artboard is the window */
}
.s7__row--1{ top:287px }
.s7__row--2{ top:565px }
/* The track is wider than the row and holds three copies of the set, so a
   shift of exactly one set width lands on an identical frame — that is what
   makes the loop seamless rather than snapping. */
.s7__track{
  position:absolute;
  top:0;
  display:flex;
  gap:50px;                        /* 272 pitch − 222 tile */
  will-change:transform;
}
.s7__tile{
  flex:0 0 auto;
  width:222px; height:222px;
  border-radius:10px;
  overflow:hidden;
}
.s7__tile img{
  width:100%; height:100%;
  object-fit:cover;
  display:block;
}
@media (prefers-reduced-motion:reduce){
  .s7__track{ will-change:auto }
}

/* reveal mask — vertical clip only; the heading is italic and overhangs */
.s7 .line{display:block;overflow:hidden;margin-left:-14px;padding-left:14px;padding-right:22px;
  /* same vertical bleed as .s3 .line — see the note there */
  margin-top:-26px;padding-top:26px;margin-bottom:-12px;padding-bottom:12px}
.s7 .line > span{display:block;will-change:transform}

/* ============================================================
   SECTION 8 — "Image compositing"  (artboard 1440 × 900)

   Two before/after cards with a draggable divider. Not pinned —
   scales to WIDTH and scrolls.

   The 'before' layer keeps the artboard's own placement, stretch
   included: the raw photo is landscape and the card is portrait,
   and Figma scaled it non-uniformly to fit. Reproduced verbatim at
   the user's instruction ("just do it like i did"). One consequence
   worth knowing: that placement only spans 275 of the card's 456px,
   so the divider's travel is clamped in JS to where the image
   actually reaches — otherwise dragging further would expose bare
   card.
   ============================================================ */
.s8{
  position:relative;
  width:100%;
  height:calc(900px * var(--k));
  overflow:hidden;
  background:var(--c-bg);
}
.s8__art{
  position:absolute;
  left:0; top:0;
  width:1440px; height:900px;
  transform-origin:0 0;
  transform:scale(var(--k));
}
.s8__heading{
  position:absolute;
  left:432px; top:76.5px;
  margin:0;
  font-family:'PP Editorial New',Georgia,serif;
  font-style:italic;
  font-weight:200;
  font-size:82px;
  line-height:82px;
  color:var(--c-ink);
  white-space:nowrap;
}
.s8__intro{
  position:absolute;
  left:0; top:182px;
  width:1440px;
  margin:0;
  font-family:'PP Neue Montreal',Helvetica,Arial,sans-serif;
  font-weight:350;
  font-size:18px;
  line-height:25px;
  color:var(--c-ink);
  text-align:center;
  white-space:nowrap;
}

/* -- the comparison cards -- */
.s8__card{
  position:absolute;
  overflow:hidden;
  cursor:ew-resize;
  touch-action:pan-y;              /* let the page still scroll vertically */
  user-select:none;
}
.s8__card img{position:absolute;max-width:none;display:block;pointer-events:none}
/* the 'before' half: only its WIDTH changes, so the image inside never moves */
.s8__before{
  position:absolute;
  left:0; top:0; bottom:0;
  overflow:hidden;
}
.s8__divider{
  position:absolute;
  top:0; bottom:0;
  width:2px;
  margin-left:-1px;                /* the artboard's line is centred on the split */
  background:var(--c-ink);
  pointer-events:none;             /* the whole card is the drag target */
}
/* keyboard users still get a target, but nothing is drawn until it is focused */
.s8__divider:focus-visible{
  outline:2px solid var(--c-accent);
  outline-offset:4px;
}

/* reveal mask — vertical clip only; the heading is italic and overhangs */
.s8 .line{display:block;overflow:hidden;margin-left:-14px;padding-left:14px;padding-right:22px;
  /* same vertical bleed as .s3 .line — see the note there */
  margin-top:-26px;padding-top:26px;margin-bottom:-12px;padding-bottom:12px}
.s8 .line > span{display:block;will-change:transform}

/* ============================================================
   SECTION 9 — "Print & Collateral"  (artboard 1440 × 900)
   Not pinned — scales to WIDTH and scrolls.
   ============================================================ */
.s9{
  position:relative;
  width:100%;
  height:calc(1130px * var(--k));
  overflow:hidden;
  background:var(--c-bg);
}
.s9__art{
  position:absolute;
  left:0; top:0;
  width:1440px; height:1130px;
  transform-origin:0 0;
  transform:scale(var(--k));
}
/* PRINT SECTION.svg: ink box (434.98,67.10)-(1006.56,136.31), baseline y=135.
   Canvas ink at 82px reproduces 571.54 x 69.21 only when the ampersand is
   left at the body size — this artboard dropped the shrunken amp. */
.s9__heading{
  position:absolute;
  left:439px; top:70.5px;
  margin:0;
  font-family:'PP Editorial New',Georgia,serif;
  font-style:italic;
  font-weight:200;
  font-size:82px;
  line-height:82px;
  color:var(--c-ink);
  white-space:nowrap;
}
.s9__heading .amp{ font-size:inherit }

/* under the card now, sharing a row with the CTA.
   ink (195.62,1021.70)-(685.74,1067.66): 20/28 at weight 400 — set to 18px on request */
.s9__intro{
  position:absolute;
  left:194.14px; top:1016.5px;
  margin:0;
  font-family:'PP Neue Montreal',Helvetica,Arial,sans-serif;
  font-weight:400;
  font-size:18px;
  line-height:25px;
  color:var(--c-ink);
  white-space:nowrap;
}
.s9__frame{position:absolute;overflow:hidden}
.s9__frame img{position:absolute;max-width:none;display:block}

/* Glass slab over the card, left side. Geometry, radius and fill are the
   SVG's own (rounded rect 128,369 408x429 r25, #181818 @ 15%, no stroke).
   The blur is NOT in the export — Figma's Background blur does not survive
   SVG, and the artboard render clearly softens the drapery inside the panel.
   14px is read off that render; it is the one number here without a source. */
.s9__panel{
  position:absolute;
  left:128px; top:369px;
  width:408px; height:429px;
  border-radius:25px;
  background:rgba(24,24,24,.15);   /* #181818 @ 15% — literal: this slab
     sits over the brochure imagery, so it is exempt like the s4 panel */
  -webkit-backdrop-filter:blur(14px);
  backdrop-filter:blur(14px);
  pointer-events:none;
}
.s9__logo{ z-index:2 }
/* ink (155.11,580.56)-(494.96,648.93): three lines of 16/27 at weight 400 */
.s9__body{
  position:absolute;
  left:155px; top:573.01px;
  margin:0;
  font-family:'PP Neue Montreal',Helvetica,Arial,sans-serif;
  font-weight:400;
  font-size:16px;
  line-height:27px;
  color:var(--c-ivory);            /* on the glass slab — see .s9__panel */
  white-space:nowrap;
  z-index:2;
}
/* the shared .cta — same 129x54 box as before, moved to the bottom-right.
   rect 1117.25,1017.25 128.5x53.5 rx10.75 stroke .5 -> border-box 1117,1017 */
.s9__cta{ left:1117px; top:1017px; width:129px; height:54px; --cta-r:11px; z-index:2 }
.s9__cta .label{ left:17.5px; top:18.5px }
.s9__cta .arrow{ left:95.5px; top:21.5px }   /* .5px border shifts the padding box */

/* reveal mask — vertical clip only; the heading is italic and overhangs */
.s9 .line{display:block;overflow:hidden;margin-left:-14px;padding-left:14px;padding-right:22px;
  /* the 82/82 line box clips the italic ascenders of P and C; open the mask
     upward and pull it back so the heading itself stays at top:70.5px */
  margin-top:-26px;padding-top:26px;margin-bottom:-12px;padding-bottom:12px}
.s9 .line > span{display:block;will-change:transform}

/* ============================================================
   SECTION 10 — "About"  (artboard 1440 × 1450)
   The closing section: bio, experience, expertise, contact links.
   All type, no imagery. Not pinned — scales to WIDTH.
   ============================================================ */
/* 1450 -> 1753. The Experience column gained a two-line description per
   role (+157 pitch instead of +91), which pushed Areas of expertise and the
   colophon row down by 262 and 303 respectively. Nothing moved sideways. */
.s10{
  position:relative;
  width:100%;
  height:calc(1753px * var(--k));
  overflow:hidden;
  background:var(--c-bg);
}
.s10__art{
  position:absolute;
  left:0; top:0;
  width:1440px; height:1753px;
  transform-origin:0 0;
  transform:scale(var(--k));
}
/* the artboard lifts the whole panel slightly off black */
.s10__wash{
  position:absolute; inset:0;
  background:var(--c-panel);
  opacity:.6;
}
/* left column labels — Editorial New Ultralight Italic 30 */
.s10__label{
  position:absolute;
  left:74px;
  margin:0;
  font-family:'PP Editorial New',Georgia,serif;
  font-style:italic;
  font-weight:200;
  font-size:30px;
  line-height:30px;
  color:var(--c-ink);
  white-space:nowrap;
}
/* right column — Neue Montreal Book 18 throughout */
.s10__bio,.s10__job,.s10__list{
  position:absolute;
  margin:0;
  font-family:'PP Neue Montreal',Helvetica,Arial,sans-serif;
  font-weight:350;
  font-size:18px;
  color:var(--c-ink);
  white-space:nowrap;
}
.s10__bio{ left:678.86px; top:89.5px; line-height:32px }
.s10__job{ left:680px; line-height:18px }         /* top set inline per role */
/* One line under each role. 16/24 in the Warm Grey the dates already use —
   two steps below the 18px title and one above the 15px date, so the three
   never read as the same voice. Its own measure tops out at 417px against
   the bio's 551, which is what keeps it subordinate. */
.s10__desc{
  position:absolute;
  left:680px;                                    /* top set inline per role */
  margin:0;
  font-family:'PP Neue Montreal',Helvetica,Arial,sans-serif;
  font-weight:350;
  font-size:16px;
  line-height:24px;
  color:var(--c-secondary);
  white-space:nowrap;
}
.s10__list{ line-height:42px }
/* 873.5 -> 1135.5, following the Experience column down */
.s10__list--1{ left:678.79px; top:1135.5px }
.s10__list--2{ left:1065px;   top:1135.5px }

/* contact links — the label is Editorial italic 15, the arrow points up-right */
/* role dates and the closing colophon — both carried by opacity in the
   artboard, not a different fill colour */
.s10__date{
  position:absolute;
  left:680px;
  margin:0;
  font-family:'PP Neue Montreal',Helvetica,Arial,sans-serif;
  font-weight:350;
  font-size:15px;
  line-height:15px;
  color:var(--c-ink);
  color:var(--c-secondary);        /* Warm Grey — secondary text */
  opacity:1;
  white-space:nowrap;
}
.s10__colophon{
  position:absolute;
  left:707px; top:1526.49px;
  margin:0;
  font-family:'PP Neue Montreal',Helvetica,Arial,sans-serif;
  font-weight:400;
  font-size:15px;
  line-height:15px;
  color:var(--c-ink);
  color:var(--c-secondary);
  opacity:.75;
  white-space:nowrap;
}
.s10__link{
  position:absolute;
  top:1527.5px;
  margin:0;
  font-family:'PP Editorial New',Georgia,serif;
  font-style:italic;
  font-weight:200;
  font-size:15px;
  line-height:15px;
  color:var(--c-ink);
  white-space:nowrap;
  text-decoration:none;
}
.s10__link:hover{ text-decoration:underline; text-underline-offset:3px }
.s10__link:focus-visible{ outline:2px solid var(--c-accent); outline-offset:4px }
.s10__arrow{
  position:absolute;
  top:2.33px;
  overflow:visible;
}

/* ============================================================
   SECTION RHYTHM — the empty bands, tightened 2026-09-06
   ------------------------------------------------------------
   Measured off a full-page render at 1440 (k=1, so these are
   design px). Every seam carried a near-uniform ~185px of black,
   and s6 stranded its copy line in a 250px band:

     section  lead  content            trail   seam to next
     s5        74   ...                 104     187
     s6        83   deck 83-484         113     186
                    [484-735 = 250 empty around one copy line]
                    reels 735-1249
                    copy  1311-1352
     s7        73   ...                 113     188
     s8        75   ...                  67     135
     s9        68   ...                  59     153
     s10       94   ...                  51      -

   At 1440 that reads as designed. At 2000+ the artboard scales
   with --k, so 185 design px becomes 257 screen px and a sparse
   section renders as an almost empty screen. Vishal asked for
   every seam at 90 and s6's inner band at 110.

   Done by clipping, not by re-coordinating anything: each section
   already has overflow:hidden, so lifting its .sN__art by the
   surplus lead and shortening the section by lead+trail surplus
   removes exactly the empty top and bottom. Not one absolutely
   positioned child inside a section moves relative to its own
   block — the two .s6 rules below are the only exception, and
   they move whole blocks, not their contents.

   Split 45/45: every section now ends 45px after its last ink and
   starts 45px before its first, so every seam is 90.

   MOBILE IS UNAFFECTED. css/mobile.css sets `top:0 !important` on
   every .sN__art and its own height on every .sN, so both of the
   properties below are already overridden below 768px. Verified
   after the change, not assumed.
   ============================================================ */

/* lead 74 -> 45, trail 104 -> 45 */
.s5__art{ top:calc(-29px * var(--k)) }
.s5{ height:calc(812px * var(--k)) }

/* lead 83 -> 45. Inside s6 the copy sat alone in 484..735; it is
   now 484..594 — 34 above the line, 34 below — which means the
   copy rises 29 and the two blocks under it rise 141. Content then
   ends at 1211 in art coordinates, 1173 after the lift, so the
   section is 1173 + 45. */
.s6__art{ top:calc(-38px * var(--k)) }
.s6__intro--a{ top:512.2px }
.s6__reels--1{ top:594px }
.s6__intro--b{ top:1164px }
.s6{ height:calc(1218px * var(--k)) }

/* lead 73 -> 45, trail 113 -> 45 */
.s7__art{ top:calc(-28px * var(--k)) }
.s7{ height:calc(804px * var(--k)) }

/* lead 75 -> 45, trail 67 -> 45 */
.s8__art{ top:calc(-30px * var(--k)) }
.s8{ height:calc(848px * var(--k)) }

/* lead 68 -> 45, trail 59 -> 45 */
.s9__art{ top:calc(-23px * var(--k)) }
.s9{ height:calc(1093px * var(--k)) }

/* lead 94 -> 45. s10 is last, so its trailing 51 is the page's own
   bottom margin — trimmed to 45 for the same rhythm, nothing more.
   1235 -> 1538.5 with the taller Experience column: the links row now ends
   at artboard 1542.5, less the 49 lifted off the top, plus the same 45.
   1538.5 -> 1341.5 (2026-09-27): the links row and colophon moved to the
   closing screen (.pfoot, css/phero.css), so s10 now ends on Areas of
   expertise — its last line box closes at 1345.5, less 49, plus 45. The
   phone's height is its own (css/mobile.css). */
.s10__art{ top:calc(-49px * var(--k)) }
.s10{ height:calc(1341.5px * var(--k)) }

/* ============================================================
   Palette hookups the markup cannot reach on its own.
   Tokens and the .inv switch live in css/nav.css.
   ============================================================ */

/* The 10x10 CTA / link arrows are inline SVG carrying stroke="#F3F3F3" as a
   presentation attribute. A CSS declaration outranks a presentation
   attribute, so the token is applied here rather than hand-editing the
   eleven copies in index.html. */
.arrow path,.s10__arrow path{ stroke:var(--c-accent) }
/* ...except the hero's, which is exempt along with the rest of the hero:
   a white arrow on the black pill, in both modes. */
.hero__cta .arrow path{ stroke:var(--c-ivory) }
/* every arrow that sits INSIDE a filled button flips to the ivory */
.cta .arrow path{ stroke:var(--c-ivory) }

/* Selection: the accent's one appearance that is not a stroke. */
::selection{ background:var(--c-accent); color:var(--c-ivory) }

/* ============================================================
   Ground under the photographic cards.

   Two framed images do not fill their frame: Burger Bae's Social backdrop
   leaves 258.8 design px uncovered (on the LEFT in the image's own space,
   which its `transform:scaleX(-1)` mirrors to the right edge), and the Print
   card leaves 19px on the left. Both were authored against a pure black page,
   where an uncovered sliver was indistinguishable from the ground; on the
   ivory they are bright gaps.

   These cards are dark photography in every slide, so the ground is the FIXED
   soft black, not the themed --c-media-bg, which would go light with the page
   and hide nothing.

   Repositioning the images instead would re-crop them — Burger Bae's is only
   1371.1 wide inside a 1332 frame, so covering it means moving the crop 278px.
   That is a change to Vishal's artboard; this is not.

   min-width so the phone keeps css/mobile.css's own grounds, which are more
   specific than this rule and would otherwise be outranked by it.
   ============================================================ */
@media (min-width:769px){
  .s4__media > .s4__frame:first-child,
  .s9__card{ background:var(--c-soft-black) }
}
