/* ============================================================
   Melanie Katz — Voice Over
   Layout follows Melanie's notes: name + branding left, demos right,
   then logos, then videos. No logo mark — the name is the brand.
   ============================================================ */

:root{
  --ink:#0F0E0C;
  --ink-2:#17150F;
  --ink-3:#211E16;
  --cream:#F5F1EA;
  --cream-dim:rgba(245,241,234,.62);
  --gold:#D4A94E;
  --gold-soft:rgba(212,169,78,.14);
  --paper:#FFFFFF;
  --maxw:1240px;
  --pad:clamp(20px,5vw,64px);
  --serif:"Fraunces",Georgia,"Times New Roman",serif;
  --sans:"Inter",-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;
  --ease:cubic-bezier(.2,.7,.2,1);
}

*{ box-sizing:border-box; }
html{ scroll-behavior:smooth; -webkit-text-size-adjust:100%; }
/* The header is fixed at 81px, so an anchor jump would otherwise park the
   section's first line underneath it — which is what was hiding "Trusted by".
   Offset every jump target by the header plus a little breathing room. */
:target, #top, #demos, #clients, #work, #about, #contact{ scroll-margin-top:104px; }
body{
  margin:0; background:var(--ink); color:var(--cream);
  font-family:var(--sans); font-size:17px; line-height:1.6;
  -webkit-font-smoothing:antialiased; overflow-x:hidden;
}
h1,h2,h3,p,ul,ol{ margin:0; padding:0; }
ul,ol{ list-style:none; }
a{ color:inherit; text-decoration:none; }
img{ max-width:100%; display:block; }
button{ font:inherit; color:inherit; background:none; border:0; cursor:pointer; }
.mono{ font-variant-numeric:tabular-nums; letter-spacing:.02em; }
/* Labels an icon-only link for screen readers without giving the browser an
   accessible name to build a native tooltip from — Safari pops one of those
   white boxes on any link that has a name but no visible text. */
.sr-only{
  position:absolute; width:1px; height:1px; margin:-1px; padding:0;
  overflow:hidden; clip:rect(0 0 0 0); clip-path:inset(50%); white-space:nowrap; border:0;
}
.wrap{ width:100%; max-width:var(--maxw); margin-inline:auto; padding-inline:var(--pad); }

.eyebrow{
  font-size:12px; font-weight:600; letter-spacing:.24em; text-transform:uppercase;
  color:var(--gold); margin-bottom:18px;
}

