/* ============================================================
   #SayMyName — site layer
   Loads after base.css. base.css is the UI kit verbatim; nothing
   here overrides a token, only adds the pieces a live site needs
   that a component kit doesn't carry: sticky header, hero,
   video, marquee, footer, scroll motion.
   ============================================================ */

/* ---------- typography ----------
   Orphan and widow control now lives in the kit (§2.4), so only the
   overflow guard stays here: the contact email is one unbreakable
   29-character token and was pushing past the 320px floor. */
/* Headings are deliberately excluded: combined with the &nbsp; that
   binds the last two words, break-word split them mid-word
   ("connectio / ns"). Long unbreakable strings only occur in body
   copy — the contact email — so the rule belongs there. */
p, li, dd{overflow-wrap:break-word}
h1, h2, h3, h4{overflow-wrap:normal; word-break:normal}
a{overflow-wrap:anywhere}

/* Measure caps from §2.4 — most orphans start as an unconstrained
   line length. */
.t-body, p{max-width:70ch}
.t-lead, .phero__lede{max-width:56ch}

/* ---------- bands (§4.1) ----------
   White is the default for content, cream breaks up long stretches of
   it, and a tint appears only where the colour carries one of the six
   themes. Tints used for variety stop meaning anything. */
.band--white{background:var(--paper)}
.band--cream{background:var(--cream)}

/* ---------- document ---------- */
html{scroll-behavior:smooth}
body{overflow-x:hidden}
p{max-width:64ch}

.skip{
  position:absolute; left:-9999px; top:0; z-index:300;
  background:var(--purple-deep); color:#fff; padding:14px 22px;
  border-radius:0 0 var(--r-card) 0; font:700 16px/1 var(--body); text-decoration:none;
}
.skip:focus{left:0}

.sr-only{
  position:absolute; width:1px; height:1px; padding:0; margin:-1px;
  overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; border:0;
}

/* ---------- sticky header ---------- */
/* The kit's .nav is a pill for display inside the kit page. On the
   site it becomes a full-width sticky bar, per §5.1: cream, no
   border, soft shadow after 8px of scroll. */
.site-head{position:sticky; top:0; z-index:100; background:var(--cream); transition:box-shadow .2s ease}
.site-head[data-stuck="true"]{box-shadow:0 2px 20px rgba(36,31,29,.09)}
.site-head .nav{
  background:transparent; border:0; border-radius:0;
  padding:14px 0; max-width:var(--content); margin-inline:auto;
  padding-inline:var(--gutter); gap:16px; flex-wrap:nowrap;
}
.nav__brand{flex:none}
.brand__cloud{width:26px; height:26px; flex:none}

.nav__burger{
  display:none; margin-left:auto; align-items:center; gap:9px;
  background:var(--purple-tint); border:0; border-radius:var(--r-pill);
  padding:11px 18px; font:700 15px/1 var(--body); color:var(--ink); cursor:pointer;
  min-height:44px;
}
.nav__burger svg{width:16px; height:16px}

@media(max-width:1180px){
  .nav__burger{display:inline-flex}
  .nav__links{
    display:none; position:absolute; inset:auto 0 auto 0; top:100%;
    flex-direction:column; align-items:stretch; gap:2px; margin:0;
    background:var(--cream); padding:8px var(--gutter) 22px;
    box-shadow:0 12px 24px rgba(36,31,29,.09);
  }
  .nav__links[data-open="true"]{display:flex; animation:menuIn .24s ease}
  .nav__link{padding:0 16px; min-height:48px; justify-content:flex-start}
  .nav__cta{margin-left:0}
  .nav__links > li:last-child{margin-left:0}
  .nav__links .btn{margin-top:10px; width:100%}
}
@keyframes menuIn{from{opacity:0; transform:translateY(-8px)}to{opacity:1; transform:none}}

/* ---------- touch targets ----------
   §5.2 sizes `sm` at 40px, but §8 sets a non-negotiable 44x44 floor
   and nav links compute to ~38px. Resolved in favour of §8. */
