/* ==========================================================================
   FONTS
   ========================================================================== */

@font-face {
  font-family: "Neue Haas";
  src: url("assets/typography/NeueHaasDisplayRoman.ttf") format("truetype");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "Neue Haas";
  src: url("assets/typography/NeueHaasDisplayRomanItalic.ttf") format("truetype");
  font-weight: 400;
  font-style: italic;
  font-display: swap;
}

@font-face {
  font-family: "Neue Haas";
  src: url("assets/typography/NeueHaasDisplayMediu.ttf") format("truetype");
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "Neue Haas";
  src: url("assets/typography/NeueHaasDisplayBold.ttf") format("truetype");
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "Neue Haas";
  src: url("assets/typography/NeueHaasDisplayLight.ttf") format("truetype");
  font-weight: 300;
  font-style: normal;
  font-display: swap;
}


/* ==========================================================================
   DESIGN TOKENS
   ========================================================================== */

:root {
  /* brand palette — hexes read straight out of assets/palette/color palette.svg */
  --cloud:       #FFFCF1;   /* the one light tone used site-wide */
  --midnight:    #131314;   /* the page background */
  --teal:        #355564;
  --brick:       #823233;
  --olive:       #A7A155;
  --blue:        #AEC6CF;

  /* legacy aliases — every light tone now resolves to cloud, so any
     existing --cream / --cream-light usage is automatically on-palette. */
  --cream-light: var(--cloud);
  --cream:       var(--cloud);
  --black:       var(--midnight);

  /* the page is midnight; type on it is cloud */
  --bg:       var(--midnight);
  --ink:      var(--cloud);
  --ink-soft: rgba(255, 252, 241, 0.62);   /* muted cloud, for secondary type */
  --line:     rgba(255, 252, 241, 0.22);   /* hairlines on midnight */
  --accent:   var(--cloud);

  --font: "Neue Haas", -apple-system, BlinkMacSystemFont, "Helvetica Neue", Arial, sans-serif;

  /* ---- fluid spacing tokens: scale smoothly 320→1440, flat at the
     desktop value (no change ≥ ~800-980px, well below the 1024 floor) ---- */
  --edge:        clamp(1.1rem, 5vw, 2.5rem);
  --nav-h:       clamp(3.5rem, 9vw, 5.5rem);
  --section-pad: clamp(3rem, 8vw, 6rem);   /* mobile stacked-section rhythm; unused on desktop */

  /* ---- fluid type scale: MIN tuned for phones, MAX = current desktop
     size (each formula is unchanged from its pre-existing desktop rule,
     so ≥1024px renders byte-identical) ---- */
  --fs-display: clamp(2rem, 5.6vw, 6.6rem);   /* contact heading */
  --fs-h2:      clamp(2rem, 5vw, 6.2rem);     /* studio heading */
  --fs-h3:      clamp(1.8rem, 4.4vw, 5.5rem); /* works heading */
  --fs-services:clamp(2rem, 4.6vw, 5.6rem);   /* services panel heading */
  --fs-bleed:   clamp(3.5rem, 15.5vw, 15rem); /* full-bleed display type: about + contact headings */
  --fs-body:    1rem;
  --fs-meta:    0.82rem;                      /* nav links, copy, back-to-top */
  --fs-nav:     0.82rem;
}


/* ==========================================================================
   BASE
   ========================================================================== */

* { box-sizing: border-box; margin: 0; padding: 0; }

/* NO overflow-x:hidden on html or body: either one turns the root into a
   scroll container, which silently breaks `position: sticky` for every
   descendant (it kills the services accordion). The full-bleed headings are
   clipped by their own sections instead — see .studio / .contact__card. */
/* NO scroll-behavior:smooth — it fights Lenis for control of anchor jumps
   (Lenis' own docs call this out). Anchors are routed through
   lenis.scrollTo() in the inline script instead. */
html { scroll-behavior: auto; }

body {
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font);
  font-size: 17px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}

a { color: inherit; }

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


/* ==========================================================================
   SCROLL REVEAL
   ========================================================================== */

[data-reveal] {
  opacity: 0;
  transform: translateY(40px);
  transition: opacity 0.8s cubic-bezier(0.16, 1, 0.3, 1),
              transform 0.8s cubic-bezier(0.16, 1, 0.3, 1);
}

[data-reveal].revealed {
  opacity: 1;
  transform: translateY(0);
}


/* ==========================================================================
   NAV — pill-shaped links: outline over black sections, filled when active
   ========================================================================== */

.nav {
  position: sticky;
  top: 0;
  z-index: 1100;      /* above the pview overlay so nav links stay clickable */
  background: transparent;
  display: flex;
  justify-content: center;
  align-items: center;
  height: var(--nav-h);
  padding: 0 var(--edge);
  transition: transform 0.35s cubic-bezier(0.16, 1, 0.3, 1);
}

/* while a project is open the whole bar is inert AND click-through, so the
   BACK button underneath it stays reachable */
.nav--locked { pointer-events: none; }
.nav--locked .nav__links,
.nav--locked .nav__burger { pointer-events: none; }

.nav--hidden {
  transform: translateY(-100%);   /* hide on scroll down, peek back on scroll up */
}

.nav__links {
  display: flex;
  gap: 0.9rem;
  font-size: var(--fs-nav);
  font-weight: 700;
  letter-spacing: 0.02em;
  text-transform: uppercase;
}

/* brand mark + burger — hidden on desktop, shown by the mobile nav below */
.nav__brand,
.nav__burger,
.nav__drawer { display: none; }

.nav__links a {
  color: var(--cream-light);
  text-decoration: none;
  border: 1.5px solid var(--cream-light);
  border-radius: 999px;
  padding: 0.62em 1.5em;
  transition: background 0.2s ease, color 0.2s ease, border-color 0.2s ease, opacity 0.15s ease;
}

.nav__links a:hover { opacity: 0.7; }

.nav__links a.nav__pill--active {
  background: var(--cream-light);
  color: var(--black);
}

/* over the cream services panels: dark outline / dark-on-cream fill */
.nav--onlight .nav__links a {
  color: var(--black);
  border-color: var(--black);
}
.nav--onlight .nav__links a.nav__pill--active {
  background: var(--black);
  color: var(--cream-light);
}


/* ==========================================================================
   HERO — black bg, cream logo + tagline, fade-in on load
   ========================================================================== */

.hero {
  min-height: 100svh;
  margin-top: calc(var(--nav-h) * -1);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0 var(--edge);
  position: relative;
  /* flat midnight, no grain overlay — the reference
     (assets/mockups/portfolio layout.png) samples a uniform #131314 top to
     bottom, and a noise texture here made the hero read lighter than every
     other section, showing a visible seam at the hero/works boundary. */
  background: var(--midnight);
  color: var(--cloud);
  overflow: hidden;
}

