/* ==========================================================================
   zosasounds.com — minimal, white, Apple-adjacent.
   No web fonts: the system stack renders real SF Pro on Apple devices,
   which is both the most faithful look and the fastest possible load.
   ========================================================================== */

:root{
  --white:  #ffffff;
  --grey:   #f5f5f7;   /* Apple's section grey */
  --ink:    #1d1d1f;   /* near-black */
  --muted:  #6e6e73;   /* secondary text */
  --line:   #d2d2d7;   /* hairlines */
  --blue:   #0071e3;   /* the one accent */

  --sans: -apple-system, BlinkMacSystemFont, "SF Pro Display", "SF Pro Text",
          "Helvetica Neue", "Inter", system-ui, sans-serif;
  --mono: ui-monospace, "SF Mono", SFMono-Regular, Menlo, monospace;

  --ease: cubic-bezier(.28,.11,.32,1);   /* Apple's motion curve */
  --ease-out: cubic-bezier(.16,1,.3,1);  /* long, soft landing — scroll reveals */
  --pad: clamp(1.25rem, 5vw, 2.5rem);
  --nav-h: 48px;
}

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

html{
  -webkit-text-size-adjust:100%;
  scroll-behavior:smooth;
  scroll-padding-top:calc(var(--nav-h) + 24px);
}
@media (prefers-reduced-motion: reduce){ html{ scroll-behavior:auto; } }

body{
  margin:0;
  background:var(--white);
  color:var(--ink);
  font-family:var(--sans);
  font-size:17px;
  line-height:1.53;
  letter-spacing:-.012em;
  -webkit-font-smoothing:antialiased;
  -moz-osx-font-smoothing:grayscale;
}

a{ color:var(--blue); text-decoration:none; }
a:hover{ text-decoration:underline; }

:where(a,button):focus-visible{
  outline:3px solid var(--blue);
  outline-offset:3px;
  border-radius:6px;
}

.skip{
  position:absolute; left:-9999px; top:0; z-index:99;
  background:var(--ink); color:#fff; padding:.7rem 1.1rem; font-size:.85rem;
  border-radius:0 0 10px 0;
}
.skip:focus{ left:0; top:0; }

.wrap{ max-width:1024px; margin-inline:auto; padding-inline:var(--pad); }
.wrap--narrow{ max-width:720px; }
.center{ text-align:center; }

/* ---------- nav ---------------------------------------------------------- */

.nav{
  position:sticky; top:0; z-index:60;
  height:var(--nav-h);
  background:rgba(255,255,255,.72);
  backdrop-filter:saturate(180%) blur(20px);
  -webkit-backdrop-filter:saturate(180%) blur(20px);
  border-bottom:1px solid transparent;
  transition:border-color .3s var(--ease), background .3s var(--ease);
}
.nav.is-stuck{ border-bottom-color:rgba(0,0,0,.09); }
.nav__in{
  max-width:1024px; margin-inline:auto; padding-inline:var(--pad);
  height:100%; display:flex; align-items:center; gap:1.5rem;
}
.nav__mark{
  color:var(--ink); font-size:.85rem; font-weight:600; letter-spacing:-.01em;
  display:inline-flex; align-items:center; gap:.5rem;
}
.nav__mark:hover{ text-decoration:none; opacity:.7; }

/* Visually hidden, still read aloud. Gives the avatar button a real name
   that the language switch can translate (paint() writes innerHTML, so an
   aria-label attribute would stay stuck in English). */
.vh{
  position:absolute; width:1px; height:1px; padding:0; margin:-1px;
  overflow:hidden; clip-path:inset(50%); white-space:nowrap; border:0;
}

/* The avatar button. Strip the UA button chrome and let the image be the
   whole control. Width and height are on the <img> too, so it reserves its
   box before the file arrives and the wordmark never jumps. */
.nav__me{
  appearance:none; -webkit-appearance:none;
  padding:0; border:0; background:none; line-height:0;
  flex:none; border-radius:50%; cursor:pointer;
}
.nav__me img{
  display:block; width:34px; height:34px;
  border-radius:50%; object-fit:cover;
  border:1px solid var(--line);
}
.nav__me:focus-visible{ outline:2px solid var(--blue); outline-offset:3px; }

/* ---------- project icons ------------------------------------------------ */

/* One tile per card, same size whether it holds a real mark or a letter, so
   the column still reads as a grid when a project has no icon of its own.
   Real marks are Luca's own artwork; the letter tiles are deliberately plain
   type, never an invented logo. */