/* ---------- touch targets & nav alignment ----------
   §5.2 sizes `sm` at 40px, but §8 sets a non-negotiable 44x44 floor.
   Resolved in favour of §8. The kit's .nav__link is inline, so its
   padding never built a real box — links measured 47px tall and sat
   9px above the CTA. inline-flex + a shared min-height lines every
   item up on one centreline. */
.btn--sm{min-height:44px}
.nav__links{align-items:center; gap:2px}
.nav__link{
  display:inline-flex; align-items:center; justify-content:center;
  min-height:44px; padding:0 16px;
}
.nav__links > li:last-child{margin-left:18px}
.nav__brand{padding-block:9px}
.site-foot ul{gap:2px}
.site-foot li a{display:inline-flex; align-items:center; min-height:44px}

/* ---------- logo lockups ----------
   Both are the real Figma artwork, not type. The guide is explicit:
   "typing #SayMyName in Chewy does not produce the logo and must never
   be used as one." Mark colours (navy/sky/pink/green) belong to the
   artwork alone and never mix with the six UI theme colours.
   Minimum widths from the guide: wordmark 140px, primary 120px.
   Clear space = one quarter of the logo height. */
.brand__wordmark{
  display:block; height:auto;
  width:clamp(150px,17vw,182px);
}
.brand__wordmark--foot{width:clamp(160px,20vw,200px)}
.nav__brand{gap:0; padding-block:9px}

.hero__lockup{
  display:block; height:auto;
  width:clamp(230px,26vw,330px);
  margin-bottom:26px;
}
/* The lockup already says the campaign name, so the H1 carries only
   the second half of the line. */
.hero__tagline{margin-bottom:18px}

/* ---------- icon badge (§6) ----------
   Simple rounded line icon, 2px stroke, inside a solid circular badge
   in the section's colour. Ink on pastel — never white on pastel. */
.iconbadge{
  width:60px; height:60px; border-radius:50%;
  /* Core fills fail here: ink on maroon is 1.5:1 and on purple 2.9:1,
     under the 3:1 floor for a 2px stroke. Tint ground + deep icon
     clears on all six. */
  background:var(--tintc,var(--green-tint));
  color:var(--deepc,var(--green-deep));
  display:grid; place-items:center; flex:none;
  margin-bottom:18px;
}
.iconbadge svg{
  width:26px; height:26px; fill:none; stroke:currentColor;
  stroke-width:2; stroke-linecap:round; stroke-linejoin:round;
}

/* ---------- film CTA ----------
   Reuses the §5.17 split: square still one side, copy the other. The
   still is a button opening the same modal as the hero, so the site has
   one film component rather than two. */
.filmcta__media{
  position:relative; display:block; width:100%;
  aspect-ratio:1/1; border-radius:var(--r-card); overflow:hidden;
  border:0; padding:0; cursor:pointer; background:var(--purple-tint);
}
.filmcta__media img{width:100%; height:100%; object-fit:cover; transition:transform .5s ease}
.filmcta__media:hover img{transform:scale(1.03)}
.filmcta__media::after{
  content:""; position:absolute; inset:0; z-index:1;
  background:linear-gradient(180deg,rgba(36,31,29,0) 50%,rgba(36,31,29,.28));
}
.filmcta__btn{
  position:absolute; top:50%; left:50%; transform:translate(-50%,-50%);
  z-index:2; width:84px; height:84px; border-radius:50%;
  background:var(--purple-deep); color:#fff;
  display:grid; place-items:center; pointer-events:none;
  transition:transform .22s ease, background-color .15s ease;
}
.filmcta__btn svg{width:26px; height:26px; margin-left:5px}
.filmcta__media:hover .filmcta__btn,
.filmcta__media:focus-visible .filmcta__btn{transform:translate(-50%,-50%) scale(1.08)}
.filmcta__meta{margin-top:18px; color:var(--ink-muted)}