.hero__content {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: clamp(1.4rem, 3vw, 2.4rem);
  /* simple fade-in on load (placeholder for the pixel-reveal, to be revisited) */
  opacity: 0;
  transform: translateY(10px);
  transition: opacity 1s cubic-bezier(0.16, 1, 0.3, 1),
              transform 1s cubic-bezier(0.16, 1, 0.3, 1);
}
.hero__content.is-revealed {
  opacity: 1;
  transform: translateY(0);
}

.hero__logo {
  flex: 0 0 auto;
  width: 40.5vw;
}

.hero__logo img {
  width: 100%;
  height: auto;
  /* Source is a cloud-filled copy of the mark (cabou-horizontal-cloud.svg).
     The old `brightness(0) invert(1)` filter rendered it pure #FFFFFF, which
     read brighter than the cloud (#FFFCF1) tagline beside it — recolouring
     the SVG itself keeps every hero element on exactly one light tone. */
}

.hero__line {
  font-size: clamp(0.78rem, 1.35vw, 1.05rem);
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  text-align: center;
  color: var(--cream-light);
}

@media (prefers-reduced-motion: reduce) {
  .hero__content { transition: none; }

  /* everything else reveals via a transition to a class-set end state,
     so removing the transition still lands on the correct final look */
  .nav__drawer,
  .nav__burger span,
  .pview__head,
  .pview__item,
  .pview__item img,
  .talk__panel,
  .talk { transition: none; }

  .polaroid,
  .polaroid:hover { transition: none; }

  .wtile { animation: none; }
}


/* ==========================================================================
   WORKS — scattered photo collage on black, "SELECTED WORKS" overlaid.
   Tile positions/sizes measured directly off the mockup (2442px-wide
   reference), all axis-aligned. .works__stage is a fixed-ratio box
   (1735x1232 in the mockup) so the whole composition scales as one unit;
   tiles + the heading are placed as % of that stage, same technique as the
   contact section's polaroid stage.

   FLOAT ANIMATION — mechanic studied directly from
   github.com/olivierlarose/floating-image-gallery (see the inline script):
   tiles are grouped into 3 depth "planes"; mousemove accumulates a decaying
   force (lerp-eased back to 0) applied to each plane at 1x / 0.5x / 0.25x,
   via relative gsap.set() increments — NOT a looping @keyframes wobble.
   Planes get a subtle brightness step (closer = brighter/faster) for the
   same depth cue as the reference, tuned lighter than its own 0.7/0.6/0.5
   since these are portfolio photos that need to stay legible, not purely
   decorative. See the inline <script>'s WORKS FLOAT block.
   ========================================================================== */

.works {
  position: relative;
  background: var(--midnight);
  color: var(--cloud);
  /* top padding cut from clamp(4rem,9vw,7rem): measured hero-tagline -> first
     work image at 25.33% of viewport width against the mockup's 19.33%.
     Trimming ~73px lands it at ~20.5% — matching the reference's rhythm while
     keeping the section breathing. Bottom padding is unchanged: it already
     measured 5.29% vs the mockup's 5.32%. */
  padding: clamp(1.5rem, 2.6vw, 2.75rem) var(--edge) clamp(1.5rem, 3vw, 2.5rem);
}

/* The stage IS the tiles' content bounding box, measured off
   assets/mockups/works.png via its alpha channel: that box is 3447x2442
   native (aspect 1.4115) and spans 70.58% of the reference frame's width.
   Tile --x/--y/--w below are exact % of this box, so the arrangement
   reproduces the mockup 1:1 at any viewport size, and the heading's
   50%/50% centre matches the reference's own measured text centre
   (49.99% / 49.98%). */
.works__stage {
  position: relative;
  width: min(70.58vw, 1400px);
  margin: 0 auto;
  aspect-ratio: 3447 / 2442;
}

/* The three planes each cover the whole stage, so the topmost one swallowed
   every pointer event and the "click me" cursor only appeared over the tiles
   that happened to live in that plane. Planes are transparent to the pointer;
   the tiles themselves take the hits. */
.wplane {
  position: absolute;
  inset: 0;
  pointer-events: none;
}

.wplane[data-plane="1"] { filter: brightness(1); }
.wplane[data-plane="2"] { filter: brightness(0.88); }
.wplane[data-plane="3"] { filter: brightness(0.76); }

.wtile {
  position: absolute;
  pointer-events: auto;   /* re-enable inside the pointer-transparent .wplane */
  left: var(--x);
  top: var(--y);
  width: var(--w);
  aspect-ratio: var(--ar);
  z-index: var(--z, 1);
  padding: 0;
  border: 0;
  border-radius: 4px;
  overflow: hidden;
  background: var(--black);
  cursor: pointer;
  font: inherit;
  color: inherit;
  will-change: transform;
}

/* --ar now matches each source photo's true native aspect ratio (measured
   from the actual files, not the mockup's rendered tile shape), so cover
   no longer crops anything — the box IS the image's own shape. */
.wtile img { display: block; width: 100%; height: 100%; object-fit: cover; }

.wtile:hover { z-index: 5; }
.wtile:focus-visible { outline: 2px solid var(--blue); outline-offset: 4px; }

/* "SELECTED WORKS" — dead center of the frame (left/top 50% + translate
   -50%/-50%, matching the reference's .title), uniform cream color (no
   per-letter accent colors). X-RAY EFFECT: mix-blend-mode:difference
   against a cream-light base inverts the text against whatever photo
   content is floating behind it at any moment, so it stays legible even
   when a .wtile drifts directly underneath a letter — verified live
   against the actual collage photos (difference read more clearly than
   exclusion here, which washed out against the lighter/mid-tone images). */
.works__heading {
  position: absolute;
  /* centered against the tiles' actual computed content bounding box, not
     the .works__stage box itself — the stage has empty margin below the
     bottom row (aspect-ratio 1735:1232 is wider than the tiles' own
     cluster), so 50/50 read as off-center against the real collage. */
  left: 50%;
  top: 50%;
  /* standalone `translate` property, NOT transform: translate(). The
     scroll-reveal rule [data-reveal].revealed sets `transform` at a higher
     specificity and would replace a transform-based centre; `translate`
     composes independently and survives. */
  translate: -50% -50%;
  width: max-content;
  max-width: 90%;
  z-index: 10;
  font-size: clamp(1.6rem, 4.4vw, 5.2rem);
  font-weight: 400;
  text-align: center;
  text-transform: uppercase;
  letter-spacing: -0.02em;
  line-height: 1;
  color: var(--cream-light);
  mix-blend-mode: difference;
  pointer-events: none;
}

/* custom cursor badge (assets/ui/project cursor.svg, 104x25) */
.proj-cursor {
  position: fixed;
  top: 0;
  left: 0;
  width: 104px;
  height: 25px;
  pointer-events: none;
  z-index: 900;
  opacity: 0;
  transition: opacity 0.18s ease;
  will-change: transform;
}