.card__icon{
  display:flex; align-items:center; justify-content:center;
  width:40px; height:40px; margin:0 0 .9rem;
  border-radius:10px; overflow:hidden; flex:none;
}
.card__icon img{ display:block; width:100%; height:100%; object-fit:cover; }

/* The stand-in: the project's initial, set in the page's own type. */
.card__icon--mono{
  background:var(--grey); border:1px solid var(--line);
  font-size:1rem; font-weight:600; letter-spacing:-.02em; color:var(--muted);
}

/* A section's own mark, above its heading. */
.sec__mark{
  display:block; width:46px; height:46px; margin:0 auto 1rem;
  border-radius:11px; object-fit:cover;
}

/* A partner's wordmark in place of a heading. It is their black artwork, so
   the link drops the accent colour — recolouring someone else's mark to our
   blue would misrepresent their brand. Height scales with the h2 it replaces. */
.brandlink{ color:var(--ink); display:inline-block; }
.brandlink:hover{ opacity:.65; text-decoration:none; }
.brand{
  display:block; width:auto;
  height:clamp(2.2rem, 5.5vw, 3.2rem);
}

/* The track, pinned into the bottom-left corner. Both edges sit ON the walls:
   no offset, no radius, no shadow, so it reads as part of the window frame
   rather than a card floating near it. line-height:0 kills the inline-gap
   under the iframe that would otherwise leave a 4px sliver of page showing
   below it — the difference between touching the wall and nearly touching it.
   Below the nav (60) so the sticky header still wins. */
.player{
  position:fixed; left:0; bottom:0; z-index:50;
  width:min(400px, 100vw);
}
/* The invitation, sitting on top of the player and flush to the same wall.
   White card, hairline, muted type — no new tokens. Only the top-right corner
   is rounded, because it is the one corner not against a wall. */
.player__lead{
  margin:0; padding:.55rem .9rem;
  font-size:.78rem; line-height:1.35; color:var(--muted);
  background:var(--white);
  border-top:1px solid var(--line);
  border-right:1px solid var(--line);
  border-radius:0 12px 0 0;
}
.player__clip{ height:80px; overflow:hidden; line-height:0; }
/* Spotify rounds the card INSIDE the iframe, which we cannot restyle across
   origins — flush at 0,0 still left white slivers in the corner. So the frame
   is grown by one radius and pushed left and down past the clip: the rounded
   left and bottom corners fall outside and are cut, leaving straight edges
   hard against both walls. The top-right corner is the one not touching a
   wall, so its curve is left alone. */
.player__frame{
  display:block; border:0; border-radius:0;
  --r:12px;
  width:calc(100% + var(--r));
  height:calc(100% + var(--r));
  margin-left:calc(var(--r) * -1);
}
/* Nothing in the page should end up permanently underneath it: 80px of
   player plus the label above it. */
.foot{ padding-bottom:calc(1.75rem + 120px); }

/* The portrait itself. Sized off the viewport so it never overflows a phone,
   and built from existing tokens — white card, hairline border, ink and
   muted type. The only new value is the backdrop wash. */
.portrait{
  position:fixed; inset:0; margin:auto;
  width:min(320px, 84vw); height:max-content;
  padding:10px; border:1px solid var(--line); border-radius:24px;
  background:var(--white); color:var(--ink);
  box-shadow:0 30px 70px rgba(0,0,0,.22);
}
.portrait img{ display:block; width:100%; height:auto; border-radius:16px; }
.portrait__cap{
  margin:.8rem .3rem .3rem; text-align:center;
  font-size:.85rem; color:var(--muted);
}
.portrait::backdrop{ background:rgba(0,0,0,.3); }

/* Browsers without popover would otherwise render this as a plain div sitting
   in the page. Hide it there: no portrait beats a portrait stuck open. */
@supports not selector(:popover-open){
  .portrait{ display:none; }
}
.nav__links{ display:flex; gap:1.65rem; margin-inline:auto; }
.nav__links a{
  color:var(--ink); font-size:.78rem; opacity:.85;
  transition:opacity .25s var(--ease);
}
.nav__links a:hover{ opacity:.5; text-decoration:none; }
@media (max-width:760px){ .nav__links{ display:none; } }

