/* ============================================================
   HANO STUDIOS
   Tokens taken directly from the brand guidelines.
   ============================================================ */

:root{
  /* Brand colours */
  --void:      #0A0A0A;
  --frost:     #FFFFFF;
  --nebula:    #47127C;
  --aurora:    #8B32F7;

  /* Derived neutrals */
  --ink-90:    rgba(255,255,255,.90);
  --ink-60:    rgba(255,255,255,.60);
  --ink-40:    rgba(255,255,255,.40);
  --ink-14:    rgba(255,255,255,.14);
  --ink-08:    rgba(255,255,255,.08);
  --dark-60:   rgba(10,10,10,.60);
  --dark-14:   rgba(10,10,10,.14);


  /* Type */
  --sans: 'Instrument Sans', system-ui, -apple-system, sans-serif;
  --serif:'Aleo', Georgia, serif;

  /* Rhythm */
  --pad:  clamp(20px, 4vw, 64px);
  --gap:  clamp(48px, 8vw, 140px);
}

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

html{ scroll-behavior:smooth; -webkit-text-size-adjust:100%; }

body{
  background:var(--void);
  color:var(--frost);
  font-family:var(--sans);
  font-weight:400;
  line-height:1.35;
  letter-spacing:-.02em;
  overflow-x:hidden;
  -webkit-font-smoothing:antialiased;
}

img{ max-width:100%; display:block; }
a{ color:inherit; text-decoration:none; }
ul,ol{ list-style:none; }
button{ font:inherit; color:inherit; background:none; border:0; cursor:pointer; }

:focus-visible{ outline:2px solid var(--aurora); outline-offset:3px; }

/* ---------- Shared type patterns ---------- */

/* Two-tone headings: bold lead-in, light remainder */
.h-split{
  font-size:clamp(30px, 4.4vw, 62px);
  font-weight:300;
  letter-spacing:-.03em;
  line-height:1.04;
}
.h-split strong{ font-weight:600; }
.h-split.dark{ color:var(--void); }

.sub{
  margin-top:14px;
  max-width:34ch;
  font-size:clamp(13px, 1.05vw, 15px);
  font-weight:400;
  line-height:1.5;
  color:var(--ink-40);
}
.sub strong{ font-weight:500; color:var(--ink-90); }
.sub.dark{ color:var(--dark-60); }
.sub.dark strong{ color:var(--void); }

.eyebrow{
  font-size:11px;
  font-weight:500;
  letter-spacing:.10em;
  text-transform:uppercase;
  color:var(--ink-60);
}

.link-arrow{
  display:inline-flex;
  align-items:center;
  gap:12px;
  margin-top:34px;
  padding-bottom:6px;
  font-size:12px;
  font-weight:500;
  letter-spacing:.10em;
  text-transform:uppercase;
  border-bottom:1px solid var(--ink-40);
  transition:gap .3s ease, border-color .3s ease;
}
.link-arrow:hover{ gap:20px; border-color:var(--frost); }
.link-arrow.dark{ border-bottom-color:var(--dark-14); color:var(--void); }
.link-arrow.dark:hover{ border-bottom-color:var(--void); }

/* Reveal-on-scroll base state — only hidden when JS is running,
   so content is never stranded invisible if scripts fail to load. */
.js .reveal{ opacity:0; transform:translateY(26px); }

/* Fallbacks when GSAP is unavailable (blocked CDN, CSP, offline).
   Pinned/scrubbed sections degrade to plain scrollable layout. */
.no-gsap .works-pin{ height:auto; overflow-x:auto; overflow-y:hidden; }
.no-gsap .works-track{ padding-block:12vh; }
.no-gsap .wipe{ display:none; }

/* ============================================================
   MENU
   ============================================================ */

.menu-btn{
  position:fixed;
  top:clamp(16px,2.4vw,32px);
  right:clamp(16px,2.4vw,32px);
  z-index:120;
  width:46px; height:46px;
  display:grid;
  place-content:center;
  gap:5px;
  border-radius:50%;
  background:var(--frost);
  opacity:0;
  pointer-events:none;
  transition:opacity .4s ease, background .3s ease;
}
.menu-btn.is-visible{ opacity:1; pointer-events:auto; }
.menu-btn span{
  display:block;
  width:16px; height:1.5px;
  background:var(--void);
  transition:transform .35s ease, opacity .25s ease;
}
/* Over the light services section a white pill on white is invisible, so it
   inverts to a dark pill — which is also how Figma draws it there. */
.menu-btn.on-light{ background:var(--void); }
.menu-btn.on-light span{ background:var(--frost); }

/* Open, it is a bare white × on the purple ground (Figma frame 183). The white
   pill is only needed closed, where it sits over arbitrary page content. */
.menu-btn.is-open{ background:transparent; }
.menu-btn.is-open span{ width:20px; background:var(--frost); }
.menu-btn.is-open span:nth-child(1){ transform:translateY(3.25px) rotate(45deg); }
.menu-btn.is-open span:nth-child(2){ transform:translateY(-3.25px) rotate(-45deg); }

.menu-overlay{
  position:fixed;
  inset:0;
  z-index:110;
  display:flex;
  flex-direction:column;
  justify-content:center;
  align-items:flex-end;
  padding:clamp(72px,10vh,120px) clamp(20px,4vw,64px) clamp(64px,9vh,104px);
  background:var(--nebula);
  clip-path:circle(0% at calc(100% - 40px) 40px);
  transition:clip-path .7s cubic-bezier(.76,0,.24,1);
}
.menu-overlay.is-open{ clip-path:circle(150% at calc(100% - 40px) 40px); }

/* Logomark sits top-left, opposite the close button */
.menu-mark{
  position:absolute;
  top:clamp(16px,2.4vw,32px);
  left:clamp(20px,4vw,64px);
  width:clamp(32px,3.2vw,46px);
  height:auto;
}