.proj-cursor--on { opacity: 1; }


/* ==========================================================================
   PROJECT VIEW — plain "Back" text, tag pills, metadata rows, a
   two-paragraph description and a scroll-only image carousel
   (assets/mockups/project view.svg). Falls back to a centred
   "Coming soon" body via .pview--simple.
   ========================================================================== */

.pview {
  position: fixed;
  inset: 0;
  z-index: 1000;
  background: var(--cloud);
  color: var(--midnight);
  display: flex;
  flex-direction: column;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.5s ease;
}

.pview--open { opacity: 1; pointer-events: auto; }

/* BACK — plain bold text, top-left, no pill */
.pview__back {
  position: absolute;
  top: calc(var(--nav-h) * 0.42);
  left: var(--edge);
  z-index: 1200;        /* above the nav (1100), which overlaps this corner */
  background: none;
  border: none;
  color: var(--midnight);
  font-family: var(--font);
  font-size: 0.85rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  padding: 0.4rem;
  cursor: pointer;
  transition: transform 0.2s ease, opacity 0.2s ease;
}
.pview__back:hover { transform: translateX(-4px); opacity: 0.6; }
.pview__back:focus-visible { outline: 2px solid var(--midnight); outline-offset: 3px; }

.pview__head {
  position: absolute;
  inset: 0;
  flex: 0 0 auto;
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 3rem;
  padding: 0;
  display: block;
  opacity: 0;
  transform: translateY(24px);
  transition: opacity 0.7s cubic-bezier(0.16,1,0.3,1),
              transform 0.7s cubic-bezier(0.16,1,0.3,1);
}

.pview--in .pview__head { opacity: 1; transform: translateY(0); }

/* left column: tag pills, then the PROJECT NAME / DATE rows */
.pview__intro {
  position: absolute;
  left: 0.98vw;
  top: 11.18vh;
  width: 38vw;
}

/* right column: the two description paragraphs */
.pview__desc {
  position: absolute;
  left: 41.38vw;
  top: 11.51vh;
  width: 39.91vw;
}

/* service tags — plain bold text, two per row (max-width forces the wrap the
   mockup shows). Column gap 2.7vw and row pitch 3.89vh are measured values. */
.pview__tags {
  display: flex;
  flex-direction: column;
  row-gap: 1.19vh;        /* 3.89vh row pitch minus the 2.70vh pill */
}

.pview__tag-row {
  display: flex;
  column-gap: 0.61vw;
}

/* midnight pills with cloud type, as in the reference */
.pview__tag {
  background: var(--midnight);
  color: var(--cloud);
  border-radius: 999px;
  /* reference pill: 2.72vh tall with a 1.08vh cap height, i.e. ~1.50vh type
     with tight vertical and generous horizontal padding */
  font-size: 1.50vh;
  font-weight: 500;
  line-height: 1.2;
  padding: 0.31em 1.65em;
  white-space: nowrap;
}

.pview__meta {
  display: flex;
  flex-direction: column;
  /* first label row lands at 23.47vh; the tag block ends at 21.68vh */
  margin-top: 1.62vh;
  gap: 0.80vh;            /* 2.70vh row pitch minus the 1.90vh line box */
}

.pview__meta-row {
  display: flex;
  align-items: baseline;
  gap: 0;
}

.pview__meta-label {
  font-size: 1.58vh;
  font-weight: 700;
  color: var(--midnight);
  text-transform: uppercase;
  letter-spacing: 0.01em;
  line-height: 1.2;
  width: 8.26vw;          /* label at 1.02vw, value column starts at 9.28vw */
  flex: 0 0 auto;
}

.pview__meta-value {
  font-size: 1.58vh;
  font-weight: 400;
  line-height: 1.2;
  color: var(--midnight);
}

.pview__desc {
  color: var(--midnight);
  font-size: 1.62vh;      /* 2.19vh line pitch / 1.35 line-height */
  line-height: 1.35;
}
.pview__desc p { margin-bottom: 1.24vh; font-weight: 400; }
.pview__desc p:last-child { margin-bottom: 0; }

/* ---- image carousel: bottom-aligned, varying heights, scroll-only ---- */
/* viewport: the track inside it is what actually moves, via GSAP Draggable +
   InertiaPlugin (see index.html). Native overflow scrolling is deliberately
   off — it can't do throw/inertia or snap-on-release. */
.pview__scroll {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 52.45vh;
  overflow: hidden;
  scrollbar-width: none;
}
.pview__scroll::-webkit-scrollbar { display: none; }

/* fallback if the Draggable/Inertia plugins fail to load: revert to native
   horizontal scrolling so the carousel is never stuck */
.pview__scroll.is-native { overflow-x: auto; }

.pview__track {
  display: flex;
  flex-direction: row;
  align-items: flex-end;         /* shared baseline, staggered tops */
  gap: 1.6vw;
  width: max-content;
  height: 100%;
  padding: 0 var(--edge) 1.45vh;
  will-change: transform;
  cursor: grab;
}
.pview__track.is-dragging { cursor: grabbing; }

.pview__item {
  flex: 0 0 auto;
  display: flex;
  align-items: flex-end;
  opacity: 0;
  transform: translateY(34px);
  transition: opacity 0.8s cubic-bezier(0.16, 1, 0.3, 1),
              transform 0.8s cubic-bezier(0.16, 1, 0.3, 1);
}

/* Heights are driven by --hf, each image's share of its source strip height,
   so the staggered tops reproduce the artwork exactly rather than being
   approximated with nth-child rules. Bottoms align via align-items:flex-end. */
.pview__item img {
  display: block;
  width: auto;
  height: calc(var(--hf, 1) * 51vh);
  opacity: 0;
  transition: opacity 0.55s ease;
}
.pview__item img.is-loaded { opacity: 1; }

.pview--in .pview__item { opacity: 1; transform: translateY(0); }

/* ---- simple "Coming soon" body ---- */
.pview__simple {
  display: none;
  flex: 1 1 auto;
  align-items: center;
  justify-content: center;
  text-align: center;
}
.pview__simple p {
  color: var(--midnight);
  font-size: clamp(1.6rem, 3.6vw, 2.6rem);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: -0.01em;
}

.pview--simple .pview__head,
.pview--simple .pview__scroll { display: none; }
.pview--simple .pview__simple { display: flex; }


/* ==========================================================================
   SERVICES — 3 full-bleed jazzicreates-style pinned stacking panels
   ========================================================================== */

.services {
  position: relative;
}

/* jazzicreates.tv-style pinned, cascading stacking cards (GSAP
   ScrollTrigger — see the inline <script> at the bottom of index.html).
   Each panel's ScrollTrigger pins it for one viewport height of scroll
   (RANGE = window.innerHeight) with pinSpacing:false, which means GSAP
   does NOT insert a spacer to reserve that scroll distance itself — the
   wrap has to provide it via real document height, or the pin barely
   holds before the page runs out of room to scroll through it. */