.lang{
  margin-left:auto; display:flex; padding:2px; gap:2px;
  background:rgba(0,0,0,.05); border:0; border-radius:980px;
  font-family:var(--sans); cursor:pointer;
}
.lang span{
  font-size:.68rem; font-weight:500; letter-spacing:.02em;
  padding:.2rem .6rem; border-radius:980px; color:var(--muted);
  transition:background .3s var(--ease), color .3s var(--ease);
}
.lang span.on{ background:var(--white); color:var(--ink); box-shadow:0 1px 3px rgba(0,0,0,.1); }

/* ---------- Y2K switch --------------------------------------------------- */

/* "Switch" by chase2k25, https://uiverse.io/chase2k25/fluffy-dog-18 (MIT).
   The original is 150x60; every length here is that at 0.44 so it fits the
   48px nav. Greys are the original's: neumorphic, pressed into the bar.
   Hidden without JS: the checkbox can only navigate with app.js listening. */
.switch-container{ display:none; }
.js .switch-container{
  display:block; position:relative; flex:none; margin-left:.6rem;
  width:66px; height:26px; border-radius:50px;
  background:#d6d6d6;
  box-shadow:inset -4px -4px 7px #ffffff, inset 4px 4px 7px #b0b0b0;
}
/* The hint in the empty half of the track; the knob covers it once on. */
.switch-container::after{
  content:"Y2K"; position:absolute; right:7px; top:50%; transform:translateY(-50%);
  font:600 .52rem/1 var(--sans); letter-spacing:.06em; color:#8a8a8a; pointer-events:none;
}
.toggle-checkbox{ display:none; }
.switch{
  position:absolute; top:50%; left:0; width:100%; height:100%; transform:translateY(-50%);
  border-radius:50px; overflow:hidden; cursor:pointer; z-index:1;
}
.toggle{
  position:absolute; width:35px; height:22px; top:2px; left:2px;
  background:linear-gradient(145deg, #d9d9d9, #bfbfbf); border-radius:50px;
  box-shadow:-2px -2px 4px #ffffff, 2px 2px 4px #b0b0b0;
  transition:all .3s ease-in-out;
  display:flex; align-items:center; justify-content:flex-start; padding-left:5px;
}
.led{
  width:5px; height:5px; background:grey; border-radius:50%;
  box-shadow:0 0 5px 1px rgba(0,0,0,.2);
  transition:all .3s ease-in-out;
}
.toggle-checkbox:checked + .switch .toggle{
  left:29px;
  background:linear-gradient(145deg, #cfcfcf, #a9a9a9);
  box-shadow:-2px -2px 4px #ffffff, 2px 2px 4px #8a8a8a;
}
.toggle-checkbox:checked + .switch .led{ background:yellow; box-shadow:0 0 7px 2px yellow; }
.switch:hover .toggle{ box-shadow:-2px -2px 5px #ffffff, 2px 2px 5px #9b9b9b; }
.toggle-checkbox:focus-visible + .switch{ outline:3px solid var(--blue); outline-offset:2px; }

/* ---------- type --------------------------------------------------------- */

.h2{
  font-size:clamp(2.6rem,6vw,4rem);
  line-height:1.06; letter-spacing:-.024em; font-weight:600;
  margin:0 0 1.1rem;
}
.over{
  font-size:.95rem; font-weight:600; letter-spacing:-.01em;
  color:var(--blue); margin:0 0 .5rem;
}
.lede{
  font-size:clamp(1.1rem,2.2vw,1.35rem); line-height:1.45;
  color:var(--muted); max-width:38ch;
  margin:0 auto clamp(3rem,6vw,4.5rem);
}

/* ---------- sections ----------------------------------------------------- */

.sec{ padding-block:var(--sec-pad, clamp(5rem,11vw,9rem)); }
.sec--grey{ background:var(--grey); }
/* The first grey section meets the hero with a blend instead of a line: white
   at the seam, full grey by "The story so far", the same distance the Shape
   Waves canvas hangs down and dissolves over. */
#journey.sec--grey{ background:linear-gradient(var(--white), var(--grey) var(--sec-pad)); }

/* ---------- hero --------------------------------------------------------- */

.hero{ position:relative; padding-block:clamp(5rem,13vw,10rem) clamp(4rem,8vw,6rem); }
.hero > .wrap{ position:relative; z-index:1; }

/* Shape Waves behind the headline (assets/shape-waves.js). The canvas is
   transparent and paints only the shapes, and it hangs below the hero by
   exactly the next section's top padding (--sec-pad, the same clamp .sec
   uses), so the shapes cross the white-to-grey seam and the shader's own
   edge fade has dissolved them by the time they reach "The story so far".
   It fades in once the first frame is up, so there is never an empty flash.
   A soft white wash, kept to the hero's own box, keeps the headline crisp. */
:root{ --sec-pad:clamp(5rem,11vw,9rem); }
.hero__bg{ position:absolute; left:0; right:0; top:0; bottom:calc(-1 * var(--sec-pad)); z-index:0; pointer-events:none; }
.hero__bg canvas{
  position:absolute; inset:0; display:block; width:100%; height:100%;
  opacity:0; transition:opacity .6s cubic-bezier(.22,1,.36,1);
}
.hero__bg.is-ready canvas{ opacity:1; }
.hero__bg::after{
  content:""; position:absolute; left:0; right:0; top:0; bottom:var(--sec-pad); pointer-events:none;
  background:radial-gradient(ellipse 46% 52% at 50% 50%, rgba(255,255,255,.92), rgba(255,255,255,.55) 55%, rgba(255,255,255,0) 100%);
}
@media (prefers-reduced-motion: reduce){ .hero__bg canvas{ transition:none; } }

.pill{
  display:inline-block; margin:0 0 1.5rem;
  padding:.32rem .9rem; border-radius:980px;
  background:var(--grey); color:var(--muted);
  font-size:.8rem; font-weight:500; letter-spacing:0;
}
.pill span{ font-variant-numeric:tabular-nums; color:var(--ink); font-weight:600; }

.hero__h{
  font-size:clamp(2.9rem,8.5vw,5.5rem);
  line-height:1.03; letter-spacing:-.03em; font-weight:600;
  margin:0 0 1.2rem;
}
.hero__sub{
  font-size:clamp(1.15rem,2.6vw,1.6rem); line-height:1.38;
  color:var(--muted); max-width:26ch; margin:0 auto 2.4rem;
}
.hero__cta{ display:flex; gap:1.6rem; justify-content:center; align-items:center; flex-wrap:wrap; }

.btn{
  display:inline-block;
  background:var(--blue); color:#fff;
  padding:.72rem 1.5rem; border-radius:980px;
  font-size:1rem; font-weight:400; letter-spacing:-.01em;
  transition:background .25s var(--ease), transform .25s var(--ease);
}
.btn:hover{ background:#0077ed; text-decoration:none; transform:scale(1.02); }
.btn:active{ transform:scale(.98); }
.btn--plain{ background:transparent; color:var(--blue); padding:.72rem 0; }
.btn--plain:hover{ background:transparent; text-decoration:underline; transform:none; }
.btn--big{ font-size:1.15rem; padding:.85rem 1.9rem; }

/* ---------- stat row ----------------------------------------------------- */

.hero__stats{
  display:grid; grid-template-columns:repeat(3,1fr);
  gap:2rem; margin-top:clamp(4rem,8vw,6.5rem);
  text-align:center;
}
.hero__stats--tight{ margin-top:0; margin-bottom:clamp(3.5rem,7vw,5rem); }
.stat{ display:flex; flex-direction:column; gap:.35rem; }
.stat__n{
  font-size:clamp(2.4rem,5.5vw,3.6rem); font-weight:600;
  letter-spacing:-.03em; line-height:1;
  font-variant-numeric:tabular-nums;
}
.stat__l{ font-size:.88rem; color:var(--muted); letter-spacing:-.005em; }
@media (max-width:620px){
  .hero__stats{ grid-template-columns:1fr; gap:2.2rem; }
}

/* ---------- cards -------------------------------------------------------- */

.grid{ display:grid; gap:1.25rem; }
.grid--2{ grid-template-columns:repeat(auto-fit,minmax(320px,1fr)); }
.grid--3{ grid-template-columns:repeat(auto-fit,minmax(260px,1fr)); }

.card{
  background:var(--white);
  border-radius:20px;
  padding:clamp(1.75rem,3.2vw,2.5rem);
  border:1px solid rgba(0,0,0,.06);
  box-shadow:0 2px 10px rgba(0,0,0,.035);
  transition:transform .45s var(--ease), box-shadow .45s var(--ease);
}
.sec:not(.sec--grey) .card{ background:var(--grey); border-color:transparent; box-shadow:none; }
.card:hover{ transform:translateY(-5px); box-shadow:0 14px 34px rgba(0,0,0,.09); }

.card__over{ font-size:.8rem; font-weight:600; color:var(--blue); margin:0 0 .4rem; }
.card__h{ font-size:1.6rem; font-weight:600; letter-spacing:-.02em; margin:0 0 .6rem; }
.card__p{ color:var(--muted); margin:0 0 1.4rem; font-size:.98rem; }
.card__big{ margin:0 0 .3rem; display:flex; align-items:baseline; }
.card__big .stat__n{ font-size:clamp(2.6rem,6vw,3.4rem); }
.card__unit{ font-size:1.6rem; font-weight:600; letter-spacing:-.02em; }
.card__f{ margin:0; font-size:.85rem; color:var(--muted); }
.card__f b{ color:var(--ink); font-weight:600; font-variant-numeric:tabular-nums; }

/* Craft card: no number slot — it leads with the technical artifact instead.
   Distinguished from a number card by type scale alone: a semibold ink
   sentence where the big numeral would be. */
/* Small card: the "tried" section. Same card, quieter — no icon, no number. */
.card--sm{ padding:clamp(1.4rem,2.6vw,1.9rem); }
.card--sm .card__h{ font-size:1.3rem; }
.card--sm .card__p{ font-size:.94rem; margin-bottom:1rem; }
.card--sm .card__p:last-child{ margin-bottom:0; }

.card__a{
  margin:0 0 1rem;
  font-size:clamp(1.05rem,2vw,1.2rem);
  line-height:1.42; font-weight:600; letter-spacing:-.015em;
}

/* ---------- case study (caliwrld) ---------------------------------------- */

/* .case__grid paints its gaps via background, so it must live inside a plain
   .wrap div — never merge the two (see README: ears). */
.case{ margin-top:clamp(3.5rem,7vw,5rem); }
.case__h{
  font-size:clamp(1.4rem,3vw,1.8rem); font-weight:600;
  letter-spacing:-.02em; margin:0 0 .5rem; text-align:center;
}
.case__lede{
  font-size:1rem; color:var(--muted); text-align:center;
  max-width:52ch; margin:0 auto 2rem;
}
.case__grid{
  display:grid; grid-template-columns:repeat(2,1fr);
  gap:1px; background:var(--line); border-radius:16px; overflow:hidden;
}
.case__cell{ background:var(--white); padding:1.5rem 1.4rem; }
.case__k{ margin:0 0 .35rem; font-size:.78rem; font-weight:600; color:var(--blue); }
.case__t{ margin:0 0 .3rem; font-size:1.05rem; font-weight:600; letter-spacing:-.015em; }
.case__d{ margin:0; font-size:.9rem; color:var(--muted); line-height:1.5; }
@media (max-width:620px){ .case__grid{ grid-template-columns:1fr; } }

/* ---------- chest ("Things I tried") ------------------------------------- */

/* No JS: no chest, grid open. With JS the grid starts folded (app.js sets
   height 0) and its cards stay hidden until the chest throws them out. */
.chest-wrap{ display:none; }
.js .chest-wrap{
  display:flex; flex-direction:column; align-items:center; gap:1rem;
  margin:clamp(2rem,4vw,3rem) auto clamp(1.5rem,3vw,2.5rem);
}
.js .tried{ overflow:visible; height:0; }
.js #tried:not(.cards-out) .tried .card{ visibility:hidden; opacity:0; }

.chest{
  position:relative; display:block; width:clamp(200px,24vw,270px); aspect-ratio:1/1;
  padding:0; border:0; background:none; cursor:pointer;
  -webkit-tap-highlight-color:transparent;
}
.chest:focus-visible{ outline:3px solid var(--blue); outline-offset:-10px; border-radius:24px; }
.chest__vid{
  position:absolute; inset:0; width:100%; height:100%; object-fit:contain;
  mix-blend-mode:multiply; pointer-events:none;
}
.chest__vid--close{ visibility:hidden; }
#tried.chest-closing .chest__vid--close{ visibility:visible; }
#tried.chest-closing #chest-open{ visibility:hidden; }
.chest__burst{
  position:absolute; left:50%; top:50%; width:80%; height:50%; z-index:2; pointer-events:none;
  transform:translate(-50%,-50%) scale(.3); opacity:0; border-radius:50%;
  background:radial-gradient(closest-side, rgba(255,232,150,.95), rgba(255,200,80,.55) 45%, rgba(255,200,80,0));
  filter:blur(6px);
}
#tried.chest-burst .chest__burst{ animation:zs-chest-burst .9s var(--ease-out) forwards; }
@keyframes zs-chest-burst{
  0%  { opacity:0;  transform:translate(-50%,-50%) scale(.3); }
  25% { opacity:1;  transform:translate(-50%,-62%) scale(1.15); }
  100%{ opacity:0;  transform:translate(-50%,-80%) scale(1.6); }
}
.chest__hint{ margin:0; font-size:.85rem; color:var(--muted); transition:opacity .3s var(--ease); }
#tried.chest-open .chest__hint{ opacity:0; }

@media (prefers-reduced-motion: no-preference){
  /* An idle nudge so it reads as something to click. */
  #tried:not(.chest-open) .chest{ animation:zs-chest-idle 3.2s var(--ease) infinite; }
  .chest .chest__vid{ transition:transform .5s var(--ease); }
  #tried:not(.chest-open) .chest:hover .chest__vid{ transform:scale(1.04); }
  @keyframes zs-chest-idle{
    0%, 78%, 100%{ transform:none; }
    84%{ transform:rotate(-2.5deg) translateY(-2px); }
    90%{ transform:rotate(2.5deg) translateY(-2px); }
    95%{ transform:rotate(-1deg); }
  }
}

/* ---------- also built --------------------------------------------------- */

.also{ margin-top:clamp(3rem,6vw,4.5rem); }
.also__h{ font-size:.85rem; color:var(--muted); margin:0 0 1rem; text-align:center; }
.also__list{ list-style:none; margin:0; padding:0; display:grid; gap:1px; background:var(--line); border-radius:14px; overflow:hidden; }
.also__list li{
  background:var(--white); padding:1rem 1.25rem;
  display:flex; gap:.6rem; flex-wrap:wrap; font-size:.95rem; color:var(--muted);
}
.also__list b{ color:var(--ink); font-weight:600; min-width:8.5rem; }

/* ---------- growth bar (socials) ----------------------------------------- */

/* No new palette: the track is the hairline colour, the fill is the one
   accent. The filled start state is the JS-off truth — the empty start state
   is scoped to .js and to reduced-motion: no-preference, exactly like
   .reveal, so a blocked script can never leave a bar reading zero. */
.bar{
  margin:1rem 0 0; height:6px; border-radius:999px;
  background:var(--line); overflow:hidden;
}
.bar__f{
  display:block; height:100%; width:var(--p,0%);
  border-radius:999px; background:var(--blue);
}

.soc__note{
  text-align:center; font-size:.85rem; color:var(--muted);
  margin-top:2rem; max-width:60ch; margin-inline:auto;
}

/* ---------- series ------------------------------------------------------- */

.reels{ display:grid; gap:1.25rem; grid-template-columns:repeat(auto-fit,minmax(220px,1fr)); }
.reel{
  position:relative; display:flex; flex-direction:column; justify-content:flex-end;
  aspect-ratio:9/16; padding:1.25rem; border-radius:20px; overflow:hidden;
  background:linear-gradient(170deg,#e9e9ec 0%,#fafafc 55%,#e4e4e8 100%);
  color:var(--ink);
  transition:transform .45s var(--ease), box-shadow .45s var(--ease);
}
.reel:hover{ transform:translateY(-5px); box-shadow:0 16px 36px rgba(0,0,0,.1); text-decoration:none; }
.reel__day{ font-size:.75rem; font-weight:600; color:var(--blue); margin:0 0 .25rem; }
.reel__t{ font-size:1.15rem; font-weight:600; letter-spacing:-.02em; margin:0; }
.reels__note{ text-align:center; font-size:.85rem; color:var(--muted); margin-top:1.5rem; }

/* ---------- domus facts -------------------------------------------------- */

.facts{ display:grid; grid-template-columns:repeat(3,1fr); gap:1px; background:var(--line); border-radius:16px; overflow:hidden; }
.fact{ background:var(--white); padding:1.5rem 1.25rem; text-align:center; display:flex; flex-direction:column; gap:.3rem; }
.fact b{ font-size:.78rem; font-weight:500; color:var(--muted); }
.fact span{ font-size:1.1rem; font-weight:600; letter-spacing:-.02em; }
@media (max-width:620px){ .facts{ grid-template-columns:1fr; } }

/* ---------- story -------------------------------------------------------- */

.story{ display:grid; gap:1.5rem; }
.story p{
  margin:0; font-size:clamp(1.05rem,2.1vw,1.2rem); line-height:1.6;
  color:var(--muted);
}

/* ---------- journey (sideways timeline) --------------------------------- */

/* Base state = no JS or reduced motion: a native sideways-scrolling row,
   everything visible. .jr--pin (added by app.js) turns it into a pinned
   frame that vertical scroll drives left to right. */
.jr{ position:relative; margin-top:clamp(2.5rem,5vw,3.5rem); }
.jr__sticky{ position:relative; }
.jr__track{
  list-style:none; margin:0;
  display:flex; align-items:flex-start; gap:clamp(1rem,2vw,1.5rem);
  padding:2.2rem max(var(--pad), calc((100vw - 1080px) / 2)) 1.5rem;
  overflow-x:auto; scroll-snap-type:x proximity; scrollbar-width:none;
}
.jr__track::-webkit-scrollbar{ display:none; }

.ms{ position:relative; flex:none; width:clamp(270px,27vw,340px); scroll-snap-align:start; }
/* The axis: each milestone draws its own stretch of line, dot on the left. */
.ms::before{
  content:""; position:absolute; top:-1.25rem; left:0; right:calc(-1 * clamp(1rem,2vw,1.5rem));
  height:2px; background:var(--line);
}
.ms:last-child::before{ right:50%; }
.ms::after{
  content:""; position:absolute; top:calc(-1.25rem - 5px); left:0;
  width:12px; height:12px; border-radius:50%;
  background:var(--white); border:2px solid var(--ink);
}
.ms--now::after{ background:var(--blue); border-color:var(--blue); box-shadow:0 0 0 6px rgba(0,113,227,.15); }

.ms__when{ margin:0 0 .6rem; font-size:.8rem; font-weight:600; color:var(--blue); }
.ms__card{
  background:var(--white); border-radius:20px; padding:1.4rem 1.4rem 1.5rem;
  border:1px solid rgba(0,0,0,.06); box-shadow:0 2px 10px rgba(0,0,0,.035);
}
.ms__t{ margin:0 0 .45rem; font-size:1.15rem; font-weight:600; letter-spacing:-.015em; line-height:1.25; }
.ms__t a{ color:inherit; }
.ms__p{ margin:0; font-size:.9rem; line-height:1.5; color:var(--muted); }

.ms__fig{ margin:0 0 1rem; }
.ms__fig img{
  display:block; width:100%; height:clamp(150px,30vh,260px);
  object-fit:cover; border-radius:12px; background:var(--grey);
}
.ms__fig figcaption{ margin-top:.45rem; font-size:.75rem; color:var(--muted); }
.ms__figs{ display:grid; grid-template-columns:1fr 1fr; gap:.5rem; margin-bottom:1rem; }
.ms__figs .ms__fig{ margin:0; }

.sec:not(.sec--grey) .ms__card{ background:var(--grey); border-color:transparent; box-shadow:none; }

.jr__bar{ display:none; }

/* Pinned mode. */
.jr--pin .jr__sticky{
  position:sticky; top:var(--nav-h);
  height:calc(100vh - var(--nav-h)); height:calc(100svh - var(--nav-h));
  overflow:hidden; display:flex; flex-direction:column; justify-content:center;
}
.jr--pin .jr__track{ overflow:visible; scroll-snap-type:none; will-change:transform; }
.jr--pin .jr__bar{
  display:block; position:relative; height:2px; background:var(--line);
  margin:1.25rem auto 0; width:min(240px,50vw); border-radius:2px; overflow:hidden;
}
.jr--pin .jr__bar i{
  position:absolute; inset:0; background:var(--blue);
  transform-origin:0 50%; transform:scaleX(0);
}
/* Each milestone rises and fades in as it reaches the screen; its dot fills. */
.jr--pin .ms{
  opacity:0; transform:translate3d(0,26px,0) scale(.97);
  transition:opacity .9s var(--ease-out), transform 1.1s var(--ease-out);
}
.jr--pin .ms.in{ opacity:1; transform:none; }
.jr--pin .ms::after{ transition:background .5s var(--ease), border-color .5s var(--ease); }
.jr--pin .ms.in::after{ background:var(--ink); }
.jr--pin .ms--now.in::after{ background:var(--blue); }

@media (max-width:620px){
  .ms{ width:78vw; }
  .ms__fig img{ height:clamp(130px,26vh,220px); }
}

.foot{ border-top:1px solid var(--line); padding-block:1.75rem; }
.foot__in{ display:flex; justify-content:space-between; flex-wrap:wrap; gap:.5rem; font-size:.8rem; color:var(--muted); }

/* ---------- the transitions ---------------------------------------------- */

/* The hidden start state is scoped to .js (set in <head> before paint) so that
   a JS failure or a blocked script can never leave the page invisible. */
@media (prefers-reduced-motion: no-preference){
  .js .reveal{
    opacity:0;
    transform:translate3d(0,40px,0) scale(.975);
    transition:opacity 1s var(--ease-out), transform 1.2s var(--ease-out);
    transition-delay:var(--d,0ms);
  }
  .js .reveal.in{ opacity:1; transform:none; }
  /* Once landed, cards hand transform back to the hover lift, on the short
     curve — otherwise the hover would inherit the 1.2s reveal timing. */
  .js .card.reveal.in{
    transition:opacity 1s var(--ease-out), transform .45s var(--ease), box-shadow .45s var(--ease);
    transition-delay:0ms;
  }

  /* Same idea, same easing, one beat behind the card it sits in. */
  .js .reveal .bar__f{
    width:0;
    transition:width 1.4s var(--ease);
    transition-delay:calc(var(--d,0ms) + 260ms);
  }
  .js .reveal.in .bar__f{ width:var(--p,0%); }

  /* The avatar answers the pointer before it is clicked. */
  .nav__me{ transition:transform .4s var(--ease); }
  .nav__me:hover{ transform:scale(1.09); }
  .nav__me:active{ transform:scale(1.02); }

  /* The portrait rises and fades in — the page's one motion idea, reused.
     The hidden start state lives INSIDE this block on purpose: outside it,
     `prefers-reduced-motion: reduce` would keep opacity:0 with nothing to
     animate it back, and the portrait would open invisible.
     display and overlay need allow-discrete or the exit frame is skipped. */
  .portrait{
    opacity:0; transform:translateY(12px) scale(.97);
    transition:
      opacity .5s var(--ease),
      transform .5s var(--ease),
      overlay .5s var(--ease) allow-discrete,
      display .5s var(--ease) allow-discrete;
  }
  .portrait:popover-open{ opacity:1; transform:none; }
  @starting-style{
    .portrait:popover-open{ opacity:0; transform:translateY(12px) scale(.97); }
  }

  .portrait::backdrop{
    opacity:0;
    transition:
      opacity .5s var(--ease),
      overlay .5s var(--ease) allow-discrete,
      display .5s var(--ease) allow-discrete;
  }
  .portrait:popover-open::backdrop{ opacity:1; }
  @starting-style{
    .portrait:popover-open::backdrop{ opacity:0; }
  }
}

/* ---------- scroll-linked motion ----------------------------------------- */

/* Progressive enhancement: only browsers with scroll-driven animations get
   these, and only without reduced motion. Nothing here starts hidden, so a
   browser that skips the block loses motion, never content. */
@supports (animation-timeline: scroll()){
  @media (prefers-reduced-motion: no-preference){
    /* Reading progress, as a hairline under the nav. */
    .nav::after{
      content:""; position:absolute; left:0; right:0; bottom:-1px; height:2px;
      background:var(--blue); transform-origin:0 50%; transform:scaleX(0);
      animation:zs-progress linear both;
      animation-timeline:scroll(root);
    }
    @keyframes zs-progress{ to{ transform:scaleX(1); } }

    /* The hero drifts up and softens as you leave it. On the wrapper, not on
       the .reveal children, so it composes with their entrance instead of
       fighting it. */
    .hero > .wrap--narrow{
      animation:zs-hero-out linear both;
      animation-timeline:view();
      animation-range:exit 0% exit 90%;
    }
    @keyframes zs-hero-out{ to{ opacity:0; transform:translate3d(0,-60px,0) scale(.96); } }

    /* Section headings travel a little slower than the page. */
    .sec > .wrap.center{
      animation:zs-drift linear both;
      animation-timeline:view();
      animation-range:cover 0% cover 100%;
    }
    @keyframes zs-drift{
      from{ transform:translate3d(0,36px,0); }
      to  { transform:translate3d(0,-36px,0); }
    }
  }
}
