/* ============================================================
   DOH Studio — Portfolio  ·  v0.3  (editorial / boutique / light)
   Near-white paper + near-black ink. Plus Jakarta Sans.
   Reference language: Julien Pianetti + Uncommon.
   ============================================================ */

:root{
  --paper:   #f7f6f2;
  --ink:     #0c0c0c;
  --ink-70:  rgba(12,12,12,.7);
  --ink-45:  rgba(12,12,12,.46);
  --ink-30:  rgba(12,12,12,.3);
  --line:    rgba(12,12,12,.13);
  --line-soft: rgba(12,12,12,.08);
  --pad: clamp(20px, 5vw, 92px);
  --ease: cubic-bezier(.16,1,.3,1);
  --r: 3px;
  --font: "Plus Jakarta Sans", system-ui, sans-serif;
}

*{ box-sizing:border-box; margin:0; padding:0; }
html{ scroll-behavior:smooth; -webkit-font-smoothing:antialiased; text-rendering:optimizeLegibility; }
body{ font-family:var(--font); background:var(--paper); color:var(--ink); line-height:1.42; overflow-x:hidden; }
a{ color:inherit; text-decoration:none; }
img{ display:block; max-width:100%; }
::selection{ background:var(--ink); color:var(--paper); }

.grain{
  position:fixed; inset:0; z-index:70; pointer-events:none; opacity:.025;
  background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='120' height='120'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2'/></filter><rect width='100%25' height='100%25' filter='url(%23n)'/></svg>");
}

/* ---------- ambient satin sheen (pink / blue / green) ----------
   Three large blurred pastel circles drifting behind the whole page.
   Position/drift is pure, always-on CSS (unrelated to interaction). But
   OPACITY (how present the whole layer feels) is controlled at the
   CONTAINER level by exactly one mechanism per platform, not layered:
     - Desktop (hover:hover + pointer:fine): script.js binds it to real-time
       mouse velocity via --sheen-live, growing significantly on movement
       and decaying smoothly back to baseline when the cursor is still.
     - Touch / no-hover: no mouse to bind to, so a plain CSS @keyframes
       breathing loop (6-8s) pulses it up and down on its own instead.
   mix-blend-mode:multiply keeps it satin rather than a flat color wash. */