/* Scroll-driven sticky accordion (jazzicreates.tv pacing, our styling).
   Each wrap is one viewport tall and sticks at the top, so its panel holds
   in place while the NEXT wrap scrolls up and covers it — a layered
   stacked-card effect. Later wraps are later in the DOM and are positioned,
   so they paint above earlier ones without needing explicit z-index.

   This replaces a GSAP `pin: true, pinSpacing: false` implementation, which
   reserved no scroll room of its own and had to be propped up with
   min-height:180vh per wrap — that inflated the services section to ~8x the
   reference height and made the page ~3x too long. Sticky needs exactly
   100vh per card and no compensating hacks. */
.services__wrap {
  /* display:contents — the wrap generates no box, so each panel's sticky
     containing block becomes .services itself. Previously the panel was
     sticky inside its own 100vh wrap, so its dwell ended with that wrap and
     it scrolled AWAY off the top instead of staying put and being covered. */
  display: contents;
}

/* the final card is never covered by another, so it doesn't need a full
   viewport of scroll behind it — reserving one left a large black void
   between the last services card and the contact card. */
/* Each wrap is 68vh but the card is only ~60.6vh, leaving ~7.4vh of midnight
   between consecutive cards. Pulling every wrap after the first upward closes
   that and gives a slight overlap — without shortening the wrap, which is what
   provides the pin duration (shrink the wrap and the card stops pinning). */
.services__panel:last-of-type { margin-bottom: 0; }

/* the card is the sticky element now — it pins just under the nav and the
   next card slides up over it */
/* stepped offsets: each card lands slightly lower than the last, so the top
   edge of every previous card stays visible behind it. margin-bottom is the
   scroll distance before the next card starts covering this one. */
.services__panel {
  position: sticky;
  top: calc(9vh + var(--stack-i, 0) * 2.2rem);
  /* zero gap: each panel's top edge sits flush against the previous panel's
     bottom edge, so no midnight ever shows between them. The scroll distance
     each panel dwells for is then its own height, which is what carries the
     hand-off. */
  margin-bottom: 0;
}

/* inset from the page edges by ~2.87% each side (measured: at native
   x=2422/2442px width the background stays black straight through the
   services y-range; the colored panel only starts at x≈70) — applied to
   the pinned element itself, not a wrapper, since ScrollTrigger's pin
   position only depends on the element's vertical document position,
   which this horizontal inset doesn't touch. */
/* ---------------------------------------------------------------------------
   EDITORIAL 2-COLUMN CARD (redesign)

   Replaces a version whose every element was absolutely positioned at a fixed
   % of a locked 3.4545:1 box (traced from assets/mockups/services/*.png).
   That produced structural dead space no amount of tuning could remove:
   24.5% of the card width empty between the text column and the image, and
   31.9% of its height empty between the paragraph and the pills.

   Now: content lives in normal flow inside a 58/42 grid, so spacing is a
   designed relationship between neighbours rather than a set of hard-coded
   offsets. Card height follows its content (with a floor), which makes
   clipping structurally impossible — the old failure mode.
   --------------------------------------------------------------------------- */
.services__panel {
  width: 94.3%;
  max-width: 94.3%;
  margin: 0 auto;
  /* FIXED aspect, matching the banner PNGs exactly (4549x1671 = 2.7223) so
     all three cards are identical in size — a content-driven floor made the
     Creative card taller than the other two (2-line heading + 6 body lines).
     Safe to fix here because every child below is placed as a % of the panel
     and sized in cq units, so nothing can outgrow the box. */
  aspect-ratio: 4549 / 1671;
  container-type: size;
  overflow: hidden;
  border-radius: 20px;
  /* scaled/dimmed from centre as the next card covers this one (scrubbed in
     the inline script); origin matters so the card shrinks toward its middle
     rather than drifting up-left */
  transform-origin: center center;
}

/* Measured off the updated assets/mockups/services/*.png (4549x1671).
   All three banners share one structure — only the body line-count differs
   (5 lines; Creative has 6). Percentages are of the panel; type is in cqw so
   it scales with the card. Values as % of panel HEIGHT unless noted:
     heading  top 13.90, ink 11.90, one line, runs to ~58% of panel WIDTH
     body     top 33.03, line pitch 4.43, ink 3.55
     pills    top 70.62, row 5.00, row pitch 6.52
     image    x 64.30..98.92 (w 34.62%), y 32.38..88.27 (h 55.89)
   Note the image spans exactly body-top -> pills-bottom: an intentional
   alignment in the design, not a coincidence. */
.services__inner {
  position: absolute;
  inset: 0;
  transform-origin: center center;
  will-change: transform;
}

.services__copy {
  position: absolute;
  inset: 0;
  z-index: 1;
}

.services__heading {
  position: absolute;
  left: 1.35%;
  top: 13.90cqh;
  margin: 0;
  font-size: 5.80cqw;
  font-weight: 400;          /* roman — the design is NOT bold here */
  text-transform: uppercase;
  letter-spacing: -0.02em;
  line-height: 1;
  white-space: nowrap;
}

.services__body {
  position: absolute;
  left: 1.10%;
  top: 33.03cqh;
  width: 34%;
  margin: 0;
  font-size: 1.37cqw;
  font-weight: 500;          /* medium — heavier than the body default */
  line-height: 1.187;
}

.services__tags {
  position: absolute;
  left: 1.08%;
  top: 70.62cqh;
  width: 32%;
  margin: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 0.558cqw 0.5cqw;
}

.services__tags span {
  border-radius: 999px;
  font-size: 0.78cqw;
  font-weight: 700;
  letter-spacing: 0.01em;
  text-transform: uppercase;
  line-height: 1;
  padding: 0.528cqw 0.95cqw;
  white-space: nowrap;
}

.services__media {
  position: absolute;
  left: 64.30%;
  top: 32.38cqh;
  width: 34.62%;
  height: 55.89cqh;
  border-radius: 12px;
  overflow: hidden;
}

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

/* ---- panel 1: Branding Strategy — cream / black / olive pills ---- */
.services__panel--branding {
  background: var(--cream-light);
  color: var(--black);
}
.services__panel--branding .services__tags span {
  background: var(--olive);
  color: var(--black);
}

/* ---- panel 2: Packaging Design — olive ground, teal type, cloud pills
       (sampled off assets/mockups/services/packaging design.png:
        bg #A7A155, ink #355564, pill #FFFCF1 / #355564) ---- */
.services__panel--packaging {
  background: var(--olive);
  color: var(--teal);
}
.services__panel--packaging .services__tags span {
  background: var(--cloud);
  color: var(--teal);
}

/* ---- panel 3: Creative Strategy — blue ground, brick type, cloud pills
       (sampled off creative strategy.png: bg #AEC6CF, ink #823233,
        pill #FFFCF1 / #823233) ---- */