.menu-inner{ text-align:left; }

.menu-links li{ overflow:hidden; }
.menu-links a{
  display:flex;
  align-items:baseline;
  padding:2px 0;
  font-size:clamp(28px,5vw,68px);
  font-weight:400;
  letter-spacing:-.03em;
  line-height:1.2;
  text-transform:uppercase;
  transform:translateY(110%);
  transition:transform .6s cubic-bezier(.22,1,.36,1), opacity .3s ease;
}
/* Arrow lives in a reserved gutter, so revealing it never shifts the word */
.menu-links a::before{
  content:'\2192';
  flex:none;
  width:.9em;
  margin-right:.16em;
  opacity:0;
  transform:translateX(-.25em);
  transition:opacity .3s ease, transform .3s ease;
}
.menu-links a:hover::before,
.menu-links a:focus-visible::before{ opacity:1; transform:none; }

.menu-overlay.is-open .menu-links a{ transform:translateY(0); }
.menu-links li:nth-child(1) a{ transition-delay:.10s; }
.menu-links li:nth-child(2) a{ transition-delay:.16s; }
.menu-links li:nth-child(3) a{ transition-delay:.22s; }
.menu-links li:nth-child(4) a{ transition-delay:.28s; }
.menu-links li:nth-child(5) a{ transition-delay:.34s; }
.menu-links li:nth-child(6) a{ transition-delay:.40s; }

/* Copyright left, socials right, pinned to the bottom edge */
.menu-foot{
  position:absolute;
  left:clamp(20px,4vw,64px);
  right:clamp(20px,4vw,64px);
  bottom:clamp(18px,3vh,34px);
  display:flex;
  flex-wrap:wrap;
  align-items:center;
  justify-content:space-between;
  gap:12px clamp(16px,3vw,38px);
  font-size:12px;
  color:var(--ink-60);
}
.menu-social{ display:flex; flex-wrap:wrap; gap:clamp(14px,2.4vw,34px); }
.menu-foot a{ transition:color .25s ease; }
.menu-foot a:hover{ color:var(--frost); }

@media (max-width:600px){
  .menu-overlay{ align-items:flex-start; }
  .menu-foot{ justify-content:flex-start; }
}

/* ============================================================
   HERO
   ============================================================ */

/* Opaque, and stacked above the statement while pinned: the pin runs with
   pinSpacing:false, so the statement scrolls up BEHIND the hero during the
   sequence. The z-index is managed INLINE by main.js (3 while pinned, 1 once
   released so the statement paints over the riding hero) — it must NOT be set
   here: GSAP's pin-spacer clones the element's computed z-index at pin setup,
   and a spacer stuck at 3 out-stacks the statement whatever the hero says. */
.hero{
  position:relative;
  min-height:100svh;
  padding:var(--pad);
  display:flex;
  flex-direction:column;
  justify-content:space-between;
  background:var(--void);
  overflow:hidden;
}
/* Scroll room for the pinned sequence. The pin runs until the statement's top
   reaches the top of the screen, so this margin IS the extra scroll the hero
   animation gets beyond one viewport (main.js derives the pin length from the
   statement's position — one source of truth). Compressing the sequence into
   exactly one viewport made the whole thing rush past in a single flick.
   The margin region is always behind the pinned hero, never visible.
   Only when GSAP is actually driving the pin — otherwise it is a real gap. */
.js:not(.no-gsap) .hero{ margin-bottom:62svh; }

.hero-glow{
  position:absolute;
  inset:-10%;
  z-index:0;
  background:
    radial-gradient(46% 40% at 68% 34%, rgba(255,255,255,.20) 0%, transparent 62%),
    radial-gradient(30% 34% at 40% 62%, rgba(255,255,255,.09) 0%, transparent 66%),
    radial-gradient(70% 60% at 84% 12%, rgba(255,255,255,.07) 0%, transparent 70%);
  filter:blur(38px);
  animation:drift 26s ease-in-out infinite alternate;
}
/* A second, counter-drifting layer on a different period. One layer on one loop
   read as static because every highlight moved together; two layers at 26s and
   19s cross each other, so the gradient keeps reshaping instead of just sliding. */
.hero-glow::after{
  content:'';
  position:absolute;
  inset:-14%;
  background:
    radial-gradient(38% 32% at 26% 52%, rgba(255,255,255,.13) 0%, transparent 64%),
    radial-gradient(52% 44% at 82% 68%, rgba(255,255,255,.10) 0%, transparent 68%);
  filter:blur(46px);
  animation:drift-b 19s ease-in-out infinite alternate;
}
@keyframes drift{
  from{ transform:translate3d(-2%,-1%,0) scale(1);   }
  to  { transform:translate3d(3%,2%,0)   scale(1.10);}
}
@keyframes drift-b{
  from{ transform:translate3d(4%,2%,0)  scale(1.06) rotate(0deg);   }
  to  { transform:translate3d(-3%,-2%,0) scale(1)    rotate(-6deg); }
}

.hero-tagline{
  position:relative; z-index:2;
  max-width:36ch;
  font-size:clamp(13px, 1.15vw, 16px);
  font-weight:400;
  line-height:1.5;
  color:var(--ink-90);
}
.hero-tagline em{
  font-family:var(--serif);
  font-style:italic;
  font-weight:500;
  letter-spacing:0;
}

.hero-mark{
  position:absolute;
  top:var(--pad);
  right:var(--pad);
  z-index:2;
  width:clamp(38px, 4vw, 58px);
  height:auto;
}

/* Centred on the hero, above the glow but below the words, so the title reads
   over it while it is small and it swallows the screen once it grows.
   opacity:0 by default — with no GSAP it simply never appears. */