/* ---------- buttons ---------- */
.btn{
  display:inline-flex; align-items:center; gap:.6em;
  padding:14px 26px; border-radius:2px; font-weight:500; font-size:15px;
  letter-spacing:.02em; border:1px solid transparent; white-space:nowrap;
  transition:background .2s, color .2s, border-color .2s, transform .2s var(--ease);
}
.btn:hover{ transform:translateY(-2px); }
.btn--solid{ background:var(--gold); color:#1A1508; border-color:var(--gold); font-weight:600; }
.btn--solid:hover{ background:#E2BB68; }
.btn--ghost{ border-color:rgba(245,241,234,.28); color:var(--cream); }
.btn--ghost:hover{ border-color:var(--cream); background:rgba(245,241,234,.06); }

/* ---------- header ---------- */
.site-header{
  position:fixed; inset:0 0 auto 0; z-index:60;
  display:flex; align-items:center; justify-content:space-between;
  padding:18px var(--pad); border-bottom:1px solid transparent;
  transition:background .3s, padding .3s, border-color .3s;
}
.site-header.scrolled{
  background:rgba(15,14,12,.93); backdrop-filter:saturate(150%) blur(12px);
  border-bottom-color:var(--ink-3); padding-block:12px;
}
.brand{ display:flex; flex-direction:column; line-height:1.1; }
.brand__name{ font-family:var(--serif); font-size:19px; font-weight:600; }
.brand__role{
  font-size:9.5px; font-weight:600; letter-spacing:.22em; text-transform:uppercase;
  color:var(--gold); margin-top:4px;
}
/* Header contact. Sits outside .nav on purpose — .nav becomes a full-screen
   overlay behind the hamburger below 900px, and these must never be hidden
   behind a tap. Inherits .social for the gold circles. */
.hdr-contact{ margin-left:auto; margin-right:20px; gap:8px; }
/* 30% down from the 40px circles used in the contact section — the header
   versions are a quick-access strip, not the main call to action.
   Doubled up on the class because .social a is defined later in this file with
   the same specificity, so a plain .hdr-contact a loses to it. */
.hdr-contact.social a{ width:34px; height:34px; }
.hdr-contact.social svg{ width:16px; height:16px; }

/* The phone is the exception: no circle, just the icon with the number beside
   it. A producer should be able to read the number without clicking anything. */
.hdr-contact.social .hdr-contact__tel a{
  width:auto; height:auto; border:0; border-radius:0;
  gap:7px; padding:2px 0;
  display:inline-flex; align-items:center;
  font-size:14px; font-weight:600; letter-spacing:.02em;
  color:var(--gold); background:none;
}
.hdr-contact.social .hdr-contact__tel a:hover{
  background:none; color:var(--cream); transform:none;
}
.hdr-contact.social .hdr-contact__tel svg{ width:16px; height:16px; }
.hdr-contact .hdr-contact__tel{ margin-right:6px; }

.nav{ display:flex; align-items:center; gap:28px; }
.nav a{ font-size:14px; font-weight:500; color:var(--cream-dim); transition:color .2s; }
.nav a:hover{ color:var(--cream); }
.nav a.btn{ color:var(--cream); padding:10px 20px; }
.nav-toggle{ display:none; width:44px; height:44px; }
.nav-toggle span{ display:block; width:24px; height:1.5px; background:var(--cream); margin:5px auto; transition:.3s; }

/* ---------- hero: intro left, demos right ---------- */
.hero{
  /* Was clamp(120px,14vh,168px). Melanie wants the three boxes bigger AND the
     words + wave still on screen with them; growing the boxes pushed the wave
     below the fold. This is the only height in the hero that is pure whitespace,
     so it goes first — the header is ~76px tall, and 84 still clears it. */
  padding:clamp(84px,10vh,124px) 0 clamp(14px,1.5vw,20px);
  background:
    radial-gradient(85% 65% at 82% 14%, rgba(212,169,78,.13), transparent 62%),
    radial-gradient(65% 55% at 6% 92%, rgba(212,169,78,.05), transparent 62%),
    var(--ink);
}

/* The trait words and the scope used to be the last two rows of .hero__grid.
   Demos now sits between them and the photo, so they live in their own section
   below it. No background here on purpose — body is already var(--ink), and
   repeating the hero's gradients would put a second light-source under the
   black Demos band. */
.hero-tail{
  padding:clamp(16px,1.8vw,26px) 0 clamp(12px,1.4vw,20px);
}
/* One full-width column instead of the hero's three-box track list. The trait
   words need ~896px to stay on one line and the hero's columns gave them 604px.
   The scope has to be pinned back afterwards or its 72% would resolve against
   the whole 1240px wrap and it would double in size. */
/* Narrower than the hero above it, not wider. The hero row now runs to 1320 and
   the trait words were sprawling the full width beneath it; pulling this block
   in makes the words and the wave read as one tight unit under the three boxes. */
.hero-tail .hero__grid{ grid-template-columns:1fr; row-gap:6px; max-width:1040px; }
.hero-tail .wave-rule{
  width:min(380px, 64%);
  /* The scope's height IS the amplitude of the waveform, so this is the last
     thing to shrink — but it is also the only give left once the boxes above
     grew. The 20vh term makes it yield on SHORT windows (where the wave would
     otherwise fall below the fold) while leaving the full 185px on anything tall
     enough to show it, so "it all comes up together" holds without permanently
     flattening the animation for everyone. */
  height:clamp(110px, min(13vw, 20vh), 185px);
  margin:clamp(6px,0.8vw,12px) auto 0;
}
/* the tail already ends in whitespace; the band's full margin on top of it read
   as a gap twice the size of any other section seam */
.hero-tail + .band{ margin-top:clamp(20px,2.4vw,34px); }
/* Three columns: photo, name, demos. */
.hero__grid{
  /* Photo left, card right, both 30% down from 460/420. The type inside the
     card is scaled by the same factor (--card-scale) — shrinking the box alone
     would just push every line into wrapping. */
  --card-scale:.85;
  /* Wider than the site's 1240 .wrap, on purpose. At 1240 the three boxes came
     to 1082 of a 1112 usable width — 30px of slack, so they could not grow at
     all. This overrides .wrap's max-width (same specificity, declared later) to
     give the hero row its own wider container; every other section stays at
     1240. 1320 − 128 padding = 1192 usable, less two 46px gaps = 1100.
     All three stretch to a common height (see .hero__photo) — that is what keeps
     the photo and the card exactly level, rather than any fixed number here. */
  max-width:1320px;
  display:grid;
  /* card | photo | demos — the widths follow that order, so the card keeps the
     widest track for the name and the photo sits centred between the two. */
  grid-template-columns:minmax(0,385px) minmax(0,340px) minmax(0,375px);
  justify-content:center;
  /* row gap kept tight — it is the space between the boxes and the trait words;
     the column gap between the three boxes stays wide */
  gap:clamp(8px,1vw,14px) clamp(26px,3.2vw,48px); align-items:stretch;
}
.hero__name{
  font-family:var(--serif); font-weight:300;
  font-size:calc(clamp(46px,5.4vw,82px) * var(--card-scale,1)); line-height:.9; letter-spacing:-.02em;
}
.hero__name em{ font-style:italic; font-weight:400; color:var(--gold); }
/* Sits between the name and the tagline. Deliberately cream rather than gold —
   the rotating roles just below are already gold, and two gold lines stacked
   under the name would fight each other. */
.hero__place{
  margin-top:14px;
  font-size:calc(11.5px * var(--card-scale,1)); font-weight:700; letter-spacing:.20em;
  text-transform:uppercase; color:#C08BFF;
}
/* Once the whole hero has settled, the line lights like a sign: a few flickers,
   then it keeps breathing. Two animations chained — the flicker runs once, and
   the pulse starts as it ends and continues. The pulse is listed second so it
   wins the text-shadow once its delay is up. */
.hero__place.is-lit{
  animation:vegasNeon 2.4s var(--ease) both,
            vegasPulse 4.2s ease-in-out 2.4s infinite;
}
@keyframes vegasNeon{
  0%   { text-shadow:none; }
  5%   { text-shadow:0 0 26px rgba(192,139,255,1), 0 0 58px rgba(192,139,255,.7); }
  9%   { text-shadow:none; }
  15%  { text-shadow:0 0 30px rgba(192,139,255,1), 0 0 70px rgba(192,139,255,.8); }
  20%  { text-shadow:0 0 4px rgba(192,139,255,.25); }
  28%  { text-shadow:0 0 34px rgba(192,139,255,1), 0 0 80px rgba(192,139,255,.85); }
  100% { text-shadow:0 0 20px rgba(192,139,255,.75), 0 0 46px rgba(192,139,255,.4); }
}
/* the resting breath — never fully off, so it reads as a lit sign not a blink */
@keyframes vegasPulse{
  0%,100% { text-shadow:0 0 18px rgba(192,139,255,.62), 0 0 40px rgba(192,139,255,.3); }
  50%     { text-shadow:0 0 34px rgba(192,139,255,1),   0 0 76px rgba(192,139,255,.6); }
}
@media (prefers-reduced-motion:reduce){
  .hero__place.is-lit{ animation:none; text-shadow:0 0 20px rgba(192,139,255,.7); }
}
.hero__tagline{
  font-family:var(--serif); font-size:calc(clamp(19px,2.1vw,26px) * var(--card-scale,1)); font-style:italic;
  font-weight:300; line-height:1.3; margin-top:22px; color:var(--cream);
  max-width:22ch;
}
.hero__credits{
  position:relative; height:1.6em; margin-top:20px;
  font-size:calc(11.5px * var(--card-scale,1)); letter-spacing:.16em; text-transform:uppercase; color:var(--gold);
}
.hero__credits span{
  position:absolute; left:0; top:0; white-space:nowrap;
  opacity:0; transform:translateY(5px);
  transition:opacity .45s var(--ease), transform .45s var(--ease);
}
/* incoming waits for the outgoing to clear, so they never overlap at full strength */
.hero__credits span.is-on{ opacity:1; transform:none; transition-delay:.35s; }
/* reduced motion: show them all, wrapped, no cycling */
.hero__credits.is-static{ position:static; height:auto; display:flex; flex-wrap:wrap; gap:6px 0; }
.hero__credits.is-static span{ position:static; opacity:1; transform:none; }
.hero__credits.is-static span + span::before{ content:"•"; margin:0 10px; color:rgba(212,169,78,.5); }
.hero__lead{
  margin-top:calc(22px * var(--card-scale,1)); color:var(--cream-dim);
  font-size:calc(16.5px * var(--card-scale,1)); line-height:1.75;
}
/* the number carries the credential, so it gets the display face and weight */
.hero__years{
  font-family:var(--serif); font-weight:600; font-size:2.1em;
  color:var(--cream); line-height:1; letter-spacing:-.02em;
  margin-right:2px; display:inline-block;
  font-variant-numeric:tabular-nums;   /* stops the line jittering as digits change */
}
.hero__years.is-counting{ color:var(--gold); }
.hero__years.is-done{ animation:yearPop .7s var(--ease); }
@keyframes yearPop{
  0%{ transform:scale(1); }
  40%{ transform:scale(1.22); color:var(--gold); }
  100%{ transform:scale(1); }
}
.hero__cta{ display:flex; flex-wrap:wrap; gap:12px; margin-top:30px; }

/* ---------- hero entrance ----------
   A staged curtain-up on first paint, kept distinct from the gestures already
   on the page: the three panels rise, the name wipes up from behind its own
   baseline, the tagline follows, and the photo settles out of a slow zoom.
   Nothing here touches the roles rotator, the 30 count-up or the trait blur —
   those still run exactly as before, layered on top.
   main.js adds .is-in on load; an observer is unreliable for content already on
   screen at first paint, which is why the traits are load-driven too. */
.hero__intro, .hero__photo, .hero__demos{
  opacity:0; transform:translateY(26px);
  transition:opacity 1.1s var(--ease), transform 1.1s var(--ease);
}
.hero.is-in .hero__intro{ opacity:1; transform:none; transition-delay:.05s; }
.hero.is-in .hero__photo{ opacity:1; transform:none; transition-delay:.18s; }
.hero.is-in .hero__demos{ opacity:1; transform:none; transition-delay:.31s; }

/* The mask. Padding plus a matching negative margin gives the italic and the
   tall caps room inside the clip without shifting the layout by a pixel. */
.hero__name .line{
  display:block; overflow:hidden;
  padding:.08em .1em; margin:-.08em -.1em;
}
.hero__name .line > *{
  display:block; transform:translateY(125%);
  transition:transform 1.15s var(--ease);
}
.hero.is-in .hero__name .line > *{ transform:none; }
.hero.is-in .hero__name .line:nth-child(1) > *{ transition-delay:.34s; }
.hero.is-in .hero__name .line:nth-child(2) > *{ transition-delay:.46s; }

.hero__tagline, .hero__place{
  opacity:0; transform:translateY(14px);
  transition:opacity .95s var(--ease), transform .95s var(--ease);
}
.hero.is-in .hero__place{ opacity:1; transform:none; transition-delay:.62s; }
.hero.is-in .hero__tagline{ opacity:1; transform:none; transition-delay:.76s; }

/* the photo eases out of a slow zoom rather than simply fading in */
.hero__photo img{ transform:scale(1.08); transition:transform 1.9s var(--ease); }
.hero.is-in .hero__photo img{ transform:none; transition-delay:.18s; }

@media (prefers-reduced-motion:reduce){
  .hero__intro, .hero__photo, .hero__demos, .hero__tagline, .hero__place{
    opacity:1; transform:none; transition:none;
  }
  .hero__name .line > *{ transform:none; transition:none; }
  .hero__photo img{ transform:none; transition:none; }
}

/* Trait words under the three hero boxes. Each one resolves out of a blur, in
   turn, and stays put — an entrance, not a loop. */
.traits{
  grid-column:1/-1; order:3;                 /* span beneath all three hero boxes */
  display:flex; flex-wrap:wrap; justify-content:center;
  /* The seven words measure ~670px of text, so the gaps alone decide whether
     they make one line — the original 2.6vw added 224px and forced a second row.
     Tightened again so the whole run sits narrower under the hero. Still a
     clamp, so they wrap rather than overflow once the viewport is truly narrow. */
  gap:10px clamp(10px,1.15vw,18px);
  margin-top:clamp(4px,0.5vw,8px);
}
.traits li{
  font-family:var(--serif); font-style:italic; font-weight:400;
  font-size:clamp(17px,1.8vw,23px); color:var(--gold);
  opacity:0; filter:blur(18px); transform:translateY(10px);
  transition:opacity 1.4s var(--ease), filter 1.4s var(--ease), transform 1.4s var(--ease);
}
/* The words are buttons now — they inherit the italic serif look, and turn
   purple on hover to say they do something. */
.traits .trait{
  font: inherit; color: inherit; letter-spacing: inherit;
  background: none; border: 0; padding: 0; cursor: pointer;
  transition: color .22s var(--ease), text-shadow .22s var(--ease);
}
.traits .trait:hover, .traits .trait:focus-visible{
  color: #C08BFF; text-shadow: 0 0 18px rgba(192,139,255,.55); outline: none;
}
.traits .trait.is-cued{ color: #C08BFF; text-shadow: 0 0 22px rgba(192,139,255,.7); }

.traits li.is-on{
  opacity:1; filter:blur(0); transform:none;
  /* white while it is still out of focus, still white at the moment it lands
     (1.4s), then it cools down into the gold */
  animation:traitSettle 2.4s var(--ease) forwards;
}
@keyframes traitSettle{
  0%,58%{ color:#fff; text-shadow:0 0 20px rgba(255,255,255,.30); }
  100%  { color:var(--gold); text-shadow:none; }
}
@media (prefers-reduced-motion:reduce){
  .traits li{ opacity:1; filter:none; transform:none; transition:none; }
  /* The words are buttons now — they inherit the italic serif look, and turn
   purple on hover to say they do something. */
.traits .trait{
  font: inherit; color: inherit; letter-spacing: inherit;
  background: none; border: 0; padding: 0; cursor: pointer;
  transition: color .22s var(--ease), text-shadow .22s var(--ease);
}
.traits .trait:hover, .traits .trait:focus-visible{
  color: #C08BFF; text-shadow: 0 0 18px rgba(192,139,255,.55); outline: none;
}
.traits .trait.is-cued{ color: #C08BFF; text-shadow: 0 0 22px rgba(192,139,255,.7); }

.traits li.is-on{ animation:none; }
}

/* ---------- hero photo ---------- */
/* Uncropped: the frame carries the source image's own 3:4, so cover has
   nothing to cut. Her head fills most of this 480x640 frame and every earlier
   crop attempt clipped her face. */
/* The photo now matches the demo panel's height. Filling a TALLER, narrower box
   crops horizontally, not vertically — so her chin is never at risk; it only
   trims a little of the mic side. */
.hero__photo{
  order:2;                       /* centre box: card 1 | photo 2 | demos 3 */
  /* Height comes from the grid ROW — which the card beside it defines — so the
     two boxes always end up exactly the same height at every viewport.
     This was align-self:center + aspect-ratio:480/640, which made the height a
     function of the TRACK WIDTH. That can only match the card at the one
     viewport the width happens to be tuned for, and drifts everywhere else.
     The img MUST be absolutely positioned for this to work: left in flow, its
     intrinsic height feeds back into the row and the row just grows to fit it
     again, which is the loop that made every fixed-width attempt fail. */
  align-self:stretch; min-height:0; position:relative;
  margin:0; border:1px solid var(--ink-3);
  width:100%; overflow:hidden;
}
.hero__photo img{
  position:absolute; inset:0;                    /* see .hero__photo — keeps the row height off this element */
  width:100%; height:100%; display:block;
  object-fit:cover; object-position:8% center;   /* her face is left of frame — crop from the mic side */
  filter:saturate(.94) contrast(1.02);
}
/* Panelled to match the demos, and set flush right so the text runs up against
   the photo in the centre. */
.hero__intro{
  order:1; align-self:stretch;   /* stretch so all three hero boxes share one height */
  display:flex; flex-direction:column; justify-content:center;
  /* right-justified: the card sits left of the photo now, so the type ranges
     against the photo rather than away from it */
  text-align:right;
  border:1px solid var(--ink-3); border-radius:3px;
  background:linear-gradient(180deg, rgba(245,241,234,.035), rgba(245,241,234,.012));
  padding:26px 24px 30px;
}
.hero__intro .hero__tagline{ max-width:none; margin-left:auto; margin-right:0; }
.hero__intro .hero__credits{ justify-content:flex-end; }
/* the rotating role is absolutely positioned — it has to be pinned to the right
   edge too, or it alone would stay left while everything above it ranges right */
.hero__intro .hero__credits span{ left:auto; right:0; }

/* ---------- demos (in the hero, compact) ---------- */
/* The demos sit in their own panel so the column reads as a module rather than
   loose icons floating beside the name. */
.hero__demos{
  order:3; align-self:stretch;
  display:flex; flex-direction:column;
  border:1px solid var(--ink-3); border-radius:3px;
  background:linear-gradient(180deg, rgba(245,241,234,.035), rgba(245,241,234,.012));
  padding:22px 20px 24px;
}
/* In the hero the demos are a vertical list, so each one is a single compact
   row — icon, title, download — not the wide band's stacked tile. Five stacked
   tiles run ~560px and would tower over the card beside it, dragging the shared
   row height (and so the photo) with them. */
.hero__demos .demos__label{
  font-size:15px; margin:0 0 14px; padding:0; border-bottom:0;
}
.hero__demos .demo-list{
  display:flex; flex-direction:column; gap:11px;
  max-width:none; margin:0; justify-content:center; flex:1 1 auto;
}
.hero__demos .demo{
  max-width:none; width:100%;
  flex-wrap:nowrap; justify-content:flex-start; text-align:left;
  column-gap:11px;
}
.hero__demos .demo__play{ width:40px; height:40px; }
.hero__demos .demo__play svg{ width:17px; height:17px; }
/* overrides the band's flex:1 0 100%, which exists there to force its own row */
.hero__demos .demo__title{ order:2; flex:1 1 auto; font-size:11.5px; line-height:1.35; }
.hero__demos .demo__dl{ order:3; flex:none; }
/* Sits under the list inside the panel. Left-ranged, not centred — centring a
   four-line block in a 330px column reads as ragged rather than deliberate.
   flex:none so it can't be stretched by the column's justify-content:center. */
.hero__demos .ai-notice{
  flex:none; margin:16px 0 0; font-size:9.5px; line-height:1.5; text-align:left;
}
/* matched to .clients__label ("Trusted by") */
.demos__label{
  /* Larger than the other section labels (15px) — the demos are what a producer
     comes for — but not so large it dominates the hero. */
  font-size:20px; font-weight:600; letter-spacing:.28em; text-transform:uppercase;
  color:var(--gold); text-align:center;
  /* Tried aligning these cap-tops with the M of Melanie. It does not work in
     this layout: the demos panel already fills its row, so the shift has to
     make the hero taller, and it left dead space at the top of this panel and
     under the intro. Reverted. */
  margin-top:0; margin-bottom:22px;
  padding-bottom:18px; border-bottom:1px solid var(--ink-3);
}
/* Two rows on purpose: the two long titles share the top row at half width
   each, the three short ones sit together below. A six-column grid gives that
   exactly — 3+3 above, 2+2+2 below — and the extra width is what stops the
   labels stacking three and four lines deep. */
/* All five across one row. The old 3+2 split with its column spans existed
   only because the demos were squeezed into a hero column — with a band of
   their own they simply share five equal tracks. */
.demo-list{
  display:grid; grid-template-columns:repeat(5,1fr);
  gap:0 clamp(14px,2.4vw,40px); justify-items:center;
  max-width:1040px; margin-inline:auto;
}
.demo{ width:100%; max-width:190px; }
/* Play and download sit side by side on the top row, title spanning beneath.
   Done with grid areas rather than reordering the markup, because the DOM order
   (play → title → download) is the order a screen reader and keyboard should
   still get: the control, what it plays, then the extra. */
.demo{
  display:flex; flex-wrap:wrap;
  justify-content:center; align-items:center; text-align:center;
  column-gap:8px; row-gap:8px; cursor:pointer;
}
/* order, not markup order: the DOM stays play → title → download so a screen
   reader and the tab key still get the control, then what it plays, then the
   extra. Grid was the obvious tool here and it was wrong — the title spanning
   both columns stretched them to its own 180px, which shoved the download 57px
   clear of the play button and stretched the link's hit area to the column. */
.demo__play { order:1; }
.demo__dl   { order:2; }
.demo__title{ order:3; flex:1 0 100%; }   /* full-width => forces its own row */
.demo__play{
  width:56px; height:56px; border-radius:50%;
  border:1px solid rgba(245,241,234,.24); color:var(--cream);
  display:grid; place-items:center; flex:none;
  transition:background .25s, border-color .25s, color .25s, transform .25s var(--ease);
}
.demo__play svg{ width:24px; height:24px; fill:currentColor; margin-left:2px; }
/* One button, two glyphs — the pause bars replace the triangle while that demo
   is sounding, so the control says what it will do rather than what it did. */
.demo__play .i-pause{ display:none; }
.demo.is-playing .demo__play .i-play{ display:none; }
.demo.is-playing .demo__play .i-pause{ display:inline; }
/* the bars are optically centred, unlike the triangle */
.demo.is-playing .demo__play svg{ margin-left:0; }
.demo.is-playing .demo__play{
  background:var(--gold); border-color:var(--gold); color:#1A1508;
}
/* Download the MP3 — a producer pulling a demo to send on, without needing to
   ask for an audition. Quiet until hovered so it never competes with play. */
.demo__dl{
  display:inline-flex; align-items:center; gap:6px;
  font-size:11px; letter-spacing:.14em; text-transform:uppercase;
  color:rgba(245,241,234,.42);
  padding:4px 2px; transition:color .22s;
}
.demo__dl svg{ width:15px; height:15px; }
.demo__dl:hover, .demo__dl:focus-visible{ color:var(--gold); }
.demo:hover .demo__dl{ color:rgba(245,241,234,.68); }
.demo:hover .demo__dl:hover{ color:var(--gold); }
/* The triangle picks up the purple on hover — the svg is fill:currentColor, so
   the colour here drives it. The ring stays gold so the button still reads as
   part of the gold system. */
.demo:hover .demo__play{ border-color:var(--gold); color:#C08BFF; transform:scale(1.07); }
.demo:hover .demo__play svg{ filter:drop-shadow(0 0 7px rgba(192,139,255,.65)); }
/* playing beats hovering — a gold triangle on the gold fill would vanish */
.demo.is-active:hover .demo__play{ color:#1A1508; }
.demo.is-active:hover .demo__play svg{ filter:none; }
.demo.is-active .demo__play{
  background:var(--gold); border-color:var(--gold); color:#1A1508;
  box-shadow:0 0 0 6px rgba(212,169,78,.12);
}
.demo__title{
  /* was capped at 14ch, which is what forced the long titles three and four
     lines deep — the tile width governs the wrap now */
  max-width:100%;
  font-size:13px; font-weight:600; line-height:1.4; letter-spacing:.02em;
  color:var(--cream-dim); text-transform:uppercase;
  transition:color .25s;
}
.demo:hover .demo__title, .demo.is-active .demo__title{ color:var(--cream); }

.ai-notice{
  margin-top:22px; font-size:10.5px; line-height:1.5;
  color:rgba(245,241,234,.42); text-align:center;
}

/* The gold rules on both black bands fade out to nothing at the left and right
   rather than butting into the viewport edge. Drawn as pseudo-elements because
   a border can't carry a gradient; the 9%/91% stops match the marquee's own
   mask, so a logo and the rules above and below it fade over the same span. */
.clients::before, .clients::after,
.band::before,    .band::after,
.about__panel::before, .about__panel::after{
  content:""; position:absolute; left:0; right:0; height:2px; pointer-events:none;
  background:linear-gradient(90deg, transparent, var(--gold) 9%, var(--gold) 91%, transparent);
}
/* The two black bands touch, so one rule serves as the divider between them.
   Videos come first now, so its bottom rule is the seam and the logo band's own
   top rule is suppressed — otherwise the two stack into a double line. */
.clients::before{ display:none; }
.band::before, .about__panel::before{ top:0; }
.clients::after,  .band::after,  .about__panel::after{ bottom:0; }

/* ---------- clients: dark band, endless marquee ---------- */
.clients{
  background:#000; padding:34px 0 38px;
  position:relative; overflow:hidden;
}
/* one style for every black-band label: TRUSTED BY, ON SCREEN */
.clients__label, .band__label{
  font-size:15px; font-weight:600; letter-spacing:.28em; text-transform:uppercase;
  color:var(--gold); text-align:center; margin-bottom:28px;
}
/* ABOUT MELANIE runs a touch larger than the other section labels at Melanie's
   request — TRUSTED BY and SELECTED WORK stay at 15px — and carries the same
   purple neon as "Based in Las Vegas": one flicker on arrival, then a resting
   breath. Reuses the vegasNeon / vegasPulse keyframes rather than duplicating
   them, so the two stay in step if either is retuned. */
#about .band__label{ font-size:18px; color:#C08BFF; }
#about .band__label.is-lit{
  animation:vegasNeon 2.4s var(--ease) both,
            vegasPulse 4.2s ease-in-out 2.4s infinite;
}
@media (prefers-reduced-motion:reduce){
  #about .band__label.is-lit{ animation:none; text-shadow:0 0 20px rgba(192,139,255,.7); }
}
.marquee{
  position:relative; overflow:hidden;
  cursor:grab; touch-action:pan-y;   /* drag to scrub; vertical swipes still scroll the page */
  user-select:none; -webkit-user-select:none;
  /* fade the ends so logos enter and leave rather than being chopped */
  -webkit-mask-image:linear-gradient(90deg, transparent, #000 9%, #000 91%, transparent);
  mask-image:linear-gradient(90deg, transparent, #000 9%, #000 91%, transparent);
}
.marquee.is-dragging{ cursor:grabbing; }
.marquee__track{
  display:flex; align-items:center; gap:clamp(38px,5vw,72px);
  width:max-content; will-change:transform;
}
.marquee__track img{ -webkit-user-drag:none; user-drag:none; }
.marquee__track img{
  height:46px; width:auto; max-width:none; object-fit:contain;
  opacity:.88; transition:opacity .25s;
}
/* Apple TV, Splunk, TCA and Lionsgate are near-black marks meant for light
   backgrounds — measured luminance 0.00 to 0.30, so in full colour they vanish
   here. Reversing them to white is what each brand's own guidelines specify for
   dark backgrounds. Add `class="reverse"` to any future logo that's too dark. */
/* Wide wordmarks that carry too much optical weight at the 46px default. The
   class names are the first cut each one took; the comment is the running total,
   since several have been trimmed again since. */
.marquee__track img.sm{ height:33px; }    /* Lionsgate: 28% down from 46px */
.marquee__track img.sm15{ height:35px; }  /* Launch: 24% down from 46px */
.marquee__track img.sm10{ height:41px; }  /* Workato, Ellucian: 10% down from 46px */
/* Square/tall marks (Maker, Porsche, Warner Bros, TCA) need more height than a
   wordmark to carry the same optical weight in the row. */
.marquee__track img.tall{ height:64px; }
.marquee__track img.reverse{ filter:brightness(0) invert(1); opacity:.8; }
/* First load only: the row starts dark and each logo fades up in turn, left
   edge rightward. The loop is NOT held — the row is already travelling, so the
   logos fade and move at once. main.js adds .is-intro at parse time and removes
   it once the sweep is done (which also reveals anything still off-screen), so
   with JS disabled the logos simply render as normal. */
.marquee__track li{ transition:opacity 1.6s var(--ease); }
.marquee__track.is-intro li{ opacity:0; }
.marquee__track.is-intro li.up{ opacity:1; }

@media (prefers-reduced-motion:reduce){
  /* reduced motion: no loop, no drag — just a static wrapped row */
  .marquee__track{ flex-wrap:wrap; justify-content:center; width:auto; transform:none !important; }
  .marquee{ -webkit-mask-image:none; mask-image:none; cursor:default; }
  .marquee__track li, .marquee__track.is-intro li{ opacity:1; transition:none; }
}

/* ---------- sections ---------- */
.section{ padding:clamp(64px,8.5vw,116px) 0; }
/* About follows the video band directly — it doesn't need the band's gap AND a
   full section pad on top of it. */
#about{ padding-top:clamp(40px,4.5vw,64px); }
.section--alt{ background:var(--ink-2); }
/* About runs straight into the contact block; the two default section paddings
   stacked up to ~250px of empty band between the last spec and LET'S CHAT. */
.section--alt{ padding-bottom:clamp(28px,3.2vw,44px); }
.section-head{ max-width:640px; margin-bottom:clamp(34px,4.5vw,56px); }
.section-head h2, .about h2, .cta h2{
  font-family:var(--serif); font-weight:300;
  font-size:clamp(34px,5vw,60px); line-height:1.04; letter-spacing:-.02em;
}

/* the work section is a black band like the logo wall */
/* The waveform rule occupying the gap between the two black bands. Full width,
   masked at both ends on the same 9%/91% stops as the band rules and the
   marquee, so it dissolves rather than stopping dead. */
.wave-rule{
  /* LAYOUT PREVIEW: last row of the hero grid, spanning all three columns so
     it runs below her photo. */
  grid-column:1/-1; order:4;
  width:min(860px, 72%); margin:clamp(10px,1.4vw,20px) auto 0;
  height:clamp(150px,16vw,215px);   /* taller strip = bigger absolute swing */
  /* a long dissolve — fully transparent at both edges, so it reads as the rule
     emerging out of the background rather than being cut off by a mask edge */
  -webkit-mask-image:linear-gradient(90deg, transparent 0%, #000 34%, #000 66%, transparent 100%);
  mask-image:linear-gradient(90deg, transparent 0%, #000 34%, #000 66%, transparent 100%);
  /* Rises as it fades, starting on the same beat as "Warm". Slower than the
     words on purpose — 1.9s against their 285ms steps — so it dissolves in
     underneath the whole run rather than resolving before the row finishes.
     Sequenced from JS, not by the .reveal observer; see the scope-entrance
     block in main.js for why. */
  opacity:0; transform:translateY(14px);
  transition:opacity 1.9s var(--ease), transform 1.9s var(--ease);
}
.wave-rule.is-in{ opacity:1; transform:none; }
/* The transport that used to live in the sticky pop-up. Hidden until a demo is
   playing, so the rule is a plain divider the rest of the time. */
.wave-rule__meta{
  display:flex; align-items:center; justify-content:center; gap:14px;
  margin-top:8px; font-size:13px; color:var(--cream-dim);
  opacity:0; transform:translateY(-6px); pointer-events:none;
  transition:opacity .4s var(--ease), transform .4s var(--ease);
}
.wave-rule.is-open .wave-rule__meta{ opacity:1; transform:none; pointer-events:auto; }
.wave-rule__btn{
  width:30px; height:30px; border-radius:50%; flex:0 0 auto;
  border:1px solid var(--gold); color:var(--gold);
  display:grid; place-items:center; font-size:11px; line-height:1;
  transition:background .2s, color .2s;
}
.wave-rule__btn:hover{ background:var(--gold); color:#1A1508; }
.wave-rule__title{ font-weight:600; color:var(--cream); white-space:nowrap; }
.wave-rule__time{ font-variant-numeric:tabular-nums; }
.wave-rule__close{ color:var(--cream-dim); font-size:13px; padding:4px 6px; }
.wave-rule__close:hover{ color:var(--cream); }
/* only a scrubber while something is playing */
.wave-rule.is-open canvas{ cursor:pointer; }
.wave-rule canvas{
  display:block; width:100%; height:100%;
  /* the faint zero line a scope traces against */
  background:linear-gradient(180deg, transparent calc(50% - 0.5px),
             rgba(212,169,78,.14) calc(50% - 0.5px), rgba(212,169,78,.14) calc(50% + 0.5px),
             transparent calc(50% + 0.5px));
}
/* Idle <-> live swap: it drops away, is repainted while invisible, then springs
   back. transform is on the canvas so the CSS mask on the host is unaffected. */
.wave-rule canvas{
  transition:opacity .34s var(--ease), transform .34s var(--ease);
  transform-origin:50% 50%;
}
.wave-rule.is-out canvas{ opacity:0; transform:scaleY(.28); }
.wave-rule.is-pop canvas{ animation:waveRulePop .68s var(--ease); }
@keyframes waveRulePop{
  0%   { opacity:0; transform:scaleY(.28); }
  46%  { opacity:1; transform:scaleY(1.22); }
  72%  { transform:scaleY(.94); }
  100% { opacity:1; transform:scaleY(1); }
}
@media (prefers-reduced-motion:reduce){
  .wave-rule canvas{ transition:none; }
  .wave-rule.is-out canvas{ opacity:1; transform:none; }
  .wave-rule.is-pop canvas{ animation:none; }
}
@media (prefers-reduced-motion:reduce){
  .wave-rule{ opacity:1; transition:none; }
}

/* The demos band. Same black + gold rules as the videos, and it butts straight
   onto them so the two read as one block of samples. */
/* .band.demos-band, not .demos-band: the element carries BOTH classes, and the
   .band rule sits later in this file with the same specificity — so a plain
   .demos-band padding silently lost to it and never applied at all. */
.band.demos-band{ padding:26px 0 30px; margin-top:clamp(30px,3.6vw,52px); }
.demos-band .demos__label{ border-bottom:0; padding-bottom:0; margin-bottom:18px; }
/* One line. The 66ch measure was sized for the old narrow hero column; in a
   full-width band the sentence fits across, and wrapping it read as a stray
   second line rather than a paragraph. */
.demos-band .ai-notice{ max-width:none; margin:16px auto 0; text-align:center; white-space:nowrap; }
@media (max-width:1100px){
  /* below this it genuinely does not fit — let it wrap rather than overflow */
  .demos-band .ai-notice{ white-space:normal; max-width:70ch; }
}
/* one rule at the seam between demos and videos */
.demos-band + .band{ margin-top:0; }
.demos-band + .band::before{ display:none; }

/* breathing room so the two black bands read as separate blocks rather than
   one continuous slab */
.band{
  background:#000; padding:40px 0 46px;
  position:relative;
  margin-top:clamp(30px,3.6vw,52px); margin-bottom:0;   /* clears the hero */
}

/* ---------- video carousel (3D coverflow) ---------- */
.carousel{
  position:relative; max-width:1180px; margin-inline:auto;
  padding:0 clamp(0px,4vw,54px); overflow:hidden;
}
/* the stage: side slides fly out past this box, which is why the parent clips */
.car__viewport{
  position:relative; width:min(480px,69%); margin-inline:auto;   /* 20% down from 600px/86% */
  aspect-ratio:16/9; perspective:1500px; perspective-origin:50% 50%;
}
.car__track{
  position:absolute; inset:0; margin:0; padding:0;
  transform-style:preserve-3d;
}
.car__slide{
  position:absolute; inset:0; list-style:none;
  transition:transform .62s var(--ease), opacity .62s var(--ease), filter .62s var(--ease);
  backface-visibility:hidden; will-change:transform,opacity;
}
/* only the front slide gets full contrast; the rest sit back and dim */
.car__slide:not(.is-front){ filter:saturate(.55) brightness(.72); cursor:pointer; }
.car__slide:not(.is-front) .video{ pointer-events:none; }   /* click selects, not plays */
.car__slide.is-front .video{ box-shadow:0 26px 60px -24px rgba(0,0,0,.85); }

/* Type tag. Melanie's note: some of these are show work and two are
   commercials, and the section title alone could not carry that distinction. */
.video__kind{
  position:absolute; top:10px; left:10px; z-index:4;
  font-style:normal; font-size:10px; font-weight:600;
  letter-spacing:.16em; text-transform:uppercase;
  padding:5px 9px; border-radius:2px;
  background:rgba(10,9,7,.82); backdrop-filter:blur(4px);
  border:1px solid rgba(212,169,78,.5); color:var(--gold);
}
.video__kind.kind--commercial{ border-color:rgba(150,215,255,.55); color:#96D7FF; }
.video__kind.kind--corporate{ border-color:rgba(245,241,234,.4); color:rgba(245,241,234,.8); }
/* the tag belongs to the front slide — behind it, it is just clutter */
.car__slide:not(.is-front) .video__kind{ opacity:0; transition:opacity .4s var(--ease); }

.car__nav{
  position:absolute; top:50%; transform:translateY(-50%); z-index:20;
  width:46px; height:46px; border-radius:50%;
  border:1px solid var(--ink-3); background:rgba(15,14,12,.8);
  color:var(--cream-dim); display:grid; place-items:center;
  transition:color .2s, border-color .2s, background .2s;
}
.car__nav svg{ width:20px; height:20px; }
.car__nav:hover{ color:var(--gold); border-color:var(--gold); background:var(--ink); }
.car__prev{ left:4px; }
.car__next{ right:4px; }

.car__dots{ display:flex; justify-content:center; gap:9px; margin-top:26px; }
.car__dot{
  width:7px; height:7px; border-radius:50%; padding:0;
  background:rgba(245,241,234,.25); transition:background .25s, transform .25s var(--ease);
}
.car__dot.is-on{ background:var(--gold); transform:scale(1.35); }

.video{
  position:relative; aspect-ratio:16/9; width:100%;
  background:var(--ink-2) center/cover no-repeat;
  display:flex; flex-direction:column; justify-content:flex-end; align-items:flex-start;
  padding:20px; text-align:left; border:1px solid var(--ink-3);
  transition:border-color .25s;
}
.video::after{
  content:""; position:absolute; inset:0;
  background:linear-gradient(to top, rgba(15,14,12,.86) 0%, rgba(15,14,12,.12) 58%, transparent 100%);
}
.video::before{
  content:"\25B6"; position:absolute; inset:0; display:grid; place-items:center;
  font-size:30px; color:var(--cream); opacity:.9; z-index:2;
  transition:opacity .25s, transform .25s var(--ease), color .25s, text-shadow .25s;
}
.video:hover{ border-color:var(--gold); }
/* the play triangle goes purple on hover, matching the demo buttons; the frame
   stays gold so the tile still sits in the gold system */
.video:hover::before{
  opacity:1; transform:scale(1.12); color:#C08BFF;
  text-shadow:0 0 12px rgba(192,139,255,.65);
}
.video span, .video small{ position:relative; z-index:2; }
.video span{ font-size:16px; font-weight:600; }
.video small{ font-size:12px; color:var(--cream-dim); letter-spacing:.06em; text-transform:uppercase; }
.video.is-playing::before, .video.is-playing::after{ content:none; }
.video video{
  position:absolute; inset:0; width:100%; height:100%; border:0; z-index:3;
  /* contain, not cover — the three Coupa films are 640x160, and cropping them
     to 16:9 would cut the picture rather than letterbox it */
  object-fit:contain; background:#000;
}

/* ---------- about ---------- */
.about{ display:block; }
/* Selector doubled up on purpose: the shared `.about h2` rule above is (0,1,1)
   and was beating a plain `.about__h2` (0,1,0) no matter the order, so this
   headline had always been rendering at the shared 60px. */
.about h2.about__h2{
  font-family:var(--serif); font-weight:300;
  font-size:clamp(28px,3.9vw,50px); line-height:1.1; letter-spacing:-.02em;
  text-align:center; max-width:34ch; margin:0 auto clamp(34px,4vw,52px);
}
/* One column of running paragraphs. It was set in two columns because a single
   1100px measure runs to ~130 characters a line, which is unreadable — so the
   fix is not full width, it is a constrained measure: ~68 characters, centred,
   with a little more size and leading than the two-column version needed. */
.about__body{
  max-width:68ch; margin-inline:auto;
}
.about__body p{
  color:var(--cream-dim); margin:0 0 22px;
  font-size:17.5px; line-height:1.78;
}
.about__body p:last-child{ margin-bottom:0; }
.about__kicker{
  color:var(--cream); font-family:var(--serif); font-size:clamp(26px,3.1vw,38px);
  font-style:italic; text-align:center; margin:0 auto;   /* the panel's padding owns this space now */
  white-space:nowrap;
  max-width:100%;
}
/* the emphasised word carries the same purple as the Vegas line and the
   About Melanie label */
.about__kicker em{ color:#C08BFF; font-style:italic; }
/* The kicker resolves out of a blur, word by word — deliberately unlike
   anything else on the page (the traits slide, the logos fade, the 30 counts).
   main.js splits the line into .w spans and adds .kick, so with no JS the
   sentence just sits there fully legible. */
.about__kicker.kick .w{
  display:inline-block;
  opacity:0; filter:blur(18px); transform:translateY(12px);
  transition:opacity 1.5s var(--ease), filter 1.5s var(--ease), transform 1.5s var(--ease);
}
.about__kicker.kick .w.in{ opacity:1; filter:blur(0); transform:none; }
/* the gold word carries a glow while it is still out of focus */
.about__kicker.kick em.w{ text-shadow:0 0 30px rgba(212,169,78,.6); transition:opacity 1.5s var(--ease), filter 1.5s var(--ease), transform 1.5s var(--ease), text-shadow 1.9s var(--ease); }
.about__kicker.kick em.w.in{ text-shadow:none; }
@media (prefers-reduced-motion:reduce){
  .about__kicker.kick .w{ opacity:1; filter:none; transform:none; transition:none; text-shadow:none; }
}
@media (max-width:700px){ .about__kicker{ white-space:normal; } }

/* Studio Equipment · Vocal Profile · How We Connect */
/* The panel holds the pull quote and the rider as one block: full-bleed black,
   with the fading gold rules top and bottom. Its inner .wrap keeps the content
   on the same column as the copy above. */
.about__panel{
  position:relative; background:#000;
  margin-top:clamp(30px,3.6vw,46px);
  padding:clamp(18px,2vw,26px) 0 clamp(30px,3.4vw,44px);
}
.about__panel > .wrap{
  display:flex; flex-direction:column; gap:clamp(26px,3vw,38px);
}
/* Two stacked rows rather than two columns — the chips wrap on their own, and
   a column split just reintroduces the dead space this replaced. */
.about__specs{
  display:flex; flex-direction:column; gap:clamp(16px,1.8vw,22px);
}
.spec{ display:flex; flex-direction:column; align-items:center; gap:12px; }
.spec h3{
  font-size:16px; font-weight:600; letter-spacing:.22em; text-transform:uppercase;
  color:var(--gold); margin:0; text-align:center;
}
.speclist{ display:flex; flex-wrap:wrap; justify-content:center; gap:9px 10px; }
.speclist li{
  color:var(--cream); font-size:14px; line-height:1;
  padding:9px 14px; border:1px solid var(--gold); border-radius:2px;
  background:rgba(212,169,78,.07);
  transition:background .3s var(--ease), color .3s var(--ease);
}
.speclist li:hover{ background:rgba(212,169,78,.16); }
.speclist li span{ color:var(--cream); }


/* ---------- cta ---------- */
/* Carries the About section's warm tint (--ink-2) all the way down to the
   footer, so there is no tonal step between the two — the page darkens once,
   at the footer rule, rather than twice. */
.cta{
  text-align:center; background:var(--ink-2);
  padding-top:clamp(22px,2.4vw,32px); padding-bottom:clamp(30px,3.4vw,44px);
}

/* Contact entrance, on scroll into view. The headline wipes up from behind its
   baseline exactly as her name does in the hero — the two ends of the page use
   the same gesture — and everything under it follows in turn. */
.cta .eyebrow, .cta__lead, .cta__row, .cta__note{
  opacity:0; transform:translateY(16px);
  transition:opacity .9s var(--ease), transform .9s var(--ease);
}
.cta h2 .line{ display:block; overflow:hidden; padding:.08em .1em; margin:-.08em -.1em; }
.cta h2 .line > *{ display:block; transform:translateY(125%); transition:transform 1.1s var(--ease); }

.cta.is-in .eyebrow{ opacity:1; transform:none; transition-delay:.04s; }
.cta.is-in h2 .line > *{ transform:none; transition-delay:.20s; }
.cta.is-in .cta__lead{ opacity:1; transform:none; transition-delay:.50s; }
.cta.is-in .cta__row{ opacity:1; transform:none; transition-delay:.66s; }
.cta.is-in .cta__note{ opacity:1; transform:none; transition-delay:.82s; }

@media (prefers-reduced-motion:reduce){
  .cta .eyebrow, .cta__lead, .cta__row, .cta__note{ opacity:1; transform:none; transition:none; }
  .cta h2 .line > *{ transform:none; transition:none; }
}
.cta h2{ max-width:none; white-space:nowrap; }
@media (max-width:640px){ .cta h2{ white-space:normal; } }
/* Wider measure so the two sentences sit on one line on a normal screen. */
.cta__lead{ max-width:60ch; margin:20px auto 0; color:var(--cream-dim); }
/* inline-block keeps the closing sentence whole — it moves to the next line as
   a unit rather than breaking after "get back to" */
.cta__lead .keep{ display:inline-block; }
.cta__row{ display:flex; flex-wrap:wrap; gap:14px; justify-content:center; margin-top:34px; }
.cta__note{ margin-top:22px; font-size:13px; color:var(--cream-dim); letter-spacing:.06em; }

/* ---------- footer ---------- */
.site-footer{
  border-top:1px solid var(--ink-3); padding:26px var(--pad);
  display:flex; justify-content:center; align-items:center; gap:18px 26px; flex-wrap:wrap;
  font-size:13px; color:var(--cream-dim);
}

/* Social. Gold-outlined circles, matching the chips and the section rules. */
.social{ display:flex; align-items:center; gap:12px; justify-content:center; }
.cta .social{ margin-top:26px; }
.social a{
  width:40px; height:40px; border-radius:50%;
  display:grid; place-items:center;
  border:1px solid rgba(212,169,78,.55); color:var(--gold);
  transition:background .22s, color .22s, border-color .22s, transform .22s var(--ease);
}
.social a:hover, .social a:focus-visible{
  background:var(--gold); color:#1A1508; border-color:var(--gold); transform:translateY(-2px);
}
.social svg{ width:19px; height:19px; }

/* ---------- sticky player ---------- */


/* ---------- reveal ---------- */
.reveal{ transition:opacity .7s var(--ease), transform .7s var(--ease); }
html.js .reveal{ opacity:0; transform:translateY(18px); }
html.js .reveal.in{ opacity:1; transform:none; }

@media (prefers-reduced-motion:reduce){
  *{ animation:none !important; scroll-behavior:auto; }
  html.js .reveal{ opacity:1 !important; transform:none !important; }
  .btn:hover{ transform:none; }
}

/* ---------- responsive ---------- */
/* name + photo share row one, demos take the full width below */
@media (max-width:1120px){
  /* Three across needs 1062px including gaps and there is only ~1008px of usable
     width here, so the demos panel drops to its own full-width row under the
     photo and card. The track list must come back to TWO columns to do that —
     leaving three defined would strand an empty third track and throw the pair
     off centre.
     Also deliberately not fr units: the photo takes its width from its track and
     its height from the row, so an fr track stretches it into a letterbox. */
  .hero__grid{ gap:36px; grid-template-columns:minmax(0,385px) minmax(0,340px); }
  .hero__demos{ grid-column:1/-1; }
  /* Back to the wide tiles once the panel has the full width — but as an
     auto-fitting GRID, not a flex row. A flex row of five 190px tiles needs
     ~1000px and does not wrap, so on a phone it ran straight off both edges of
     the screen; and because this selector outranks the plain `.demo-list`
     two-column rule further down, that rule never got a chance to fix it.
     auto-fit needs no breakpoint arithmetic: five across when there is room,
     three, then two as the screen narrows. */
  .hero__demos .demo-list{
    display:grid;
    /* Measured, not guessed: on a 375px phone this list resolves to exactly
       293px. Two 140px columns plus a 14px gap needs 294 — one pixel over, and
       the whole thing silently collapsed to a single column of five. 128 + a
       12px gap needs 268, which leaves real headroom instead of sitting on the
       boundary. The 1fr still stretches them to fill the row. */
    grid-template-columns:repeat(auto-fit, minmax(128px, 1fr));
    gap:24px 12px;
    justify-items:center;
  }
  .hero__demos .demo{ flex-wrap:wrap; justify-content:center; text-align:center; max-width:190px; }
  .hero__demos .demo__title{ flex:1 0 100%; order:3; }
  /* 70ch is ~700px — wider than a phone, so this overflowed too. */
  .hero__demos .ai-notice{
    text-align:center; font-size:10.5px;
    max-width:min(70ch, 100%); margin-inline:auto;
  }
}
@media (max-width:760px){
  .hero__grid{ grid-template-columns:1fr; gap:32px; }
  .hero__tagline{ max-width:none; }
  .about__body{ columns:1; }

  /* Single column on a phone, so the right-alignment that pins the name to the
     photo on desktop has nothing to align against — everything centres instead,
     her photo included. */
  .hero__intro{ text-align:center; }
  .hero__intro .hero__tagline{ margin-left:auto; margin-right:auto; }
  .hero__lead{ margin-left:auto; margin-right:auto; }
  .hero__place{ text-align:center; }
  .hero__name .line > *{ text-align:center; }

  /* the rotating role sits in an absolutely positioned slot — it has to be
     stretched across the row before text-align can centre it */
  .hero__intro .hero__credits{ justify-content:center; }
  /* Must be scoped to .hero__intro to match the specificity of the desktop
     right-pin (.hero__intro .hero__credits span). As a bare .hero__credits span
     this rule loses even from inside the media query, and the rotating role is
     the one line on the card that stays right-aligned on a phone. */
  .hero__intro .hero__credits span{ left:0; right:0; text-align:center; }
  .hero__credits.is-static{ justify-content:center; }

  /* Single column here, so the photo is alone in its row and has no card to
     take a height from — stretch would collapse it to nothing (the img is
     absolutely positioned). Go back to deriving height from width. */
  .hero__photo{
    max-width:320px; margin-left:auto; margin-right:auto;
    align-self:center; aspect-ratio:480/640;
  }
}
@media (max-width:900px){
  .hdr-contact{ margin-right:12px; gap:7px; }
  .nav-toggle{ display:block; z-index:80; position:relative; }
  .nav{
    position:fixed; inset:0; background:var(--ink);
    flex-direction:column; justify-content:center; gap:28px;
    transform:translateX(100%); transition:transform .35s var(--ease); z-index:70;
  }
  .nav.open{ transform:none; }
  .nav a{ font-size:22px; color:var(--cream); }
}
@media (max-width:600px){
  /* At half a phone's width the two long titles run to three lines. Give them
     the full row so each still reads in two, and keep the short three together
     underneath. */
  /* five across is too tight on a phone — two rows instead */
  .demo-list{ grid-template-columns:repeat(2,1fr); gap:26px 14px; }
  .demo{ max-width:none; }
  /* email and phone stay; the social three would crowd the hamburger and are
     already in the contact section and the footer */
  .hdr-contact__social{ display:none; }
  .hdr-contact.social a{ width:34px; height:34px; }   /* matches desktop; comfortable tap target */
  /* the number stays readable on a phone — it fits, it just needs to stop
     touching the wordmark, so it runs a shade smaller here */
  .hdr-contact.social .hdr-contact__tel a{ font-size:12.5px; gap:5px; }
  .hdr-contact.social .hdr-contact__tel svg{ width:14px; height:14px; }
  .hdr-contact{ margin-left:14px; }
  .demo{ grid-template-columns:34px 1fr; row-gap:6px; padding:12px 13px; }
  .demo__time{ grid-column:2; }
  /* the transport wraps rather than squeezing the title off-screen */
  .wave-rule__meta{ flex-wrap:wrap; gap:8px 12px; font-size:12px; }
}