.services__panel--creative {
  background: var(--blue);
  color: var(--brick);
}
.services__panel--creative .services__tags span {
  background: var(--cloud);
  color: var(--brick);
}


/* ==========================================================================
   STUDIO — "Meet Francesca": full-bleed display heading, portrait + a
   single short paragraph (no more cursive CTA / expandable profile panel).
   ========================================================================== */

/* Rebuilt against assets/mockups/about.png (4574x2162 native), measured via
   numeric pixel bounding-box analysis (thresholded non-black scan against a
   black-composited crop, not eyeballed): heading x 0.74%-99.9%, y 0.74%-
   18.6%; portrait x 20.11%-46.74%, y 18.59%-100% (touches the section's own
   bottom edge); bio x 50.46%-80.08%, y 38.21%-61.84%. .studio__stage is a
   fixed 4574:2162 box — same %-of-stage technique already used for
   .works__stage / .polaroid-stage — so every element scales together and
   the section's height stays locked to the portrait's bottom edge with no
   separate top/bottom section padding. */
.studio {
  position: relative;
  background: var(--midnight);
  color: var(--cloud);
  padding: 0;
  /* clips the full-bleed "MEET FRANCESCA" heading locally, so the page
     doesn't need overflow-x:hidden on html/body (which would break sticky) */
  overflow: hidden;
}

.studio__stage {
  position: relative;
  width: 100%;
  /* measured from assets/mockups/about.png (2442x1098) */
  aspect-ratio: 2442 / 1098;
}

/* full-bleed display heading: font-size is a pure vw value (no clamp) so
   the rendered text width scales in lockstep with the stage/viewport width
   at any size — calibrated live (getBoundingClientRect vs. stage width) at
   1280px and 1440px so "MEET FRANCESCA" spans left:0.74% to right:99.9%
   without re-measuring per breakpoint. */
.studio__head {
  position: absolute;
  left: 6.72%;
  top: 12.9%;              /* ink lands at the measured 15.85% */
  display: inline-block;   /* shrinks to the heading, giving RÉSUMÉ its anchor */
}

.studio__heading {
  position: static;
  display: block;
  /* The portrait PNG carries 11% transparent padding above the head, so the
     portrait's *visible* top is 4.6% + 0.11*95.4 = 15.1% of the section — not
     the 4.6% element top. Aligning the heading's box to that is what makes
     the two read as meeting. */
  width: auto;
  margin: 0;
  text-transform: uppercase;
  white-space: nowrap;
  /* spans x 6.8%..93.7% in the reference (86.9% of page width) */
  font-size: 9.3vw;
  font-weight: 400;   /* roman, matching the paragraph below it */
  letter-spacing: -0.03em;
  /* tuned so FRANCESCA's F lands at 41.1% of the stage — the reference value,
     where her hair ends at 42.05%, leaving just a sliver of the F tucked
     behind her head so the word still reads. */
  word-spacing: 0.97em;
  line-height: 1;
}

.studio__portrait {
  position: absolute;
  left: 23.34%;
  top: 1.15%;              /* visible head lands at the measured 12.02% */
  width: 24.94%;
  bottom: 0;
  overflow: hidden;
  border-radius: 4px;
}

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

/* intro sits narrow; the body below runs wider and lighter — the measure
   opening up is the point of the design */
.studio__intro {
  position: absolute;
  left: 52.91%;
  top: 40.2%;
  width: 20.84%;
}
.studio__intro p {
  margin: 0 0 0.95em;
  font-size: clamp(0.9rem, 1.2vw, 1.15rem);
  font-weight: 500;          /* medium */
  line-height: 1.12;
}
.studio__intro p:last-child { margin-bottom: 0; }

.studio__body {
  position: absolute;
  left: 52.91%;
  top: 62%;
  width: 35.09%;
}
.studio__body p {
  margin: 0 0 0.95em;        /* paragraph break = ~1 blank line, per reference */
  font-size: clamp(0.9rem, 1.2vw, 1.15rem);
  font-weight: 400;          /* roman */
  line-height: 1.12;
}
.studio__body p:last-child { margin-bottom: 0; }






/* ==========================================================================
   CONTACT — outer black section; an inset cream rounded card holds 5
   static Polaroid-style tiles (Resume dropped) and a full-bleed black-on-
   cream heading at the card's bottom. A 4-item footer sits below the card,
   still on the black section background. No drop-in physics this pass —
   the client will ask for a new animation treatment here later.
   ========================================================================== */

/* no horizontal padding here: the reference's card is 97.09% of the FULL page
   width, so the card must measure against the viewport, not a padded box.
   The footer below re-applies --edge to itself. */
.contact {
  position: relative;
  background: var(--midnight);
  color: var(--cloud);
  padding: clamp(3rem, 7vw, 6rem) 0 0;
}

/* inset card: ~2.5% margin each side, matching the services panels'
   inset-card treatment (measured off the mockup: card left/right edges
   sit a little tighter than the services inset, ~2.5% vs ~2.87%) */
/* Measured off assets/mockups/portfolio layout.png: the card is 2371x1238
   logical (97.09% of the 2442 page width, aspect 1.9152). Height is pinned
   by aspect-ratio rather than left to content+padding, so the card keeps the
   reference's proportions at every viewport instead of drifting with type
   size. */
.contact__card {
  position: relative;
  width: 97.09%;
  max-width: 97.09%;
  aspect-ratio: 2371 / 1238;
  margin: 0 auto;
  background: var(--cloud);
  color: var(--midnight);
  border-radius: 24px;
  overflow: hidden;
  padding: 0;
}

/* the polaroid stage is a fixed-ratio box (3838x1205 in the mockup,
   measured via connected-component analysis of contact.png) so the whole
   arrangement scales as one unit; tiles are placed as % of it */
/* cluster bbox measured within the card: left 8.6%, top 23.0%, width 81.1%,
   aspect 1924/547 — positioned absolutely so it holds those proportions
   regardless of the heading's type size below it. */
.polaroid-stage {
  position: absolute;
  left: 8.6%;
  top: 23.0%;
  width: 81.1%;
  max-width: 81.1%;
  aspect-ratio: 1924 / 547;
}

/* real Polaroid PNG assets (assets/ui/polaroid 1-5.png) — rotation, border,
   shadow and paper texture are already baked into each image, so NO CSS
   rotate() is applied here (--r is gone). --w/--ar size the tile to the
   full PNG canvas (image includes ~8-9% transparent margin around the
   visible card for the baked-in tilt); --x/--y were back-solved so the
   VISIBLE card lands on its measured mockup position despite that margin. */
.polaroid {
  position: absolute;
  left: var(--x);
  top: var(--y);
  width: var(--w);
  aspect-ratio: var(--ar);
  z-index: var(--z, 1);
  display: block;
  padding: 0;
  background: none;
  border: none;
  cursor: pointer;
  font: inherit;
  text-decoration: none;
  transition: transform 0.25s ease;
}
.polaroid img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
  pointer-events: none;
}
.polaroid:hover { transform: translateY(-4px); }
.polaroid:focus-visible { outline: 2px solid var(--teal); outline-offset: 4px; }