.sheen{
  position:fixed; inset:0; z-index:1; pointer-events:none; overflow:hidden;
  mix-blend-mode:multiply;
  opacity:var(--sheen-live, .035);
}
.sheen__blob{
  position:absolute; width:62vmax; height:62vmax; border-radius:50%;
  filter:blur(120px); will-change:transform;
}
.sheen__blob--pink{
  top:-14%; left:-16%; background:radial-gradient(circle, #f0b7d4, transparent 68%);
  animation: sheenDrift1 48s ease-in-out infinite;
}
.sheen__blob--blue{
  top:14%; right:-20%; background:radial-gradient(circle, #b7d6f0, transparent 68%);
  animation: sheenDrift2 56s ease-in-out infinite;
}
.sheen__blob--green{
  bottom:-18%; left:18%; background:radial-gradient(circle, #bceac8, transparent 68%);
  animation: sheenDrift3 63s ease-in-out infinite;
}

@keyframes sheenDrift1{
  0%,100%{ transform:translate(0,0) scale(1); }
  33%{ transform:translate(9vw,6vh) scale(1.12); }
  66%{ transform:translate(-5vw,11vh) scale(.92); }
}
@keyframes sheenDrift2{
  0%,100%{ transform:translate(0,0) scale(1); }
  40%{ transform:translate(-11vw,5vh) scale(1.08); }
  75%{ transform:translate(-3vw,-9vh) scale(.95); }
}
@keyframes sheenDrift3{
  0%,100%{ transform:translate(0,0) scale(1); }
  30%{ transform:translate(6vw,-10vh) scale(1.1); }
  70%{ transform:translate(11vw,3vh) scale(.9); }
}

/* mobile / no-hover: no mouse to drive it, so breathe on its own instead */
@media (hover:none){
  .sheen{ animation:sheenBreathe 7s ease-in-out infinite; }
  @keyframes sheenBreathe{ 0%,100%{ opacity:.035; } 50%{ opacity:.15; } }
}

/* real content must stack ABOVE the fixed sheen layer: without this, every
   static-flow section would paint BEHIND a position:fixed element regardless
   of DOM order, and the sheen (and its dark footer) would misrender. */
main, .foot{ position:relative; z-index:2; }

/* ---------- floating cursor image (text index) ---------- */
.cursor-media{
  position:fixed; top:0; left:0; z-index:30; pointer-events:none;
  width:clamp(200px, 21vw, 320px); aspect-ratio:4/5;
  transform:translate3d(-50vw,-50vh,0); will-change:transform;
  transition:aspect-ratio .35s var(--ease);
}
/* format variety per hovered project, so the preview box doesn't read as
   one fixed portrait shape every time (mirrors the carousel treatment) */
.cursor-media--square{ aspect-ratio:1/1; }
.cursor-media--wide{ aspect-ratio:3/2; }
.cursor-media__inner{
  width:100%; height:100%; overflow:hidden; border-radius:var(--r);
  opacity:0; transform:scale(.92);
  transition:opacity .5s var(--ease), transform .6s var(--ease);
}
.cursor-media.is-active .cursor-media__inner{ opacity:1; transform:scale(1); }
.cursor-media img{ width:100%; height:100%; object-fit:cover; }
.cursor-media__cap{
  display:block; margin-top:10px; font-size:13px; font-weight:600; color:var(--ink);
  opacity:0; transform:translateY(4px); transition:opacity .4s var(--ease), transform .4s var(--ease);
}
.cursor-media.is-active .cursor-media__cap{ opacity:1; transform:none; }

/* ---------- global invert cursor: mix-blend-mode:difference ----------
   Position + scale are both set via one JS-composed transform per frame
   (see script.js) so CSS never fights inline styles over the same property.
   The circle stays difference-blended in EVERY state (idle, hover, viewing)
   so it keeps inverting whatever image or color sits beneath it, even while
   expanded over Selected Work / the carousel. The "View" label is a plain
   (non-blended) child painted on top, so it stays a stable, predictable
   mark rather than compounding a second blend pass. */
.invert-dot{
  position:fixed; top:0; left:0; z-index:65; pointer-events:none;
  width:44px; height:44px; margin:-22px 0 0 -22px; border-radius:50%;
  display:flex; align-items:center; justify-content:center;
  background:#fff; mix-blend-mode:difference;
  transform:translate3d(-50vw,-50vh,0) scale(0);
  will-change:transform;
}
.invert-dot__label{
  opacity:0; font-size:10px; font-weight:600; letter-spacing:.04em; text-transform:uppercase;
  color:#000; white-space:nowrap; transition:opacity .3s var(--ease);
}
.invert-dot.is-viewing .invert-dot__label{ opacity:1; }

/* ---------- NAV: mix-blend-mode:difference so it reads over any content ---------- */
.nav{
  position:fixed; top:0; left:0; right:0; z-index:50;
  display:grid; grid-template-columns:1fr auto 1fr; align-items:center;
  height:66px; padding:0 var(--pad);
  color:#fff; mix-blend-mode:difference;
}
.nav__brand{ justify-self:start; font-weight:700; font-size:15.5px; letter-spacing:-.01em; }
.nav__links{ justify-self:center; display:flex; align-items:center; gap:6px; font-size:14.5px; font-weight:500; }
.nav__links span{ opacity:.45; margin-right:2px; }
.nav__links a{ opacity:.75; transition:opacity .3s var(--ease); }
.nav__links a:hover{ opacity:1; }
.lang{ justify-self:end; display:flex; align-items:center; gap:5px; background:none; border:none; color:inherit; font:inherit; font-size:13px; cursor:pointer; }
.lang__opt{ opacity:.4; transition:opacity .3s var(--ease); }
.lang__opt.is-active{ opacity:1; }
.lang__sep{ opacity:.4; }

/* ---------- shared section head ---------- */
.section-head{ display:flex; align-items:baseline; justify-content:space-between; gap:16px;
  margin-bottom:clamp(30px,4vw,54px); }
.section-head__title{ font-size:clamp(22px,2.3vw,32px); font-weight:600; letter-spacing:-.02em; }
.section-head__idx{ font-size:13px; color:var(--ink-45); font-variant-numeric:tabular-nums; }

/* ---------- HERO ---------- */
.hero{ min-height:100dvh; padding:120px var(--pad) 46px; display:flex; flex-direction:column; justify-content:space-between; }
.hero__top{ display:flex; gap:24px; font-size:12.5px; letter-spacing:.05em; text-transform:uppercase; color:var(--ink-45); font-weight:600; }
.hero__title{ font-weight:700; letter-spacing:-.045em; line-height:.92; font-size:clamp(46px, 11.5vw, 168px); }
.hero__title span{ display:block; }
.hero__foot{ display:flex; align-items:flex-end; justify-content:space-between; flex-wrap:wrap; gap:28px;
  padding-top:16px; }
.hero__cue{ font-size:15px; font-weight:600; display:inline-flex; align-items:center; gap:9px; flex-shrink:0; }
.hero__cue::after{ content:"↓"; font-weight:400; transition:transform .35s var(--ease); }
.hero__cue:hover::after{ transform:translateY(3px); }
.hero__lede{ max-width:52ch; font-size:clamp(15px,1.55vw,19px); color:var(--ink-70); line-height:1.45; }

/* ---------- FEATURED ---------- */
.featured{ padding:clamp(40px,5vw,70px) var(--pad) clamp(64px,8vw,112px); }
.feat{ display:grid; grid-template-columns:repeat(3,1fr); gap:clamp(16px,2vw,30px); }
.feat__media{ overflow:hidden; border-radius:var(--r); aspect-ratio:4/5; background:var(--line-soft); }
.feat__media img{ width:100%; height:100%; object-fit:cover; transform:scale(1.001);
  transition:transform .7s var(--ease), filter .5s var(--ease); }
.feat__item:hover .feat__media img{ transform:scale(1.055); }
.feat__cap{ display:grid; grid-template-columns:auto 1fr auto; align-items:baseline; gap:12px; margin-top:16px; }
.feat__n{ font-size:13px; color:var(--ink-45); font-variant-numeric:tabular-nums; }
.feat__name{ font-size:clamp(18px,1.9vw,24px); font-weight:600; letter-spacing:-.02em; }
.feat__tag{ font-size:13.5px; color:var(--ink-45); text-align:right; }

/* ---------- STUDIO (Uncommon-style) ---------- */
.studio{ padding:clamp(58px,7.5vw,112px) var(--pad) clamp(70px,9vw,130px); }
.studio__head{ display:grid; grid-template-columns:1.4fr 1fr; gap:clamp(28px,5vw,80px); align-items:start; }
.studio__title{ font-size:clamp(30px,4.6vw,66px); font-weight:600; letter-spacing:-.035em; line-height:1.02; max-width:14ch; }
.studio__note{ font-size:clamp(15px,1.5vw,18px); color:var(--ink-70); line-height:1.5; max-width:46ch; align-self:end; }

.studio__gridlabel{ margin-top:clamp(56px,7vw,96px); margin-bottom:26px; font-size:12.5px;
  text-transform:uppercase; letter-spacing:.05em; color:var(--ink-45); }
.pillars{ display:grid; grid-template-columns:repeat(4,1fr); }
.pillar{ padding:26px clamp(18px,2vw,30px) 30px 0; border-top:1px solid var(--ink); }
.pillar + .pillar{ padding-left:clamp(18px,2vw,30px); border-left:1px solid var(--line); }
.pillar__n{ display:block; font-size:12.5px; color:var(--ink-45); }
.pillar__gfx{ display:block; margin:30px 0 34px; color:var(--ink); }
.pillar__gfx svg{ width:clamp(64px,6vw,88px); height:clamp(64px,6vw,88px); }
.pillar__title{ font-size:clamp(17px,1.7vw,21px); font-weight:600; letter-spacing:-.02em; }
.pillar__desc{ margin-top:12px; font-size:14px; color:var(--ink-45); max-width:26ch; line-height:1.45; }

/* -- icon motion: shared, reusable keyframes, staggered per shape (minimal, continuous, loops) -- */
.pillar__gfx svg{ overflow:visible; }
.pillar__gfx svg *{ transform-box:fill-box; transform-origin:center; }
@keyframes gfxPulse{ 0%,100%{ transform:scale(1); } 50%{ transform:scale(1.14); } }
@keyframes gfxPulseSoft{ 0%,100%{ transform:scale(1); opacity:1; } 50%{ transform:scale(.8); opacity:.55; } }
@keyframes gfxSpin{ to{ transform:rotate(360deg); } }
@keyframes gfxSpinRev{ to{ transform:rotate(-360deg); } }
@keyframes gfxDash{ to{ stroke-dashoffset:-20; } }
@keyframes gfxDriftL{ 0%,100%{ transform:translateX(0); } 50%{ transform:translateX(-4px); } }
@keyframes gfxDriftR{ 0%,100%{ transform:translateX(0); } 50%{ transform:translateX(4px); } }

/* icon 1 · Brand Foundations: four viewfinder brackets breathing inward
   and outward as they lock onto a fixed core, like a lens finding focus,
   insight resolving into a solid foundational point. */
@keyframes gfxFocusTL{ 0%,100%{ transform:translate(0,0); } 50%{ transform:translate(3px,3px); } }
@keyframes gfxFocusTR{ 0%,100%{ transform:translate(0,0); } 50%{ transform:translate(-3px,3px); } }
@keyframes gfxFocusBR{ 0%,100%{ transform:translate(0,0); } 50%{ transform:translate(-3px,-3px); } }
@keyframes gfxFocusBL{ 0%,100%{ transform:translate(0,0); } 50%{ transform:translate(3px,-3px); } }
.gfx-bracket-tl{ animation:gfxFocusTL 3.2s ease-in-out infinite; }
.gfx-bracket-tr{ animation:gfxFocusTR 3.2s ease-in-out infinite; }
.gfx-bracket-br{ animation:gfxFocusBR 3.2s ease-in-out infinite; }
.gfx-bracket-bl{ animation:gfxFocusBL 3.2s ease-in-out infinite; }
.gfx-core{ animation:gfxPulse 3.2s ease-in-out infinite; }

/* icon 2 · Design Systems: two circles breathing apart and together,
   a simple Venn for shared, modular systems. */
.gfx-venn-a{ animation:gfxDriftL 4.2s ease-in-out infinite; }
.gfx-venn-b{ animation:gfxDriftR 4.2s ease-in-out infinite; }

/* icon 3 · Content & Digital: a hub distributing to four channels,
   connectors flowing outward, nodes pulsing like live activity. */
.gfx-link{ stroke-dasharray:5 4; animation:gfxDash 2.6s linear infinite; }
.gfx-node:nth-of-type(1){ animation:gfxPulse 2.6s ease-in-out infinite; }
.gfx-node:nth-of-type(2){ animation:gfxPulse 2.6s ease-in-out infinite -0.65s; }
.gfx-node:nth-of-type(3){ animation:gfxPulse 2.6s ease-in-out infinite -1.3s; }
.gfx-node:nth-of-type(4){ animation:gfxPulse 2.6s ease-in-out infinite -1.95s; }

/* icon 4 · Experience & Consulting: three open coil rings turning at
   different speeds, a continuous partnership always in motion. */
.gfx-coil1{ animation:gfxSpin 9s linear infinite; }
.gfx-coil2{ animation:gfxSpinRev 14s linear infinite; }
.gfx-coil3{ animation:gfxSpin 20s linear infinite; }

/* ---------- MORE WORK: forced 2-3-per-line flow + carousel ---------- */
.more{ padding:clamp(58px,7.5vw,104px) var(--pad) clamp(50px,6vw,80px); }
.flow{ text-align:left; font-size:clamp(26px,4.4vw,58px); font-weight:600; letter-spacing:-.03em; }
/* each line is its own block so it always breaks here, independent of viewport width */
.flow__line{ display:flex; flex-wrap:wrap; align-items:baseline; line-height:1.28; }
/* two groupings of the same 15 links: .flow--desktop is the wide-row
   grouping tuned for desktop width; .flow--mobile regroups the same
   names 2-per-line (never 3) so phone-width wrapping never stalls one
   name alone mid-list. Toggled in the mobile media query below. */
.flow--mobile{ display:none; }
.flow__item{ opacity:1; transition:opacity .4s var(--ease); }
.flow:hover .flow__item{ opacity:.3; }
.flow:hover .flow__item:hover{ opacity:1; }
/* solid round separator dot, not a glyph, so it reads consistently across fonts */
.flow__sep{
  display:inline-block; width:.16em; height:.16em; margin:0 .38em; align-self:center;
  border-radius:50%; background:var(--ink); flex:0 0 auto;
  transition:opacity .4s var(--ease);
}
.flow:hover .flow__sep{ opacity:.3; }

.carousel{ margin-top:clamp(44px,6vw,72px); overflow:hidden; }
.carousel__track{ display:inline-flex; align-items:flex-start; gap:clamp(14px,1.6vw,24px); animation:marquee 55s linear infinite; }
.carousel__link{ display:block; flex:0 0 auto; width:clamp(240px,26vw,380px); }
.carousel__link figure > img{ width:100%; aspect-ratio:3/2; object-fit:cover; border-radius:var(--r); background:var(--line-soft); }
.carousel__link figcaption{ margin-top:9px; font-size:12.5px; color:var(--ink-45); }
/* format variety so the strip doesn't read as one uniform row of horizontals */
.carousel__link--portrait figure > img{ aspect-ratio:4/5; }
.carousel__link--square figure > img{ aspect-ratio:1/1; }
@keyframes marquee{ from{ transform:translateX(0); } to{ transform:translateX(-50%); } }

/* ---------- CONTACT (left-aligned) ---------- */
.contact{ padding:clamp(72px,10vw,160px) var(--pad) clamp(90px,13vw,190px); }
.contact__eyebrow{ font-size:clamp(15px,1.7vw,20px); color:var(--ink-45); margin-bottom:26px; }
/* font-size is tied directly to the real available container width (viewport
   minus both --pad insets) divided by an empirical per-character advance for
   "hello.dohstudio@gmail.com" (26 chars), so it can never overflow to a
   second line at any breakpoint, no matter how the email string changes. */
.contact__mail{ display:inline-block; font-size:clamp(15px, calc((100vw - 2 * var(--pad)) / 17), 104px);
  font-weight:600; letter-spacing:-.045em; line-height:1;
  white-space:nowrap; transition:opacity .3s var(--ease); }
.contact__mail:hover{ opacity:.55; }
.contact__row{ margin-top:38px; display:flex; gap:26px; flex-wrap:wrap; font-size:14.5px; color:var(--ink-45); }

/* ---------- FOUNDER (closing stinger) ----------
   Text stays left, photo lives far right and starts fully hidden. Hovering
   the NAME specifically (not the whole text block) reveals it. Desktop
   only: the photo is dropped entirely on touch, since a :hover trigger
   can't be sustained there and would just leave dead space. */
.founder{
  padding:clamp(36px,5vw,56px) var(--pad) clamp(56px,7vw,96px);
  display:flex; align-items:center; justify-content:space-between; gap:32px;
}
.founder__label{ font-size:12.5px; text-transform:uppercase; letter-spacing:.05em; color:var(--ink-45); }
.founder__name{ margin-top:6px; font-size:clamp(24px,3vw,40px); font-weight:600; letter-spacing:-.03em; width:fit-content; }
.founder__desc{ margin-top:14px; font-size:clamp(14.5px,1.4vw,17px); color:var(--ink-70); max-width:46ch; }

.founder__photo{
  flex:0 0 auto; width:clamp(150px,15vw,220px); aspect-ratio:4/5;
  overflow:hidden; border-radius:var(--r);
  opacity:0; transition:opacity .4s ease-in-out;
}
.founder__photo img{ width:100%; height:100%; object-fit:cover; }
.founder:has(.founder__name:hover) .founder__photo{ opacity:1; }

@media (max-width:768px), (hover:none){
  .founder__photo{ display:none; }
}

/* ---------- FOOTER: bold closing device (deliberate dark theme flip) ---------- */
.foot{ background:var(--ink); color:rgba(247,246,242,.72); padding:clamp(40px,5vw,64px) var(--pad) clamp(28px,3vw,40px); }
.foot__top{ display:grid; grid-template-columns:1.6fr 1fr 1fr; gap:clamp(28px,4vw,60px); }
.foot__blurb{ font-size:clamp(15px,1.5vw,18px); line-height:1.5; max-width:38ch; color:rgba(247,246,242,.85); }
.foot__col{ display:flex; flex-direction:column; gap:9px; font-size:14.5px; }
.foot__col a{ opacity:.78; transition:opacity .3s var(--ease); width:fit-content; }
.foot__col a:hover{ opacity:1; }
.foot__colhead{ font-size:10px; text-transform:uppercase; letter-spacing:.05em; color:rgba(247,246,242,.4); margin-bottom:2px; }

/* brutalist crop: logo anchored hard-left. The wordmark-wrap uses the
   standard container-agnostic full-bleed technique (width:100vw +
   margin-left:50% + translateX(-50%)), which always spans exactly the true
   viewport regardless of the parent's padding. The SVG's own viewBox was
   re-measured with getBBox() to tightly frame the real artwork (1022x544,
   ratio ~1.88:1) after an earlier dedup pass left a huge empty strip in it.
   Copyright now sits in normal flow ABOVE the wordmark (not overlapping
   it), so the logo is free to use the full width without colliding with
   any text near its bottom-left, where the cropped circle is still visible. */
.foot__wordmark-wrap{
  overflow:hidden;
  width:100vw; margin-left:50%; transform:translateX(-50%);
  padding-top:clamp(20px,3vw,32px);
}
.foot__wordmark{
  display:block; width:74vw; max-width:none; height:auto; object-fit:contain;
  transform:translateX(-18vw);
}

.foot__bottom{
  display:flex; flex-direction:column; gap:6px;
  margin-top:clamp(28px,4vw,44px);
  font-size:13px; color:rgba(247,246,242,.45); text-align:left;
}

/* ---------- CASE STUDY (project pages) ----------
   Reference language: julienpianetti.com/works/pullin — big title, a short
   meta row, ONE concept paragraph, then a scroll-down sequence of full-bleed
   and paired images. "See other works" / Contact / Founder / Footer below
   all reuse the exact homepage sections (.more/.flow, .contact, .founder,
   .foot) verbatim for 1:1 visual consistency — no new CSS needed for those. */
.case-hero{ padding:130px var(--pad) 56px; display:flex; flex-direction:column; gap:26px; }
.case-hero__back{
  display:inline-flex; align-items:center; gap:8px; width:fit-content;
  font-size:12.5px; font-weight:600; text-transform:uppercase; letter-spacing:.05em;
  color:var(--ink-45); transition:color .3s var(--ease);
}
.case-hero__back::before{ content:"←"; transition:transform .3s var(--ease); }
.case-hero__back:hover{ color:var(--ink); }
.case-hero__back:hover::before{ transform:translateX(-3px); }

.case-hero__title{
  font-weight:700; letter-spacing:-.04em; line-height:.96; max-width:18ch;
  font-size:clamp(40px,7.4vw,118px);
}

.case-hero__meta{ display:flex; gap:clamp(24px,4vw,56px); flex-wrap:wrap; }
.case-hero__meta-item{ display:flex; flex-direction:column; gap:6px; }
.case-hero__meta-label{ font-size:11.5px; font-weight:600; text-transform:uppercase; letter-spacing:.05em; color:var(--ink-45); }
.case-hero__meta-value{ font-size:14.5px; font-weight:600; }

.case-hero__lede{ max-width:62ch; font-size:clamp(15.5px,1.6vw,19px); color:var(--ink-70); line-height:1.5; }

/* full-bleed frame: same edge-to-edge technique already used for the footer
   wordmark (width:100vw + margin-left:50% + translateX(-50%)). This is the
   ONE true edge-to-edge moment on a case-study page — the opening cinematic
   beat right after the hero info, before the page settles into the padded
   editorial grid below. */
.case-hero-media{
  width:100vw; margin-left:50%; transform:translateX(-50%);
  height:100dvh; overflow:hidden; background:var(--line-soft);
}
.case-hero-media img, .case-hero-media video{ width:100%; height:100%; object-fit:cover; display:block; }

/* project description: one offset, typography-led statement — an empty
   left column (matching the .studio__head 2-col idiom used elsewhere) lets
   the paragraph start mid-page instead of running full width, so it reads
   as a considered editorial beat rather than body copy. */
.case-desc{
  padding:clamp(70px,10vw,140px) var(--pad);
  display:grid; grid-template-columns:1fr 1fr; gap:clamp(24px,4vw,60px);
}
.case-desc__text{
  grid-column:2/3; max-width:52ch;
  font-size:clamp(22px,3vw,36px); font-weight:600; letter-spacing:-.02em; line-height:1.3;
}

/* dynamic media grid: a flat list of frames, no manual full/pair markup.
   nth-child(3n+1) is the ONLY thing deciding layout, so it repeats the same
   1-full / 2-half rhythm forever no matter how many images the project has:
   item 1 spans both columns (wide/cinematic), items 2-3 split the row
   (portrait), item 4 spans again, and so on. */
.case-grid{
  display:grid; grid-template-columns:1fr 1fr; gap:clamp(14px,1.6vw,24px);
  padding:clamp(60px,8vw,100px) var(--pad);
}
.case-grid__item{ overflow:hidden; border-radius:var(--r); background:var(--line-soft); aspect-ratio:4/5; }
.case-grid__item img, .case-grid__item video{ width:100%; height:100%; object-fit:cover; display:block; }
.case-grid__item:nth-child(3n+1){ grid-column:1/-1; aspect-ratio:16/9; }

/* -- device mockup (web/app case studies): a portrait slot becomes a dark
   backdrop with a real phone-framed screenshot centered in it, instead of
   a plain photo fill. Use on a "half" grid item when the project's actual
   deliverable was a website/app and the honest content is a UI screenshot,
   not photography. */
.case-grid__item--mockup{
  display:flex; align-items:center; justify-content:center;
  background:var(--ink); padding:clamp(28px,5vw,64px);
}
.mockup-phone{
  width:clamp(150px,44%,210px); aspect-ratio:390/844; flex:0 0 auto;
  border-radius:34px; padding:9px; background:#050505; border:1px solid rgba(255,255,255,.08);
}
.mockup-phone__screen{ width:100%; height:100%; border-radius:24px; overflow:hidden; background:var(--line-soft); }
.mockup-phone__screen img{ width:100%; height:100%; object-fit:cover; object-position:top; display:block; }

/* ---------- SEE OTHER WORKS (label left, cards fill the rest, case-study pages) ----------
   Reference: julienpianetti.com/works/pullin — "See other works" sits in its
   own narrow column on the left; the projects fill ALL remaining width to
   the right as one even row (not shrunk down with dead space beside them). */
.other-works{
  padding:clamp(58px,7.5vw,104px) var(--pad) clamp(70px,9vw,120px);
  display:flex; align-items:flex-start; gap:clamp(24px,4vw,64px);
}
.other-works__head{ flex:0 0 auto; width:clamp(160px,20vw,240px); }
.other-works__grid{
  display:grid; grid-template-columns:repeat(2,1fr); gap:clamp(16px,2vw,28px);
  flex:1 1 auto; min-width:0;
}
.other-works__thumb{ overflow:hidden; border-radius:var(--r); aspect-ratio:4/5; background:var(--line-soft); }
.other-works__thumb img{ width:100%; height:100%; object-fit:cover; transform:scale(1.001);
  transition:transform .7s var(--ease); }
.other-works__card:hover .other-works__thumb img{ transform:scale(1.045); }
.other-works__name{ margin-top:16px; font-size:clamp(17px,1.8vw,22px); font-weight:600; letter-spacing:-.02em; }
.other-works__cat{ margin-top:4px; font-size:13.5px; color:var(--ink-45); }

/* ---------- reveal motion (JS only) ---------- */
html.js .reveal{ opacity:0; transform:translateY(22px); transition:opacity .8s var(--ease), transform .8s var(--ease); }
html.js .reveal.in{ opacity:1; transform:none; }

/* ---------- responsive ---------- */
@media (max-width:1024px){
  .studio__head{ grid-template-columns:1fr; }
  .studio__note{ align-self:start; }
  .pillars{ grid-template-columns:repeat(2,1fr); }
  .pillar:nth-child(3){ border-left:none; }
}
@media (max-width:768px){
  .nav{ grid-template-columns:auto 1fr; }
  .nav__links{ display:none; }

  /* 1 · Hero: stop forcing a full-viewport space-between spread (that's
     what was creating the dead air below the title); size to content. */
  .hero{
    min-height:auto; justify-content:flex-start;
    padding-top:100px; padding-bottom:40px; gap:32px;
  }
  /* gap(32px) + margin-top = total space between "Creative studio / CDMX"
     and the headline; 26px brings that total to 58px, exactly +20% over
     the previous 48px. */
  .hero__title{ margin-top:26px; }
  .hero__foot{ flex-direction:column; align-items:flex-start; }

  /* 2 · Selected Work: horizontal scroll-snap carousel instead of a stack.
     Touch tap feedback lives in the (hover:none) block below. */
  .feat{
    display:flex; flex-wrap:nowrap; overflow-x:auto; scroll-snap-type:x mandatory;
    scroll-padding-left:var(--pad); gap:24px; max-width:none;
    padding-bottom:4px; -ms-overflow-style:none; scrollbar-width:none;
  }
  .feat::-webkit-scrollbar{ display:none; }
  .feat__item{ flex:0 0 78%; scroll-snap-align:start; }

  /* 3 · More Work: hide the hover hint, no image-preview affordance to
     promise on touch (the underlying JS is already fine-pointer gated). */
  .more .section-head__idx{ display:none; }

  .flow{ font-size:clamp(24px,7vw,34px); }

  /* the desktop grouping (~3 names/line, sized for a wide row) wraps
     unevenly at phone widths and can strand a single name on its own row;
     swap to a mobile-only grouping tuned so every line either pairs up
     cleanly or is a name long enough that it was always going to stand
     alone (see .flow--mobile markup in index.html). */
  .flow--desktop{ display:none; }
  .flow--mobile{ display:flex; flex-direction:column; }

  /* 3b · More Work carousel: the auto-playing marquee has no way to jump
     ahead on a phone, so swap it for a free, momentum-scrolling swipe —
     drop the animation (it would just fight the user's own scroll
     position) and hide the aria-hidden loop-duplicate half, since without
     the animation there's nothing to loop into. */
  .carousel{ overflow-x:auto; -webkit-overflow-scrolling:touch; scrollbar-width:none; -ms-overflow-style:none; }
  .carousel::-webkit-scrollbar{ display:none; }
  .carousel__track{ animation:none; }
  .carousel__link[aria-hidden="true"]{ display:none; }

  /* 4 · What we do: number/title/description on the left, icon on the
     right, compact row instead of a tall stacked card. */
  .pillars{ grid-template-columns:1fr; }
  .pillar,.pillar + .pillar{ border-left:none; padding-left:0; }
  .pillar{
    display:flex; align-items:center; justify-content:space-between;
    gap:16px; padding:26px 0;
  }
  .pillar__gfx{ order:2; flex:0 0 30%; margin:0; display:flex; justify-content:center; }
  .pillar__gfx svg{ width:52px; height:52px; }
  .pillar__text{ order:1; flex:1 1 66%; min-width:0; }
  .pillar__title{ margin-top:8px; }
  .pillar__desc{ margin-top:8px; max-width:none; }

  /* 5 · Contact: phone and address become their own stacked lines. */
  .contact__row{ flex-direction:column; align-items:flex-start; gap:10px; }

  /* 6 · Founded by: compact. */
  .founder{ padding-top:28px; padding-bottom:40px; }

  .foot__top{ grid-template-columns:1fr; gap:32px; }
  /* footer lockup: desktop shows copyright above the logo (DOM order); on
     mobile it belongs at the very end instead, so flip the visual order
     here with flex without touching the markup. */
  .foot__lockup{ display:flex; flex-direction:column; }
  .foot__wordmark-wrap{ order:1; padding-top:clamp(28px,7vw,48px); }
  .foot__bottom{ order:2; margin-top:clamp(24px,6vw,40px); }

  /* 7 · Case study pages: meta row wraps tighter, offset text/grid collapse
     to a single column, other-works cards stack. */
  .case-hero{ padding-top:100px; padding-bottom:40px; }
  .case-hero__meta{ gap:22px; }
  .case-hero-media{ height:70vh; }
  .case-desc{ grid-template-columns:1fr; padding-top:clamp(50px,12vw,80px); padding-bottom:clamp(50px,12vw,80px); }
  .case-desc__text{ grid-column:1/2; font-size:clamp(20px,6vw,26px); }
  .case-grid{ grid-template-columns:1fr; }
  .case-grid__item, .case-grid__item:nth-child(3n+1){ grid-column:1/-1; aspect-ratio:4/5; }

  /* other-works: label stacks above, cards become a swipeable horizontal
     slider — same scroll-snap technique as the Selected Work carousel. */
  .other-works{ flex-direction:column; gap:22px; }
  .other-works__head{ width:auto; }
  .other-works__grid{
    display:flex; overflow-x:auto; scroll-snap-type:x mandatory; scroll-padding-left:var(--pad);
    padding-bottom:4px; -ms-overflow-style:none; scrollbar-width:none;
  }
  .other-works__grid::-webkit-scrollbar{ display:none; }
  .other-works__card{ flex:0 0 68%; scroll-snap-align:start; }
}

/* interactions that only make sense (or only need a fallback) where there's
   no sustained hover, i.e. touch */
@media (hover:none){
  /* 2 · tap feedback on Selected Work cards */
  .feat__media img{ transition:transform .15s var(--ease); }
  .feat__item:active .feat__media img{ transform:scale(.96); }

  /* 3 · a clean, static text list: no dimming interaction to promise */
  .flow:hover .flow__item{ opacity:1; }
  .flow:hover .flow__sep{ opacity:1; }
}

@media (prefers-reduced-motion:reduce){
  *{ animation:none !important; }
  html.js .reveal{ opacity:1; transform:none; transition:none; }
  html{ scroll-behavior:auto; }
  .cursor-media, .invert-dot{ display:none !important; }
  .feat__item:hover .feat__media img{ transform:none; }
}