/* ---------- photo-backed section ----------
   Full-bleed photograph with a 50% black overlay, copy in white on top.
   Note on contrast: 50% black over a bright photo lands at 3.95:1 for
   white text — fine for headings (3:1 floor) but under the 4.5:1 body
   floor in the worst case. The photos in use are mid-tone, so the real
   figure is far higher; if a pale image is ever swapped in, raise
   --overlay to .55 and it clears worst-case at 4.74:1. */
.photosec{position:relative; overflow:hidden; isolation:isolate; color:#fff}
.photosec__img{position:absolute; inset:0; z-index:0}
.photosec__img img{width:100%; height:100%; object-fit:cover; display:block}
.photosec__scrim{position:absolute; inset:0; z-index:1; background:#000; opacity:var(--overlay,.5)}
.photosec__inner{position:relative; z-index:2}
/* White copy applies only where text sits directly on the photo. Cards
   and other opaque surfaces keep ink — without :not() the card text
   went white-on-white and vanished. */
.photosec :is(h2,h3,p,.t-h2,.t-h3,.t-body,.t-lead,.eyebrow):not(.card *):not(.mcard *){color:#fff}
.photosec .card :is(h2,h3,p,.t-h2,.t-h3,.t-body){color:var(--ink)}
/* Checklist items sit directly on the photo, so the pill fill is dropped
   and the tick mark carries the colour instead. */
.photosec .benefit{background:transparent; padding-left:0}
.photosec .benefit .t-body{color:#fff}

/* ---------- interior page hero (§5.16) ----------
   The kit supplies .phero, .phero__field and .squiggle. What it can't
   supply is where each name sits, so the scatter is placed here. The
   field is tone-on-tone at ~1.4:1 and aria-hidden — texture, never
   content. */
.phero__field span:nth-child(1){top:8%;  left:4%;  transform:rotate(-8deg)}
.phero__field span:nth-child(2){top:22%; right:6%; transform:rotate(6deg)}
.phero__field span:nth-child(3){top:52%; left:2%;  transform:rotate(4deg)}
.phero__field span:nth-child(4){bottom:10%; right:9%; transform:rotate(-5deg)}
.phero__field span:nth-child(5){top:4%;  left:44%; transform:rotate(3deg)}
.phero__field span:nth-child(6){bottom:6%;  left:26%; transform:rotate(-3deg)}
.phero__field span:nth-child(7){top:38%; right:28%; transform:rotate(7deg)}
@media(max-width:700px){
  .phero__field span:nth-child(5),
  .phero__field span:nth-child(7){display:none}
}

/* ---------- hero ---------- */
/* §11 specifies a centred hero. The brief asks for the campaign film
   on the right, so this is a deliberate two-column deviation on the
   home page only; interior pages use the centred form (.hero--center). */
.hero{padding-block:clamp(40px,6vw,76px) clamp(48px,7vw,88px)}
.hero__grid{display:grid; grid-template-columns:1fr; gap:clamp(30px,5vw,60px); align-items:center}
@media(min-width:940px){.hero__grid{grid-template-columns:1.03fr .97fr}}

.hero h1{margin-bottom:18px}
.hero__name{color:var(--purple-deep)}
.hero__sub{max-width:42ch; margin-bottom:28px}

.hero--center{text-align:center}
.hero--center .wrap > *{margin-inline:auto}
.hero--center p{max-width:56ch}
.hero--center .row{justify-content:center}

/* ---------- language selector ----------
   Three short codes rather than a dropdown: with only three languages a
   select is more clicks than links, and each is a real URL so search
   engines can follow them. The full language name is available to
   screen readers. */
/* Sits at the far right, after the primary action. .nav__links already
   claims margin-left:auto, so a second auto here split the free space
   and parked the selector mid-header. */
.langsel{margin-left:14px}
/* The kit lets .nav__links wrap. With the selector at the far right the CTA
   dropped to a second row. Keeping the links on one line fixes that, but the
   full desktop nav then needs 1070px (English is the widest of the three
   languages), so with 40px gutters the burger menu takes over below 1180px
   rather than the kit's 940px. */
@media(min-width:1181px){
  .nav__links{flex-wrap:nowrap}
  .nav__link{padding:0 12px}
  .langsel{margin-left:10px}
}
.langsel ul{display:flex; align-items:center; gap:2px; list-style:none; margin:0; padding:0}
.langsel__link{
  display:inline-flex; align-items:center; justify-content:center;
  min-height:44px; min-width:44px; padding:0 10px;
  border-radius:var(--r-pill);
  font:700 14px/1 var(--body); letter-spacing:.4px;
  color:var(--ink-muted); text-decoration:none;
}
.langsel__link:hover{background:var(--purple-tint); color:var(--ink)}
.langsel__link[aria-current="true"]{background:var(--purple-tint); color:var(--ink)}
@media(max-width:1180px){
  /* brand (150) + selector (136) + burger (107) = 393px of content in a
     350px bar, so the selector moves into the dropdown on mobile. */
  .site-head .nav{flex-wrap:wrap}
  .langsel{
    display:none; order:5; width:100%; margin:0;
    padding:12px var(--gutter) 4px;
  }
  .nav[data-menu="open"] .langsel{display:block}
  .langsel ul{justify-content:flex-start; gap:6px}
  .langsel__link{border:2px solid var(--hairline)}
}

/* ---------- hero still ----------
   The film is not cleared for use yet, so the hero shows a plain
   photograph with no play affordance. The .player, .filmcta and .modal
   rules below are kept intact so the film can be restored as a
   markup-only change (see film-components.html.bak). */
/* Grid items default to min-width:auto, so the 1000px intrinsic width of
   the still stopped the column shrinking and pushed the page wide. */
.hero__grid > *{min-width:0}
.hero__still{
  /* The width/height attributes are presentational hints, and height="1000"
     was beating aspect-ratio — the still rendered 485x1000. height:auto
     hands control back to aspect-ratio while the attributes still reserve
     the right space before the image loads. */
  width:100%; max-width:100%; height:auto; aspect-ratio:1/1; object-fit:cover;
  border-radius:var(--r-card); background:var(--purple-tint);
  display:block;
}

/* ---------- campaign film ----------
   Hero shows a square still with a centred play button. The film
   itself opens in a modal. */
.player{
  position:relative; border-radius:var(--r-card); overflow:hidden;
  background:var(--purple-tint); aspect-ratio:1/1; width:100%;
  display:block; border:0; padding:0; cursor:pointer;
  isolation:isolate;
}
.player img{width:100%; height:100%; object-fit:cover; transition:transform .5s ease}
.player:hover img{transform:scale(1.03)}
.player::after{
  content:""; position:absolute; inset:0; z-index:1;
  background:linear-gradient(180deg,rgba(36,31,29,0) 45%,rgba(36,31,29,.34));
}
.player__btn{
  position:absolute; top:50%; left:50%; transform:translate(-50%,-50%);
  z-index:2; width:96px; height:96px; border-radius:50%;
  background:var(--purple-deep); color:#fff;
  display:grid; place-items:center; pointer-events:none;
  transition:transform .22s ease, background-color .15s ease;
}
.player__btn svg{width:30px; height:30px; margin-left:5px}
.player:hover .player__btn,
.player:focus-visible .player__btn{transform:translate(-50%,-50%) scale(1.08)}
.player__label{
  position:absolute; z-index:2; left:20px; bottom:20px;
  background:rgba(36,31,29,.66); color:#fff;
  padding:10px 18px; border-radius:var(--r-pill);
  font:700 15px/1 var(--body);
}

/* ---------- film modal ---------- */
.modal[hidden]{display:none}
.modal{
  position:fixed; inset:0; z-index:200;
  display:grid; place-items:center; padding:clamp(16px,4vw,40px);
  background:rgba(36,31,29,.82);
  animation:modalIn .2s ease-out;
}
.modal__box{
  position:relative; width:min(100%,1000px);
  border-radius:var(--r-card); overflow:hidden; background:#000;
  animation:boxIn .24s ease-out;
}
.modal__box video{width:100%; height:auto; max-height:78vh; display:block; background:#000}
.modal__close{
  position:absolute; top:14px; right:14px; z-index:2;
  width:48px; height:48px; border-radius:50%; border:0; cursor:pointer;
  background:var(--cream); color:var(--ink);
  display:grid; place-items:center;
  transition:transform .18s ease, background-color .15s ease;
}
.modal__close svg{width:20px; height:20px}
.modal__close:hover{transform:scale(1.07); background:#fff}
@keyframes modalIn{from{opacity:0}to{opacity:1}}
@keyframes boxIn{from{opacity:0; transform:scale(.97)}to{opacity:1; transform:none}}
body.is-locked{overflow:hidden}

/* ---------- name marquee ---------- */
.names{
  overflow:hidden; padding-block:22px;
  border-block:2px solid var(--hairline);
  /* The track is deliberately wider than the viewport. 100vw includes the
     scrollbar, so it overflowed by the scrollbar width; 100% does not. */
  max-width:100%;
  -webkit-mask-image:linear-gradient(90deg,transparent,#000 7%,#000 93%,transparent);
  mask-image:linear-gradient(90deg,transparent,#000 7%,#000 93%,transparent);
}
.names__track{display:flex; gap:44px; width:max-content; animation:marquee 52s linear infinite}
/* Permanent Marker has a single weight (400) and ships Latin only, so
   the name list is restricted to Latin-1. Before adding a name, check
   every character is at or below U+00FF — ā (U+0101), ş (U+015F) and
   ễ (U+1EC5) all fall back mid-word, which on this campaign is the one
   thing worth avoiding. */
.names__track span{font:400 clamp(26px,3.2vw,42px)/1.15 var(--names); white-space:nowrap; color:var(--purple-deep)}
.names__track span:nth-child(2n){color:var(--maroon-deep)}
.names__track span:nth-child(3n){color:var(--green-deep)}
.names__track span:nth-child(5n){color:var(--sky-deep)}
@keyframes marquee{from{transform:translateX(0)}to{transform:translateX(-50%)}}

/* ---------- Q-row ----------
   The cloud sits inside the tinted panel rather than beside it.
   The infographic's alternation is kept by swapping which side the
   cloud takes, so the zigzag survives the move. */
.qrow{display:block; margin-bottom:20px}
.qrow__panel{
  display:grid; grid-template-columns:auto 1fr;
  gap:clamp(20px,3.5vw,52px); align-items:center;
  padding:clamp(26px,3.6vw,46px);
}
.qrow__cloud{display:flex; justify-content:center}
.qrow__cloud .cloud{
  min-width:clamp(150px,20vw,290px);
  min-height:clamp(96px,12vw,170px);
  padding:20px 30px;
}
.qrow__text > * + *{margin-top:12px}
.qrow--flip .qrow__panel{grid-template-columns:1fr auto}
.qrow--flip .qrow__cloud{order:2}

@media(max-width:760px){
  .qrow__panel,
  .qrow--flip .qrow__panel{grid-template-columns:1fr; gap:20px; justify-items:start}
  .qrow--flip .qrow__cloud{order:0}
  .qrow__cloud{justify-content:flex-start}
  .qrow__cloud .cloud{min-width:130px; min-height:80px}
}

/* ---------- school year: timeline beside a photo ---------- */
.year{display:grid; grid-template-columns:1fr; gap:clamp(28px,4vw,52px); align-items:start}
@media(min-width:900px){
  .year{grid-template-columns:1.12fr .88fr}
  .year__media{position:sticky; top:104px}
}
.year__media figure{margin:0}
.year__media img{
  width:100%; aspect-ratio:4/5; object-fit:cover;
  border-radius:var(--r-card); background:var(--sky-tint);
}
.year__cap{
  margin-top:14px; font:600 14px/1.5 var(--body); color:var(--ink-muted);
}

/* ---------- closing CTA ----------
   Full pastel band rather than a tint, so it reads as an arrival
   rather than another section. Ink on lilac is 6.1:1 (§1.5 rule 1),
   and the action uses the on-colour button from §5.2. */
.cta{background:var(--purple-deep); color:#fff; padding-block:clamp(48px,7vw,88px)}
.cta__grid{
  display:grid; grid-template-columns:1fr; gap:clamp(28px,4vw,56px);
  align-items:center;
}
@media(min-width:860px){.cta__grid{grid-template-columns:.9fr 1.1fr}}
.cta__media img{
  width:100%; aspect-ratio:1/1; object-fit:cover;
  border-radius:var(--r-card); background:var(--purple-tint);
}
.cta h2{
  color:#fff;
  font-size:clamp(34px,4.6vw,56px); line-height:1.1; letter-spacing:-.4px;
}
.cta__lead{font:400 20px/1.55 var(--body); margin-top:16px; max-width:36ch; color:rgba(255,255,255,.9)}
.cta .row{margin-top:30px}
/* The ghost button is ink by default, which is 2.02:1 on the deep-purple
   band. On this band it goes white. */
.cta .btn--ghost{color:#fff}
.cta .btn--ghost:hover{color:#fff; text-decoration:underline}
.cta__foot{
  /* lilac-deep on lilac measures 1.99:1 — under the §8 floor. Ink is
     6.58:1, and a white pill behind it keeps the line from reading as
     body copy. */
  display:inline-block; margin-top:24px;
  background:var(--paper); color:var(--ink);
  padding:11px 20px; border-radius:var(--r-pill);
  font:700 15px/1.4 var(--body);
}

/* ---------- section headings ---------- */
.eyebrow{
  font:700 13px/1 var(--body); letter-spacing:1.2px; text-transform:uppercase;
  color:var(--deep,var(--purple-deep)); display:block; margin-bottom:12px;
}
/* Measure is capped on the paragraph, not the heading — capping the
   wrapper was breaking H2s into fragments like "Six questions, six /
   answers". Headings stay on one line wherever they fit. */
.head{margin-bottom:clamp(30px,4vw,48px)}
.head p{max-width:46ch}
.head--center{margin-inline:auto; text-align:center}
.head--center p{margin-inline:auto}
@media(min-width:900px){
  .head h2, .qrow__text h3{white-space:nowrap}
}

/* ---------- media card grid ---------- */
.cards-3{display:grid; gap:24px; grid-template-columns:repeat(auto-fit,minmax(300px,1fr))}
.mcard__media img{transition:transform .5s ease}
.mcard:hover .mcard__media img{transform:scale(1.04)}

/* ---------- activity cards ---------- */
.acard{display:flex; flex-direction:column; gap:4px; height:100%}
.acard .card__num{margin-bottom:6px}
.acard__meta{margin-top:auto; padding-top:16px; font:600 14px/1.4 var(--body); color:var(--ink-muted)}

/* ---------- timeline extras ---------- */
/* §2.4 sets .tl__month to never break, which is right for a bare month
   but overflows at 320px once the activity number is appended. */
@media(max-width:460px){
  .tl__month{text-wrap:pretty; white-space:normal}
}
.tl__item:last-child{padding-bottom:0}
.tl__cloud{position:absolute; left:-40px; top:-2px; width:38px; height:38px}
.tl__row{display:grid; gap:4px}
.tl__n{font:400 26px/1 var(--display); color:var(--deep,var(--purple-deep))}

/* ---------- stats ---------- */
.stat{text-align:center}
.stat__n{color:var(--deep,var(--green-deep))}
.stat p{margin-inline:auto; max-width:26ch; font:400 15px/1.5 var(--body); color:var(--ink-muted); margin-top:8px}

/* One stat carries a word rather than a numeral. It keeps the display
   treatment so the four-up rhythm holds, at a smaller size since the
   word is far longer than a two-digit number. */
.stat--word .stat__n{font-size:clamp(26px,3vw,40px); line-height:1.1}
.stat--word .t-meta{margin-top:12px}

/* Netlify honeypot. Hidden from sight and from assistive tech, but still
   a real field in the DOM so bots fill it in and the submission is
   discarded. Not display:none — some bots skip those. */
.hidden-field{
  position:absolute; left:-9999px; top:auto;
  width:1px; height:1px; overflow:hidden;
}

/* ---------- narrow screens: use the full measure ----------
   `balance` and `pretty` deliberately shorten lines to even them out,
   which is right on a wide column and wasteful on a 350px one — the
   hero paragraph was losing 37px and the headline 13px to it. Below
   700px the text wraps normally and fills the width. */
@media(max-width:700px){
  h1, h2, h3, h4, .t-h1, .t-h2, .t-h3{text-wrap:wrap}
  p, li, dd, .t-lead, .t-body{text-wrap:wrap}

  /* Content-width elements leave a ragged right edge on a narrow
     screen. Stretching them squares it off. */
  .hero .row .btn{width:100%; justify-content:center}
  .hero__lockup{width:min(300px,100%)}

  /* The kit floors the hero H1 at 40px, which leaves the headline using
     65% of a 350px column. 44px fits the same two lines and fills more
     of it — a bigger headline reads as more deliberate than a small one
     stranded mid-column. Verified no overflow down to 320px. */
  .hero .t-h1{font-size:44px}
}

/* ---------- footer ---------- */
.site-foot{background:var(--cream); border-top:2px solid var(--hairline); padding-block:clamp(48px,6vw,72px) 32px}
.foot__grid{display:grid; gap:34px; grid-template-columns:repeat(auto-fit,minmax(220px,1fr))}
.foot__h{font:700 13px/1 var(--body); letter-spacing:1.2px; text-transform:uppercase; color:var(--ink-muted); margin-bottom:14px}
.site-foot ul{display:grid; gap:10px}
.site-foot a{color:var(--ink); text-decoration:none}
.site-foot a:hover{color:var(--purple-deep); text-decoration:underline; text-underline-offset:3px}
.foot__base{margin-top:44px; padding-top:22px; border-top:2px solid var(--hairline); font:400 14px/1.5 var(--body); color:var(--ink-muted)}

/* ---------- motion ---------- */
/* §7: fade in and rise 16px, 400ms ease-out, staggered 40ms. */
[data-reveal]{opacity:0; transform:translateY(16px); transition:opacity .4s ease-out, transform .4s ease-out}
[data-reveal].is-in{opacity:1; transform:none}
[data-reveal-group]>*{opacity:0; transform:translateY(16px); transition:opacity .4s ease-out, transform .4s ease-out}
[data-reveal-group].is-in>*{opacity:1; transform:none}
[data-reveal-group].is-in>*:nth-child(2){transition-delay:.04s}
[data-reveal-group].is-in>*:nth-child(3){transition-delay:.08s}
[data-reveal-group].is-in>*:nth-child(4){transition-delay:.12s}
[data-reveal-group].is-in>*:nth-child(5){transition-delay:.16s}
[data-reveal-group].is-in>*:nth-child(6){transition-delay:.2s}

/* Clouds bob on hover — the only playful motion, reserved for the
   signature element (§7). */
.cloud{transition:transform .18s ease}
.qrow:hover .qrow__cloud .cloud{transform:translateY(-2px)}

@media (prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  *,*::before,*::after{animation-duration:.001ms!important; animation-iteration-count:1!important; transition-duration:.001ms!important}
  [data-reveal],[data-reveal-group]>*{opacity:1; transform:none}
  .names__track{animation:none}
}