/* full-bleed heading at the bottom of the card, black-on-cream (same
   display-type family, and same left:50%/translateX(-50%) centring
   technique, as the about section's heading — bleeds past the CARD's
   edges specifically, clipped by .contact__card's overflow:hidden) */
/* reference puts the heading in the card's bottom 30%, spanning the full card
   width. Anchored to the card's bottom edge (the card is now aspect-driven)
   and using the standalone `translate` property so a `transform` from any
   other rule can't replace the centring. */
.contact__heading {
  position: absolute;
  left: 50%;
  bottom: 0;
  width: max-content;
  translate: -50% 0;
  text-transform: uppercase;
  white-space: nowrap;
  /* longer copy than the about heading ("Have a project in mind?" vs
     "Meet Francesca") needs its own, smaller fluid size to bleed by a
     comparable proportion rather than swamping the card */
  font-size: clamp(2.2rem, 8.36vw, 9rem);
  font-weight: 400;   /* roman, per the reference */
  letter-spacing: -0.03em;
  line-height: 1;
  color: var(--black);
}

.contact__foot {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 1rem;
  padding: clamp(1.4rem, 3vw, 2.2rem) var(--edge);
}

.contact__copy,
.contact__top {
  font-size: var(--fs-meta);
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--cream-light);
  line-height: 1.4;
}
.contact__top { text-decoration: none; transition: opacity 0.2s ease; }
.contact__top:hover { opacity: 0.6; }


/* ==========================================================================
   GET-IN-TOUCH PANEL — slides up from the bottom (jazzicreates.tv)
   ========================================================================== */

.talk {
  position: fixed;
  inset: 0;
  z-index: 1300;
  background: rgba(19, 19, 20, 0);
  pointer-events: none;
  transition: background 0.5s ease;
}

.talk--open {
  pointer-events: auto;
  background: rgba(19, 19, 20, 0.4);
}

.talk__panel {
  position: fixed;
  inset: 1rem;
  background: var(--bg);
  border-radius: 16px;
  overflow: hidden;
  transform: translateY(110%);
  transition: transform 0.7s cubic-bezier(0.16, 1, 0.3, 1);
  box-shadow: 0 -30px 80px rgba(0, 0, 0, 0.28);
  will-change: transform;
}

.talk--open .talk__panel { transform: translateY(0); }

.talk__close {
  position: absolute;
  top: 1.4rem;
  right: 1.6rem;
  z-index: 2;
  background: none;
  border: none;
  color: var(--ink);
  font-size: 1.5rem;
  line-height: 1;
  cursor: pointer;
  padding: 0.4rem;
  transition: opacity 0.2s ease;
}
.talk__close:hover { opacity: 0.5; }

.talk__scroll {
  height: 100%;
  overflow-y: auto;
  padding: clamp(2rem, 5vw, 5rem);
}

.talk__title {
  font-size: clamp(2.5rem, 7vw, 7rem);
  font-weight: 700;
  letter-spacing: -0.03em;
  line-height: 0.95;
}

.talk__sub {
  margin-top: 0.8rem;
  color: var(--ink-soft);
  font-size: 1rem;
}

.talk__form {
  margin-top: 2.5rem;
  max-width: 640px;
  display: flex;
  flex-direction: column;
  gap: 1.6rem;
}

.talk__field { display: flex; flex-direction: column; gap: 0.5rem; }

.talk__field > span {
  font-size: 0.8rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--ink-soft);
}

.talk__field input,
.talk__field textarea {
  font-family: var(--font);
  font-size: 1.05rem;
  color: var(--ink);
  background: transparent;
  border: none;
  border-bottom: 1.5px solid var(--line);
  padding: 0.5rem 0;
  outline: none;
  resize: vertical;
  transition: border-color 0.2s ease;
}
.talk__field input:focus,
.talk__field textarea:focus { border-color: var(--ink); }
.talk__field input::placeholder,
.talk__field textarea::placeholder { color: var(--ink-soft); }

.talk__group { border: none; padding: 0; margin: 0; }
.talk__group legend {
  font-size: 0.8rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--ink-soft);
  margin-bottom: 0.9rem;
}

.talk__chk {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  margin: 0 1.4rem 0.8rem 0;
  font-size: 0.98rem;
  cursor: pointer;
}
.talk__chk input { accent-color: var(--teal); width: 1.05rem; height: 1.05rem; }

.talk__submit {
  align-self: flex-start;
  margin-top: 0.5rem;
  background: var(--cloud);
  color: var(--midnight);
  border: none;
  border-radius: 999px;
  font-family: var(--font);
  font-size: 1rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  padding: 0.9rem 2rem;
  cursor: pointer;
  transition: transform 0.2s ease, opacity 0.2s ease;
}
.talk__submit:hover { transform: translateY(-2px); opacity: 0.9; }

a.talk__submit { display: inline-block; text-decoration: none; margin-top: 2.5rem; }


/* ==========================================================================
   FOOTER
   ========================================================================== */

.footer {
  padding: 2rem var(--edge);
  border-top: 1px solid var(--line);
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  font-size: 0.82rem;
  font-weight: 500;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--ink-soft);
}

.footer a { text-decoration: none; }

.footer a:hover { opacity: 0.5; }


/* ==========================================================================
   RESPONSIVE — breakpoints at 1024 / 768 / 480 / 390 / 320.
   The fluid tokens in :root already carry most of the 1024→320 transition;
   ≥1024px nothing below touches layout, so desktop stays byte-identical.
   Structural changes (nav, works, services, studio, contact) land at
   ≤768, with ≤480 / ≤390 / ≤320 only tightening spacing and type further.
   ========================================================================== */