.hero-square{
  position:absolute;
  top:50%;
  left:50%;
  z-index:1;
  width:clamp(90px,17vw,290px);
  aspect-ratio:1;
  margin:calc(clamp(90px,17vw,290px) / -2) 0 0 calc(clamp(90px,17vw,290px) / -2);
  border-radius:10px;
  background:linear-gradient(160deg, #1c1c1c 0%, #101010 55%, var(--void) 100%);
  opacity:0;
  will-change:transform, border-radius, opacity;
}

.hero-bottom{ position:relative; z-index:2; }

.hero-title{
  display:flex;
  align-items:baseline;
  width:100%;
  /* "HANO STUDIOS." is ~7.26x its font-size wide and must not wrap, so the
     largest size that still fits inside the page padding is ~12.67vw.
     15.2vw overflowed the viewport at every width below ~2500px. */
  font-size:clamp(40px, 12.2vw, 300px);
  font-weight:500;
  letter-spacing:-.045em;
  line-height:.86;
  white-space:nowrap;
}
.ht-gap{ flex:0 0 .22em; }
.ht-a,.ht-b{ display:inline-block; will-change:transform, filter, opacity; }

.hero-nav{
  display:flex;
  align-items:center;
  justify-content:space-between;
  gap:20px;
  margin-top:clamp(16px,2vw,28px);
  padding-top:clamp(14px,1.6vw,20px);
  border-top:1px solid var(--ink-14);
}
.hero-nav ul{ display:flex; gap:clamp(18px,3.4vw,58px); }
.hero-nav a{
  font-size:clamp(11px,1vw,14px);
  font-weight:500;
  letter-spacing:.06em;
  text-transform:uppercase;
  color:var(--ink-90);
  transition:color .25s ease;
}
.hero-nav a:hover{ color:var(--aurora); }

/* ============================================================
   STATEMENT / CLIENTS
   ============================================================ */

/* Tall enough that the copy can pin while its lines reveal one per scroll step,
   then release into the client grid.

   The top of this gradient is the same colour the .hero-square carries, so when
   the square fades out at the end of the hero pin the statement is already
   sitting behind it wearing that exact tone — the cover hands over instead of
   cutting. No leading padding: the square finishes flush against this section,
   and a --gap of empty space there was part of the black void. */
.statement{
  position:relative;
  /* Above the released hero (main.js drops it to z-index:1 at pin end) but
     below the pinned hero mid-sequence (z-index:3). */
  z-index:2;
  padding:0 var(--pad) var(--gap);
  background:linear-gradient(172deg, #1c1c1c 0, #141414 44svh, #0f0f0f 88svh, var(--void) 140svh);
}

/* Sized by its content, not the viewport. A 100svh box with the copy centred
   left half a screen of empty black between the copy and the client grid in
   normal flow — the gap kept reappearing at every window height. The top pad
   puts the copy in the upper-middle of the screen while pinned (where the Figma
   statement sits); the small bottom pad is all that separates it from the
   clients once the pin releases. */
.statement-pin{
  padding:max(20svh, 90px) 0 max(8svh, 44px);
}

/* Each line clips its own inner span, so the reveal slides up from behind the
   line above rather than fading in place. */
.sc-line{
  display:block;
  overflow:hidden;
}
.sc-line > span{
  display:block;
  will-change:transform, opacity;
}
/* Only hidden when JS is running — same guard as .reveal, so a blocked script
   leaves the statement fully readable instead of stranding it invisible.

   Opacity only, no transform. GSAP owns the Y position of these spans, and a
   translateY here stacked on top of its yPercent so the lines finished 130px
   below a 124px clip box — fully lit but clipped out of sight. */
.js .sc-line > span{
  opacity:0;
}
.no-gsap .sc-line > span{ transform:none; opacity:1; }

.statement-copy{
  font-size:clamp(24px, 3.5vw, 50px);
  font-weight:400;
  letter-spacing:-.035em;
  line-height:1.24;
  /* em, not ch. Each .sc-line is a pre-broken line from the Figma and must sit
     on ONE visual line — the reveal animates each as a single unit, so a line
     that wraps reveals as a two-line block and the whole rhythm is wrong.
     Measured across 390-1920, the longest of the four lines is a consistent
     ~23.7x its own font-size, so 25em clears it at every size while still
     constraining the column on very wide screens. The old 26ch resolved to just
     807px against a 1187px line, so all four lines wrapped and the section was
     eight lines tall instead of four. */
  max-width:25em;
}

.clients{ margin-top:clamp(24px,4vw,60px); }

/* 5 x 2 for the ten supplied marks, as the Figma draws it. */
.client-grid{
  display:grid;
  grid-template-columns:repeat(5,1fr);
  margin-top:18px;
  border:1px solid var(--ink-08);
}
.client-grid li{
  display:grid;
  place-items:center;
  /* ~8.6% of the grid's width per row, measured off the reference (105px tall
     rows on a 1225px-wide grid at a 1332 viewport). 7vw gave 93px and the wall
     read shallower than the frame. */
  min-height:clamp(78px,7.9vw,112px);
  padding:12px;
  font-size:clamp(12px,1.15vw,17px);
  font-weight:600;
  letter-spacing:-.01em;
  text-align:center;
  color:var(--ink-40);
  border-right:1px solid var(--ink-08);
  border-bottom:1px solid var(--ink-08);
  transition:color .3s ease, background .3s ease;
}
.client-grid li:hover{ color:var(--frost); background:var(--ink-08); }
.client-grid img{
  width:auto;
  /* Capped on BOTH axes, because the real set mixes wide wordmarks with square
     roundels. Width alone let a roundel's own aspect squash it to ~39px of
     visible art inside a width-limited box; the height cap is what actually
     governs those, and the width cap governs the wordmarks.
     Sized to the Figma, where a mark occupies roughly 45% of its cell's width
     and ~22% of its height. At 168/52 they filled ~67% of the cell and the
     wall read heavy and cramped next to the reference. */
  max-width:min(100%,112px);
  max-height:clamp(26px,2.4vw,34px);
  height:auto;
  /* Full strength. These are the clients' own marks in their own brand colours,
     and the Figma draws them at full saturation — Kalshi's green, Maxy's orange,
     Abstract's green all read as themselves. Dimming (.42, then .62) was carried
     over from the flat grey text placeholders and muted every one of them. */
  opacity:1;
}
/* 5 across, 2 down: no right edge on the 5th of each row, no bottom edge on
   the last row. The outer border on .client-grid supplies the frame. */
.client-grid li:nth-child(5n){ border-right:0; }
.client-grid li:nth-child(n+6){ border-bottom:0; }

/* ============================================================
   WORKS — horizontal scroll
   ============================================================ */

.works{ position:relative; background:var(--void); }
.works-pin{ height:100svh; overflow:hidden; }
.works-track{
  display:flex;
  align-items:center;
  gap:clamp(24px,3vw,54px);
  height:100%;
  padding:0 var(--pad);
  will-change:transform;
}

.works-intro{
  flex:none;
  width:clamp(240px,26vw,360px);
  padding-right:clamp(10px,2vw,30px);
}

.work-card{
  flex:none;
  width:clamp(272px,38vw,520px);
}
.work-img{
  aspect-ratio:16/10;
  border-radius:4px;
  background:#141414;
  background-size:cover;
  background-position:center;
  transition:transform .6s cubic-bezier(.22,1,.36,1);
}
.work-card:hover .work-img{ transform:translateY(-8px); }

.work-meta{
  display:grid;
  grid-template-columns:minmax(96px,.34fr) 1fr;
  gap:clamp(12px,1.6vw,26px);
  margin-top:18px;
}
.work-meta h3{
  font-size:clamp(16px,1.5vw,23px);
  font-weight:500;
  letter-spacing:-.03em;
}
.work-meta p{
  font-size:clamp(12px,.95vw,14px);
  font-weight:400;
  line-height:1.5;
  color:var(--ink-90);
}
.work-meta .tag{
  grid-column:2;
  margin-top:10px;
  font-size:10px;
  font-weight:500;
  letter-spacing:.12em;
  text-transform:uppercase;
  color:var(--ink-40);
}

/* Placeholder art tones — swap for real project images.
   Scoped to the four elements that actually show art. Unscoped, these matched
   any [data-tone] and painted gradients across the service list rows, whose
   data-tone is only there for main.js to copy onto .service-preview. */
.work-img[data-tone="amber"],
.phone[data-tone="amber"],
.member-photo[data-tone="amber"],
.service-preview[data-tone="amber"] { background-image:linear-gradient(135deg,#3a2a10,#0d0906 62%,#241906); }

.work-img[data-tone="green"],
.phone[data-tone="green"],
.member-photo[data-tone="green"],
.service-preview[data-tone="green"] { background-image:linear-gradient(135deg,#0d2a1c,#050d09 62%,#0a1f15); }

.work-img[data-tone="orange"],
.phone[data-tone="orange"],
.member-photo[data-tone="orange"],
.service-preview[data-tone="orange"]{ background-image:linear-gradient(135deg,#3a1c08,#0d0704 62%,#2a1406); }

.work-img[data-tone="lilac"],
.phone[data-tone="lilac"],
.member-photo[data-tone="lilac"],
.service-preview[data-tone="lilac"] { background-image:linear-gradient(135deg,#2a2440,#0a0910 62%,#1e1a30); }

.work-img[data-tone="steel"],
.phone[data-tone="steel"],
.member-photo[data-tone="steel"],
.service-preview[data-tone="steel"] { background-image:linear-gradient(135deg,#242424,#0b0b0b 62%,#1a1a1a); }

.work-img[data-tone="purple"],
.phone[data-tone="purple"],
.member-photo[data-tone="purple"],
.service-preview[data-tone="purple"]{ background-image:linear-gradient(135deg,var(--nebula),#120520 62%,#2a0d47); }

/* ============================================================
   SHOWCASE
   ============================================================ */

/* Two real columns — heading left, carousel right. This used to be a single
   column with the stage yanked up by a negative margin-top, which overlapped
   the heading as soon as the viewport was wide enough. */
.showcase{
  position:relative;
  /* Explicit, not inherited from body. main.js now pins this whole section
     (position:fixed while pinned), and a fixed box with no background of its
     own is transparent everywhere its children don't paint — Works, still
     scrolling underneath in normal flow, showed straight through the grid
     gaps and the empty space around the phones. */
  background:var(--void);
  display:grid;
  grid-template-columns:minmax(240px,.72fr) 1.75fr;
  gap:clamp(24px,4vw,64px);
  align-items:center;
  padding:var(--gap) var(--pad);
  overflow:hidden;
}
.showcase-head{ position:relative; z-index:2; max-width:420px; }
/* Sized to the head's own grid column (~364px at 1440) so the heading breaks
   after "Ideas" into exactly two lines, as the frame draws it — at the shared
   62px it broke after "Shaping" into three. On phones it fits on one line,
   matching mobile frame 196. */
.showcase-head .h-split{ font-size:clamp(28px,3.6vw,52px); }
.showcase-controls{ display:flex; gap:26px; margin-top:38px; }
.arrow{
  font-size:22px;
  line-height:1;
  color:var(--ink-40);
  transition:color .25s ease, transform .25s ease;
}
.arrow:hover{ color:var(--frost); transform:translateX(3px); }

/* Sound toggle. Sits with the arrows and inherits their colour, so the icon
   fill/stroke follow currentColor and the hover state comes for free. */
.sound-btn{
  display:inline-grid;
  place-items:center;
  width:24px;
  height:24px;
  /* The arrows nudge sideways on hover; a symmetrical icon should not. */
  transition:color .25s ease;
}
.sound-btn:hover{ transform:none; }
.sound-btn svg{
  grid-area:1/1;                 /* stack both icons, show one */
  width:20px;
  height:20px;
  fill:currentColor;
  stroke:none;
}
.sound-btn svg .stroke{
  fill:none;
  stroke:currentColor;
  stroke-width:1.9;
  stroke-linecap:round;
}
.sound-btn .ico-on{ display:none; }
.sound-btn.is-on .ico-muted{ display:none; }
.sound-btn.is-on .ico-on{ display:block; }
.sound-btn.is-on{ color:var(--frost); }

.showcase-stage{
  display:flex;
  gap:clamp(18px,2.6vw,40px);
  /* bleed past the page padding so the carousel reads as continuing off-screen */
  margin-right:calc(var(--pad) * -1);
  /* Side padding equal to half the free track width, so the first and last
     slides can actually reach the centre. Without it scrollLeft clamps at 0 and
     the outer slides never centre however far you scroll. */
  padding:40px 42% 40px 38%;
  overflow-x:auto;
  scrollbar-width:none;
}
/* Snap only when nothing is driving scrollLeft itself: the GSAP pin below sets
   it every scrub tick, and the browser's own snap-settling logic fights that —
   once the tween paused mid-scrub the stage would visibly snap to whichever
   phone was nearest and jump, rather than tracking smoothly. Under .no-gsap /
   reduced motion nothing scrubs it at all, so a manual swipe needs the snap. */
.no-gsap .showcase-stage{ scroll-snap-type:x mandatory; }
.showcase-stage::-webkit-scrollbar{ display:none; }

.phone{
  position:relative;
  flex:none;
  width:clamp(150px,15vw,232px);
  /* Matches the clips (540x960). At the 9/19.5 of a modern handset, cover
     cropped ~11% off each side — enough to lose the client logo in the corner
     and clip the captions, which sit close to the edge in every one of these.
     Showing the work whole beats a taller mockup. */
  aspect-ratio:9/16;
  scroll-snap-align:center;
  display:flex;
  align-items:flex-end;
  padding:18px;
  border:1px solid var(--ink-14);
  border-radius:26px;
  background-size:cover;
  overflow:hidden;                 /* keeps the video inside the rounded frame */
  /* Inactive slides are dimmed AND blurred, per the reference. The blur is what
     makes the strip read as one phone in focus rather than several competing
     for attention; main.js pauses these same videos, so a blurred neighbour is
     genuinely stopped, not merely out of focus. */
  filter:brightness(.42) blur(4px);
  transition:filter .5s ease, transform .5s ease;
}
.phone:hover{ filter:brightness(.72) blur(1.5px); }

/* The notch. Frame 198's phones are proper device mockups — this was a plain
   rounded card with no hardware cues at all, which is why it read as "just a
   card" rather than "a phone playing a video". Sits inside the frame's own
   bounds so .phone's overflow:hidden never clips it. */
.phone::before{
  content:'';
  position:absolute;
  top:9px;
  left:50%;
  z-index:3;
  width:32%;
  height:5px;
  transform:translateX(-50%);
  border-radius:5px;
  background:rgba(0,0,0,.55);
}

/* The active slide is sharp and lifted while its video plays. Also wins over
   :hover — without the second selector, pointing at the playing phone would
   drop it back to the dimmed hover state. */
.phone.is-active,
.phone.is-active:hover{ filter:brightness(1) blur(0); transform:translateY(-10px); }

.phone-video{
  position:absolute;
  inset:0;
  width:100%;
  height:100%;
  object-fit:cover;
  border-radius:inherit;
  pointer-events:none;
}
.phone span{
  font-size:11px;
  font-weight:500;
  letter-spacing:.08em;
  text-transform:uppercase;
  color:var(--ink-60);
}

/* ============================================================
   CIRCLE WIPE
   ============================================================ */

.wipe{ height:170vh; position:relative; }
.wipe-circle{
  --scale:.06;
  position:sticky;
  top:0;
  height:100vh;
  overflow:hidden;
}
/* Absolute + translate(-50%,-50%), not display:grid;place-items:center. The
   circle's own unscaled box is 56vmax square (~473px on a 390-wide phone) —
   wider than the container even before it starts growing. Centering an
   oversized box that overflows its container is exactly the case where grid
   alignment is inconsistent: it rendered visibly off-centre to the right on a
   phone. An explicit centre-point + translate has no such ambiguity — it is
   centred on the container's actual midpoint at every scale, full stop. */
.wipe-circle::after{
  content:'';
  position:absolute;
  top:50%;
  left:50%;
  width:56vmax; height:56vmax;
  border-radius:50%;
  background:var(--frost);
  transform:translate(-50%,-50%) scale(var(--scale));
  will-change:transform;
}

/* ============================================================
   SERVICES (light section)
   ============================================================ */

.services{
  position:relative;
  z-index:2;
  display:grid;
  grid-template-columns:minmax(220px,.85fr) 1.6fr;
  gap:clamp(28px,5vw,90px);
  padding:var(--gap) var(--pad);
  background:var(--frost);
  color:var(--void);
}

.services-left{ position:relative; }

.service-preview{
  width:clamp(150px,13vw,200px);
  aspect-ratio:1;
  margin-top:38px;
  border-radius:3px;
  background:#eee;
  background-size:cover;
  opacity:0;
  transform:scale(.94);
  transition:opacity .4s ease, transform .4s ease;
}
.service-preview.is-on{ opacity:1; transform:scale(1); }

.service-list{ align-self:center; }
.service-list li{ border-bottom:1px solid var(--dark-14); }
.service-list li:first-child{ border-top:1px solid var(--dark-14); }
.service-list a{
  display:block;
  padding:clamp(16px,2vw,26px) clamp(14px,1.6vw,22px);
  font-size:clamp(17px,2.3vw,32px);
  font-weight:500;
  letter-spacing:-.025em;
  text-transform:uppercase;
  transition:background .35s ease, color .35s ease, padding-left .35s ease;
}
.service-list li:hover a{
  background:var(--void);
  color:var(--frost);
  padding-left:clamp(22px,2.6vw,38px);
}

/* ============================================================
   TEAM
   ============================================================ */

/* White backdrop the black team panel grows over, continuing the light services
   section. Default state is the panel at full width covering it completely, so
   without GSAP this is invisible and the section looks exactly as before. */
.team-riser{ background:var(--frost); }

.team{
  position:relative;
  margin-inline:auto;
  padding:var(--gap) var(--pad);
  background:var(--void);
  overflow:hidden;              /* keeps content inside the rounded corners */
}

.team-grid{
  display:grid;
  grid-template-columns:1.15fr 1fr;
  gap:clamp(30px,5vw,80px);
  align-items:start;
  margin-top:clamp(28px,4vw,50px);
}
.team-cards{ display:grid; grid-template-columns:1fr 1fr; gap:clamp(14px,1.8vw,26px); }

.member{ border:1px solid var(--ink-14); padding:14px; }
.member-photo{
  aspect-ratio:4/5;
  background:#161616;
  background-size:cover;
  background-position:center;
}
.member figcaption{ padding:14px 2px 4px; }
.member strong{ display:block; font-size:clamp(13px,1.1vw,16px); font-weight:600; }
.member span{ display:block; margin-top:3px; font-size:clamp(11px,.9vw,13px); color:var(--ink-40); }

.stats div{
  display:flex;
  align-items:baseline;
  justify-content:space-between;
  gap:20px;
  padding:clamp(14px,1.8vw,24px) 0;
  border-bottom:1px solid var(--ink-14);
}
.stats dt{
  font-size:clamp(30px,3.6vw,50px);
  font-weight:300;
  letter-spacing:-.04em;
  color:var(--ink-60);
}
.stats dd{
  font-size:clamp(10px,.95vw,13px);
  font-weight:500;
  letter-spacing:.1em;
  text-transform:uppercase;
  color:var(--ink-90);
}

/* ============================================================
   FAQ
   ============================================================ */

.faq{
  display:grid;
  grid-template-columns:minmax(200px,.8fr) 1.5fr;
  gap:clamp(28px,5vw,80px);
  padding:var(--gap) var(--pad);
}

.faq-list details{ border-bottom:1px solid var(--ink-14); }
.faq-list summary{
  display:flex;
  align-items:center;
  justify-content:space-between;
  gap:20px;
  padding:clamp(15px,1.8vw,22px) 0;
  font-size:clamp(13px,1.15vw,17px);
  font-weight:400;
  list-style:none;
  cursor:pointer;
  transition:color .25s ease;
}
.faq-list summary::-webkit-details-marker{ display:none; }
.faq-list summary::after{
  content:'+';
  flex:none;
  font-size:19px;
  font-weight:300;
  line-height:1;               /* tight box so the glyph rotates about its own centre */
  color:var(--ink-40);
  transition:transform .3s ease;
}
/* The + rotates into an ×. Swapping content to '\00d7' instead would snap,
   because content is not an animatable property. */
.faq-list details[open] summary::after{ transform:rotate(45deg); }
.faq-list summary:hover{ color:var(--aurora); }
.faq-list p{
  max-width:62ch;
  overflow:hidden;              /* so main.js can animate the height cleanly */
  padding:0 0 22px;
  font-size:clamp(12px,1vw,15px);
  line-height:1.6;
  color:var(--ink-40);
}

/* ============================================================
   CTA
   ============================================================ */

.cta{
  position:relative;
  /* Figma frame 172 is 578 tall on an 1080 frame — 53.5% of its width. */
  min-height:82svh;
  display:flex;
  flex-direction:column;
  align-items:center;
  justify-content:center;
  overflow:hidden;
}
/* Figma frame 172: a soft light ribbon that rises from the left edge, crests
   behind the top of the headline, and falls away to the right — an S-sweep, not
   a ring.

   Built from two overlapping elliptical gradients rather than the flank of one
   big ellipse. An ellipse edge has uniform curvature, so it can only ever read
   as a circle corner cutting through whatever it crosses; that is exactly what
   it was doing at the bottom-left. Two ellipses — one for the rising left half,
   one for the falling right half — meet mid-frame and give the bend. Blur is on
   the gradients themselves via soft colour stops, so there is no hard edge to
   catch the eye. */
.cta-glow{
  position:absolute;
  inset:0;
  overflow:hidden;
  /* No wash. The frame is flat black apart from the ribbon — a broad glow here
     lifted every corner to grey and the band stopped reading as a band. */
}
.cta-glow::before,
.cta-glow::after{
  content:'';
  position:absolute;
  pointer-events:none;
}
/* Rising flank: enters at the LEFT EDGE around a third of the way down and
   climbs to its crest just above the headline. The bands are deliberately
   shallow — height decides whether this reads as a light streak or as fog —
   and the two rotations are gentle and opposite so the pair arcs instead of
   running diagonally off a corner.
   NB: this comment was previously two orphaned fragments with no opening
   marker, which is invalid CSS — the parser recovered by discarding the very
   next rule, so the whole left flank of the ribbon was silently dead. */
.cta-glow::before{
  left:-34%;
  top:24%;
  width:86%;
  height:19%;
  background:radial-gradient(closest-side ellipse,
    rgba(255,255,255,.44) 0%,
    rgba(255,255,255,.17) 40%,
    rgba(255,255,255,.045) 68%,
    transparent 82%);
  transform:rotate(-9deg);
  filter:blur(17px);
}
/* Falling flank: continues the crest out toward the right edge, fading as it
   goes so the ribbon thins out rather than stopping. */
.cta-glow::after{
  left:30%;
  top:17%;
  width:88%;
  height:17%;
  background:radial-gradient(closest-side ellipse,
    rgba(255,255,255,.32) 0%,
    rgba(255,255,255,.12) 42%,
    rgba(255,255,255,.032) 70%,
    transparent 84%);
  transform:rotate(7deg);
  filter:blur(19px);
}

@media (max-width:900px){
  .cta-glow::before{ left:-24%; top:21%; width:92%; height:16%; filter:blur(13px); }
  .cta-glow::after{ left:40%; top:17%; width:92%; height:14%; filter:blur(14px); }
}
@media (max-width:600px){
  .cta-glow::before{ left:-28%; top:23%; width:110%; height:13%; filter:blur(9px); }
  .cta-glow::after{ left:34%; top:20%; width:106%; height:11%; filter:blur(10px); }
}
/* The bar is pushed to the bottom by margin-top:auto, so the headline must be
   centred in the space above it rather than sitting at the top of the flex
   column — it was starting at 0% with ~340px of dead black underneath. */
.cta-title{
  position:relative; z-index:2;
  margin:auto 0;
  padding:clamp(30px,4.2vw,56px) var(--pad);
  /* 7.2vw matches the cap height in frame 172; 6.4vw read noticeably small. */
  font-size:clamp(38px,7.2vw,128px);
  font-weight:400;
  letter-spacing:-.04em;
  line-height:1.06;
  text-align:center;
  color:var(--ink-90);
}
.cta-bar{
  position:relative; z-index:2;
  display:block;
  width:100%;
  margin-top:auto;
  padding:clamp(22px,3vw,42px);
  background:var(--frost);
  color:var(--void);
  font-size:clamp(14px,1.5vw,21px);
  font-weight:500;
  letter-spacing:.06em;
  text-transform:uppercase;
  text-align:center;
  transition:background .35s ease, color .35s ease, letter-spacing .35s ease;
}
.cta-bar:hover{ background:var(--aurora); color:var(--frost); letter-spacing:.14em; }

/* ============================================================
   CALENDLY BOOKING MODAL
   ============================================================ */

.cal-modal{
  position:fixed;
  inset:0;
  z-index:200;                     /* above the menu overlay (110) and button (120) */
  display:grid;
  place-items:center;
  padding:clamp(12px,3vw,40px);
}
/* The hidden attribute must win over display:grid */
.cal-modal[hidden]{ display:none; }

.cal-backdrop{
  position:absolute;
  inset:0;
  background:rgba(10,10,10,.82);
  backdrop-filter:blur(6px);
  opacity:0;
  transition:opacity .3s ease;
}
.cal-modal.is-open .cal-backdrop{ opacity:1; }

.cal-panel{
  position:relative;
  z-index:1;
  display:flex;
  flex-direction:column;
  width:min(100%,1020px);
  height:min(88svh,760px);
  border:1px solid var(--ink-14);
  border-radius:6px;
  background:var(--void);
  overflow:hidden;
  opacity:0;
  transform:translateY(14px) scale(.99);
  transition:opacity .3s ease, transform .3s cubic-bezier(.22,1,.36,1);
}
.cal-modal.is-open .cal-panel{ opacity:1; transform:none; }

.cal-head{
  display:flex;
  align-items:center;
  justify-content:space-between;
  gap:20px;
  flex:none;
  padding:clamp(14px,1.8vw,22px) clamp(16px,2vw,26px);
  border-bottom:1px solid var(--ink-14);
}
.cal-head h2{
  font-size:clamp(14px,1.3vw,19px);
  font-weight:500;
  letter-spacing:-.02em;
}
.cal-close{
  flex:none;
  width:34px;
  height:34px;
  display:grid;
  place-items:center;
  font-size:24px;
  line-height:1;
  border-radius:50%;
  color:var(--ink-60);
  transition:background .25s ease, color .25s ease;
}
.cal-close:hover{ background:var(--ink-08); color:var(--frost); }

.cal-embed{
  flex:1;
  min-height:0;
  overflow:auto;
}
.cal-embed iframe{ display:block; width:100%; height:100%; border:0; }

.cal-fallback{
  display:flex;
  flex-direction:column;
  align-items:center;
  gap:14px;
  padding:clamp(40px,7vw,90px) var(--pad);
  text-align:center;
  font-size:14px;
  color:var(--ink-60);
}
.cal-fallback a{
  color:var(--frost);
  border-bottom:1px solid var(--ink-40);
  padding-bottom:3px;
  transition:color .25s ease, border-color .25s ease;
}
.cal-fallback a:hover{ color:var(--aurora); border-bottom-color:var(--aurora); }

@media (max-width:600px){
  .cal-panel{ width:100%; height:92svh; }
}

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

/* Figma frame 173: the footer is the BRIGHT purple (Aurora), not Royal Nebula —
   the deep violet stays on the menu overlay (frame 183). */
.footer{
  position:relative;
  padding:clamp(46px,6vw,86px) var(--pad) 0;
  background:var(--aurora);
  overflow:hidden;
}

.footer-top{
  position:relative; z-index:2;
  display:grid;
  grid-template-columns:1fr auto;
  gap:clamp(32px,5vw,80px);
}
.footer-lead{
  font-size:clamp(19px,2.3vw,32px);
  font-weight:500;
  letter-spacing:-.03em;
  line-height:1.24;
}

.footer-cols{ display:flex; flex-wrap:wrap; gap:clamp(26px,4vw,70px); }
/* Was var(--aurora) — invisible now the footer itself is Aurora. */
.col-head{
  margin-bottom:12px;
  font-size:10px;
  font-weight:500;
  letter-spacing:.12em;
  text-transform:uppercase;
  color:rgba(255,255,255,.55);
}
.footer-cols a{
  display:block;
  padding:3px 0;
  font-size:clamp(12px,1vw,14px);
  color:var(--ink-90);
  transition:color .25s ease;
}
.footer-cols a:hover{ color:var(--frost); }

.footer-mark{
  position:relative;
  z-index:1;
  margin:clamp(22px,3.4vw,42px) 0 0;
  /* Slightly wider than the viewport so it bleeds and the parallax has
     something to reveal — but only just. At 16.5vw it overran by 239px and
     cropped down to "ANO STUDIO". */
  /* Sized to sit inside the page padding rather than bleed off the edges, so the
     wordmark reads whole. The parallax below stays inside the leftover slack. */
  font-size:clamp(44px,13vw,230px);
  font-weight:600;
  letter-spacing:-.045em;
  line-height:.82;
  white-space:nowrap;
  /* Frame 173 draws the wordmark as a clearly-visible lavender over the bright
     purple, not a faint watermark — .14 was tuned for Nebula and vanished on
     Aurora. */
  color:rgba(255,255,255,.34);
  user-select:none;
}

.footer-base{
  position:relative; z-index:2;
  display:flex;
  flex-wrap:wrap;
  align-items:center;
  justify-content:space-between;
  gap:14px;
  padding:20px 0 22px;
  font-size:11px;
  color:rgba(255,255,255,.55);
}
.footer-base div{ display:flex; gap:clamp(14px,2vw,30px); }
.footer-base a:hover{ color:var(--frost); }

/* ============================================================
   RESPONSIVE
   ============================================================ */

@media (max-width:900px){
  .services,
  .faq{ grid-template-columns:1fr; }
  .team-grid{ grid-template-columns:1fr; }
  .footer-top{ grid-template-columns:1fr; }
  /* 2 across, 5 down. Each override has to undo the desktop rule it replaces,
     since these are the same specificity — hence the explicit re-adds. */
  .client-grid{ grid-template-columns:repeat(2,1fr); }
  .client-grid li:nth-child(5n){ border-right:1px solid var(--ink-08); }
  .client-grid li:nth-child(2n){ border-right:0; }
  .client-grid li:nth-child(n+6){ border-bottom:1px solid var(--ink-08); }
  .client-grid li:nth-child(n+9){ border-bottom:0; }
  .showcase{ grid-template-columns:1fr; align-items:start; }
  /* Was 40px. Frame 198's gap from the arrows down to the phones is much
     tighter than that — measured against the reference at roughly half this. */
  .showcase-stage{ margin-top:20px; }
  /* Was 38px, hugely overshooting the reference's tight gap under the
     subtext (~8px). */
  .showcase-controls{ margin-top:14px; }
  /* Was 1fr (stacked title above description). Frame 196 keeps the title in a
     narrow left column and the description in a wider right column all the
     way down to phone width — the stack was never in the reference. */
  .service-preview{ display:none; }
}

/* The mobile Figma frames lay the works out as a plain vertical stack of cards,
   not a pinned horizontal strip — main.js only builds the horizontal tween from
   701px up (gsap.matchMedia), and this is the matching layout. */
@media (max-width:700px){
  .works-pin{ height:auto; overflow:visible; }
  .works-track{
    flex-direction:column;
    align-items:stretch;
    height:auto;
    gap:44px;
    padding:var(--gap) var(--pad);
  }
  .works-intro{ width:100%; padding-right:0; }
  .work-card{ width:100%; }
}

@media (max-width:600px){
  .hero-nav{ flex-direction:column; align-items:flex-start; gap:12px; }
  /* Frame 2: the four links spread edge to edge on one row, LET'S TALK on its
     own row beneath. */
  .hero-nav ul{ width:100%; justify-content:space-between; gap:0; }
  /* Stacked two-line title, sized to frame 2 where "STUDIOS." spans nearly the
     full width — 19vw is the largest size that clears the 20px side pads. */
  .hero-title{ flex-direction:column; align-items:flex-start; line-height:.94; font-size:19vw; }
  .ht-gap{ display:none; }
  .statement-copy{ font-size:clamp(21px,6.4vw,26px); }
  /* Frame 196: one big centred phone (~62vw) with the neighbours peeking in
     from both edges. The side padding is (100vw - phone)/2 so every slide can
     centre; scroll-snap does the rest. */
  .phone{ width:62vw; }
  .showcase-stage{ padding:30px 19vw; gap:6vw; margin-right:calc(var(--pad) * -1); }
  .team-cards{ grid-template-columns:1fr; }
  .cta{ min-height:64svh; }
  /* "HANO STUDIOS" is ~7em wide; at 390px with 20px side pads the biggest size
     that still shows the whole wordmark is ~12.5vw. 17vw cropped it to
     "HANO STUD" off the right edge. */
  .footer-mark{ font-size:12.5vw; }
}

/* ============================================================
   REDUCED MOTION
   ============================================================ */

@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{
    animation-duration:.001ms !important;
    animation-iteration-count:1 !important;
    transition-duration:.001ms !important;
  }
  html{ scroll-behavior:auto; }
  .reveal{ opacity:1; transform:none; }
  .wipe{ display:none; }
  /* main.js returns before building any timeline when reduced motion is set, so
     nothing is left to reveal these — without this the whole statement stays at
     opacity:0 and the copy is unreadable. Same guarantee as .no-gsap.
     Needs the .js prefix to match the specificity of the rule that hides them;
     one class alone loses to `.js .sc-line > span` whatever the order. */
  .js .sc-line > span{ opacity:1; transform:none; }
  .hero-square{ display:none; }
  /* No pin runs in this mode, so the pin's scroll-room margin would be a real
     black gap. Same specificity as the rule that sets it; later in file wins. */
  .js:not(.no-gsap) .hero{ margin-bottom:0; }
  /* Nothing scrubs .showcase-stage's scrollLeft in this mode either (main.js
     gates that pin on !reduce) — same reasoning as .no-gsap above. */
  .showcase-stage{ scroll-snap-type:x mandatory; }
}