@media (max-width: 768px) {

  /* ---------- NAV — opaque bar + brand + hamburger → full drawer ---------- */
  .nav {
    justify-content: space-between;
    background: var(--bg);           /* opaque: never overlaps content */
  }
  .nav__links { display: none; }
  .nav__brand {
    display: block;
    font-size: 1.05rem;
    font-weight: 700;
    letter-spacing: 0.02em;
    color: var(--ink);
    text-decoration: none;
  }
  .nav__brand:focus-visible { outline: 2px solid var(--teal); outline-offset: 4px; border-radius: 4px; }

  .nav__burger {
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: 5px;
    width: 2.6rem;
    height: 2.6rem;
    background: none;
    border: none;
    padding: 0;
    cursor: pointer;
  }
  .nav__burger span {
    display: block;
    width: 100%;
    height: 2px;
    background: var(--ink);
    transition: transform 0.3s cubic-bezier(0.16, 1, 0.3, 1), opacity 0.2s ease;
  }
  .nav__burger--open span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
  .nav__burger--open span:nth-child(2) { opacity: 0; }
  .nav__burger--open span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }
  .nav__burger:focus-visible { outline: 2px solid var(--teal); outline-offset: 4px; }

  .nav__drawer {
    display: flex;
    position: fixed;
    inset: 0;
    z-index: 1250;
    background: var(--bg);
    align-items: center;
    justify-content: center;
    transform: translateY(-100%);
    transition: transform 0.45s cubic-bezier(0.16, 1, 0.3, 1);
    pointer-events: none;
  }
  .nav__drawer--open { transform: translateY(0); pointer-events: auto; }
  .nav__drawer-links { display: flex; flex-direction: column; gap: 2rem; text-align: center; }
  .nav__drawer-links a {
    font-size: 1.7rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.02em;
    color: var(--ink);
    text-decoration: none;
    padding: 0.4rem;
    transition: opacity 0.15s ease;
  }
  .nav__drawer-links a:hover { opacity: 0.55; }
  .nav__drawer-links a:focus-visible { outline: 2px solid var(--teal); outline-offset: 4px; border-radius: 6px; }
  .nav__drawer-links a.nav__pill--active { text-decoration: underline; }

  /* ---------- HERO — burger only (no brand word), logo near full width,
     tagline on one line right under it. Measured off
     assets/mockups/portfolio layout mobile.png (786x7580 = 393x3790 logical). */
  .nav__brand { display: none; }
  .nav { justify-content: flex-end; }
  .hero__logo { width: 84vw; max-width: none; }
  .hero__content { gap: 0.5rem; }
  .hero__line {
    font-size: 2.55vw;          /* one line at 393px, as in the reference */
    letter-spacing: 0.01em;
    white-space: nowrap;
    padding: 0;
  }

  /* ---------- WORKS — the reference keeps the scattered collage on mobile;
     it is NOT a stacked grid. Stage is near-square (0.9685) and every tile
     keeps its own position, measured off the mobile reference. ---------- */
  .works { padding: 0 var(--edge) var(--section-pad); }
  .works__stage {
    position: relative;
    display: block;
    width: 90vw;
    max-width: 90vw;
    margin: 0 auto;
    aspect-ratio: 0.9685;
    gap: 0;
  }
  .wplane { display: contents; }
  /* Desktop --x/--y/--w live in each tile's inline style attribute, which
     outranks any stylesheet rule, so mobile uses its OWN variables instead of
     trying to override them. */
  .wtile {
    position: absolute;
    left: var(--mx);
    top: var(--my);
    width: var(--mw);
    aspect-ratio: var(--ar);
    height: auto;
  }

  /* per-tile positions for mobile (% of the stage above) */
  .wtile[data-title="Péndulo Fitness Club"] { --mx: 33.95%; --my:  0.00%; --mw: 34.23%; }
  .wtile[data-title="Terinsa"]              { --mx: 73.27%; --my: 15.34%; --mw: 26.73%; }
  .wtile[data-title="DESAYUNOS"]            { --mx:  4.53%; --my: 18.08%; --mw: 24.47%; }
  .wtile[data-title="Nuna Glamping"]        { --mx:  0.00%; --my: 47.95%; --mw: 25.32%; }
  .wtile[data-title="3UP"]                  { --mx: 72.70%; --my: 51.78%; --mw: 26.17%; }
  .wtile[data-title="Aluna Skin"]           { --mx: 31.68%; --my: 64.38%; --mw: 33.52%; }

  .works__heading {
    position: absolute;
    left: 50%;
    top: 50%;
    translate: -50% -50%;
    display: block;
    order: 0;
    width: 88%;
    max-width: 88%;
    margin: 0;
    text-align: center;
    white-space: normal;        /* wraps to two lines, as in the reference */
    line-height: 0.92;
    font-size: 12.2vw;
  }

  .proj-cursor { display: none; }        /* no custom cursor on touch */

  /* ---------- PROJECT VIEW — stacked header, carousel fills the rest ---------- */
  .pview__back { top: calc(var(--nav-h) * 0.28); }
  .pview__head {
    flex-direction: column;
    align-items: flex-start;
    gap: 1.25rem;
    padding: calc(var(--nav-h) + 1.5rem) var(--edge) 1rem;
  }
  .pview__intro { max-width: 100%; }
  .pview__tag { font-size: 0.7rem; }
  .pview__desc { flex: 1 1 auto; max-width: 100%; font-size: 0.95rem; padding-top: 0; }
  .pview__track { gap: 4vw; padding: 0 var(--edge) 1.45vh; }
  .pview__item img { height: calc(var(--hf, 1) * 38vh); }

  /* ---------- SERVICES — the sticky stack is KEPT on mobile (same animation
     as desktop). Only the internals change: the desktop card is a fixed-ratio
     container-query box with every child absolutely positioned in cqw/cqh,
     none of which survives a narrow viewport, so content returns to normal
     flow and the type is re-scaled in rem. ---------- */
  .services { height: auto; padding-top: 0; }
  .services__wrap { display: contents; }   /* unchanged from desktop: panels stick within .services */

  .services__panel {
    /* sticky retained — only the box model is re-expressed */
    top: calc(var(--nav-h) + 0.5rem + var(--stack-i, 0) * 0.5rem);
    margin: 0;
    width: auto;
    max-width: none;
    aspect-ratio: auto;
    container-type: normal;    /* kills the cq units the children resolve against */
    min-height: 0;
    border-radius: 16px;
    padding: 1.6rem var(--edge) 1.7rem;
    transform: none !important;
    filter: none !important;
    opacity: 1 !important;
  }
  .services__panel:last-of-type { margin-bottom: 0; }

  .services__inner,
  .services__copy {
    position: static;
    inset: auto;
    transform: none !important;
  }

  .services__heading {
    position: static;
    white-space: normal;
    font-size: clamp(1.5rem, 7.6vw, 2.3rem);
    letter-spacing: -0.01em;
    margin: 0 0 0.65rem;
  }
  .services__body {
    position: static;
    width: 100%;
    font-size: 0.85rem;
    line-height: 1.38;
    margin: 0 0 0.95rem;
  }
  .services__tags {
    position: static;
    width: 100%;
    gap: 0.38rem;
    margin: 0 0 1.05rem;
  }
  .services__tags span {
    font-size: 0.62rem;
    padding: 0.5em 0.9em;
    white-space: normal;
  }
  .services__media {
    position: static;
    width: 100%;
    height: auto;
    aspect-ratio: 2367 / 1497;
    margin: 0;
  }

  /* ---------- STUDIO — the reference keeps a two-column composition: heading
     full width, (RÉSUMÉ) right-aligned under it, a narrow intro column, then
     the body text at left with the portrait beside it on the right.
     All values measured off the mobile reference. ---------- */
  .studio {
    height: auto;
    overflow: visible;
    /* no bottom padding: the portrait's bottom edge rests on the first
       services panel, as in the reference */
    padding: calc(var(--nav-h) + 1rem) 0 0;
  }
  .studio__stage {
    position: relative;
    aspect-ratio: auto;
    display: block;
    width: 100%;
    max-width: none;
  }

  .studio__head {
    position: static;
    display: block;
    width: 100%;
    margin: 0 0 0.5rem;
    padding: 0 5.9vw;
  }
  .studio__heading {
    position: static;
    left: auto; top: auto;
    width: auto;
    transform: none;
    word-spacing: normal;       /* desktop opens the words up to clear the portrait */
    white-space: nowrap;        /* one line, 6.5%..93.3% in the reference */
    text-align: left;
    line-height: 1;
    /* solved so the rendered text spans 86.8vw, the reference's 6.5%..93.3%.
       nowrap text overflows its box rather than widening it, so this has to be
       measured from the glyphs, not from the element's bounding box. */
    font-size: 10.46vw;
    margin: 0;
  }
  /* intro sits in a narrow column at the left */
  .studio__intro {
    position: static;
    left: auto; top: auto;
    width: 56%;
    max-width: 56%;
    margin: 0.7rem 0 0;
    padding: 0 0 0 5.9vw;
    font-weight: 500;
    font-size: 0.82rem;
    line-height: 1.32;
  }
  .studio__intro p { margin: 0 0 0.7rem; }
  .studio__intro p:last-child { margin-bottom: 0; }

  /* body column on the left, portrait beside it on the right */
  .studio__body {
    position: static;
    left: auto; top: auto;
    width: 44%;
    max-width: 44%;
    margin: 1.1rem 0 0;
    padding: 0 0 0 5.9vw;
    font-weight: 400;
    font-size: 0.76rem;
    line-height: 1.36;
  }
  .studio__body p { margin: 0 0 0.75rem; }
  .studio__body p:last-child { margin-bottom: 0; }

  .studio__portrait {
    position: absolute;
    left: 44.9%;
    right: auto;
    top: auto;
    bottom: 0;
    width: 48.6%;
    aspect-ratio: auto;
    margin: 0;
  }
  .studio__portrait img { width: 100%; height: auto; }

  /* ---------- CONTACT — polaroids keep their scattered arrangement, placed
     as % of a stage inside the card, same technique as desktop ---------- */
  .contact { padding: calc(var(--nav-h) + 1rem) 0 0; }
  .contact__card {
    width: 93vw;
    max-width: 93vw;
    aspect-ratio: auto;
    border-radius: 16px;
    padding: 0 0 1.6rem;
  }
  .polaroid-stage {
    position: relative;
    left: auto; top: auto;
    display: block;
    width: 88%;
    max-width: 88%;
    margin: 2.2rem auto 0;
    aspect-ratio: 0.892;
  }
  .polaroid,
  .polaroid:hover {
    position: absolute;
    left: var(--mx);
    top: var(--my);
    width: var(--mw);
    aspect-ratio: var(--ar);
    transform: none;
  }
  .polaroid--linkedin  { --mx: 10.67%; --my: 12.50%; --mw: 23.26%; }
  .polaroid--email     { --mx: 62.21%; --my: 12.61%; --mw: 27.38%; }
  .polaroid--talk      { --mx: 37.79%; --my: 35.09%; --mw: 26.22%; }
  .polaroid--spotify   { --mx: 62.85%; --my: 59.63%; --mw: 23.52%; }
  .polaroid--instagram { --mx: 13.88%; --my: 60.32%; --mw: 22.37%; }

  .contact__heading {
    position: static;
    width: auto;
    translate: none;
    white-space: normal;
    text-align: left;
    font-size: 11.6vw;
    line-height: 0.95;
    margin: 1.6rem 0 0;
    padding: 0 5vw;
  }

  .contact__foot {
    flex-wrap: nowrap;
    justify-content: space-between;
    gap: 1rem;
    padding-bottom: clamp(1.4rem, 5vw, 2.2rem);
  }
  .contact__top { text-align: right; }

  /* ---------- PROJECT VIEW on mobile ---------- */
  /* the nav is an opaque bar here and sits above the overlay (z 1100 vs 1000),
     so BACK has to clear it instead of hiding underneath */
  .pview__back { top: calc(var(--nav-h) + 0.5rem); }
  /* the desktop head is an absolutely-placed two-column layout in vw/vh; on a
     phone it goes back to a single stacked column above the carousel */
  .pview__head {
    bottom: 46vh;            /* clears the carousel */
    display: flex;
    flex-direction: column;
    gap: 1.1rem;
    padding: calc(var(--nav-h) + 2.6rem) var(--edge) 1rem;
    /* longer case-study copy would otherwise be clipped above the carousel */
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
  }
  .pview__head::-webkit-scrollbar { display: none; }
  .pview__intro,
  .pview__desc {
    position: static;
    left: auto; top: auto;
    width: 100%;
  }
  .pview__desc { font-size: 0.92rem; line-height: 1.4; }
  .pview__desc p { margin-bottom: 0.7rem; }
  .pview__tag { font-size: 0.68rem; padding: 0.4em 1em; }
  .pview__tags { row-gap: 0.4rem; }
  .pview__tag-row { column-gap: 0.4rem; }
  .pview__meta { margin-top: 0.9rem; gap: 0.35rem; }
  .pview__meta-label { font-size: 0.72rem; width: 7.5rem; }
  .pview__meta-value { font-size: 0.72rem; }
  .pview__tags { row-gap: 0.75rem; }
  .pview__tag-row { column-gap: 1.4rem; }
  .pview__tag { font-size: 0.72rem; }
  /* a 52vh carousel leaves roughly half the screen empty at this size */
  .pview__scroll { height: 46vh; }
  .pview__item img { height: calc(var(--hf, 1) * 44vh); }

  /* the mobile nav bar is opaque, so over the cloud project view it needs to
     flip with the links rather than staying a black band on a cream page */
  .nav--onlight { background: var(--cloud); }
  .nav--onlight .nav__brand { color: var(--midnight); }
  .nav--onlight .nav__burger span { background: var(--midnight); }

}   /* end @media (max-width: 768px) */

@media (max-width: 480px) {
  /* NOTE: the portrait, polaroid and works-grid overrides that used to live
     here were left over from the stacked mobile layout. They outranked the
     768px rules (same specificity, later in the file) and pinned the portrait
     to 220px and every polaroid to 132px, breaking the reference composition. */
  .pview__item img { height: calc(var(--hf, 1) * 34vh); }
  .nav__drawer-links a { font-size: 1.4rem; }
  .talk__panel { inset: 0.5rem; }
}

@media (max-width: 390px) {
  .hero__logo { width: 86vw; }
}

@media (max-width: 320px) {
  .pview__item img { height: calc(var(--hf, 1) * 30vh); }
}
