/* =========================================================================
   JOYDO — Website Experience Template
   Brand tokens are the single source of truth. Section 9 of the brief.
   ========================================================================= */
:root{
  /* Approved brand palette — do not extend */
  --teal:#1F7A8C;
  --yellow:#F6C445;
  --green:#5BBE7A;
  --charcoal:#263238;
  --white:#FFFFFF;

  /* Derived UI tints (allowed utility tints, not brand colors) */
  --t-teal:#EAF5F7;
  --t-yellow:#FFF7DE;
  --t-green:#EDF9F1;
  --t-neutral:#F4F6F7;

  /* Working shades derived from the brand teal only */
  --teal-deep:#155F6E;
  --teal-soft:#3E94A6;

  --ink:var(--charcoal);
  --ink-60:rgba(38,50,56,.66);
  --ink-40:rgba(38,50,56,.44);
  --line:rgba(38,50,56,.10);

  /* Geometry — section 10 */
  --r-btn:14px;
  --r-card:22px;
  --r-panel:32px;

  /* Soft shadows only */
  --sh-1:0 1px 3px rgba(38,50,56,.05), 0 6px 18px rgba(38,50,56,.06);
  --sh-2:0 4px 12px rgba(38,50,56,.07), 0 18px 44px rgba(38,50,56,.10);
  --sh-3:0 10px 28px rgba(38,50,56,.10), 0 32px 70px rgba(38,50,56,.14);

  --head:"Baloo 2","Nunito",ui-rounded,"Segoe UI Rounded","Hiragino Maru Gothic ProN",system-ui,sans-serif;
  --body:"Inter",system-ui,-apple-system,"Segoe UI",Roboto,sans-serif;

  --ease:cubic-bezier(.22,.61,.36,1);
  --ease-cine:cubic-bezier(.65,.02,.15,1);
  --shell:1240px;
}

*,*::before,*::after{box-sizing:border-box}
html,body{margin:0;padding:0}
html{scroll-behavior:smooth}
body{
  font-family:var(--body);
  color:var(--ink);
  background:var(--white);
  -webkit-font-smoothing:antialiased;
  line-height:1.6;
  overflow-x:hidden;
}
body.is-locked{overflow:hidden}
h1,h2,h3,h4{font-family:var(--head);font-weight:700;line-height:1.14;margin:0;letter-spacing:-.01em}
p{margin:0}
img,svg{display:block;max-width:100%}
button{font:inherit;color:inherit;border:0;background:none;cursor:pointer}
a{color:inherit;text-decoration:none}
:focus-visible{outline:3px solid var(--teal);outline-offset:3px;border-radius:6px}

.shell{width:min(100% - 40px,var(--shell));margin-inline:auto}
/* the hero runs wider than the content sections so the doorway can be big */
.hero .shell{width:min(100% - 40px,1400px)}
.sr{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}

/* ---------- Buttons ---------------------------------------------------- */
.btn{
  display:inline-flex;align-items:center;justify-content:center;gap:10px;
  padding:14px 24px;border-radius:var(--r-btn);
  font-family:var(--head);font-weight:600;font-size:16px;letter-spacing:.01em;
  transition:transform .18s var(--ease),box-shadow .22s var(--ease),background-color .22s var(--ease);
  white-space:nowrap;
}
.btn:hover{transform:translateY(-2px)}
.btn:active{transform:translateY(0)}
.btn .ico{width:20px;height:20px;flex:none}
.btn-primary{background:var(--teal);color:#fff;box-shadow:0 6px 16px rgba(31,122,140,.26)}
.btn-primary:hover{background:var(--teal-deep);box-shadow:0 10px 24px rgba(31,122,140,.30)}
.btn-ghost{background:#fff;color:var(--teal);box-shadow:inset 0 0 0 1.5px rgba(31,122,140,.35)}
.btn-ghost:hover{box-shadow:inset 0 0 0 1.5px var(--teal),0 8px 20px rgba(31,122,140,.14)}
.btn-yellow{background:var(--yellow);color:var(--charcoal);box-shadow:0 6px 16px rgba(246,196,69,.34)}
.btn-yellow:hover{background:#efb92c}
.btn-dark{background:var(--charcoal);color:#fff}
.btn-sm{padding:10px 16px;font-size:14px;border-radius:12px}
.btn-block{width:100%}

/* ---------- Header ----------------------------------------------------- */
.nav{
  position:absolute;top:0;left:0;right:0;z-index:40;
  padding:18px 0;
  transition:opacity .5s var(--ease),transform .5s var(--ease);
}
.nav-in{display:flex;align-items:center;gap:28px}
.brand{display:flex;align-items:center;flex:none}
.brand img{width:132px;height:auto}
.nav-links{display:flex;gap:26px;margin-inline:auto;font-size:15px;font-weight:500;color:var(--ink-60)}
.nav-links a{position:relative;padding:6px 0;transition:color .2s var(--ease)}
.nav-links a::after{content:"";position:absolute;left:0;right:100%;bottom:0;height:2px;background:var(--teal);border-radius:2px;transition:right .25s var(--ease)}
.nav-links a:hover{color:var(--teal)}
.nav-links a:hover::after{right:0}
.nav-wa{display:flex;align-items:center;gap:9px;font-size:15px;font-weight:600;color:var(--ink);flex:none}
.nav-wa .wa{width:32px;height:32px;border-radius:50%;background:var(--t-green);display:grid;place-items:center;flex:none}
.nav-wa .wa svg{width:18px;height:18px;fill:#3BA55C}
.nav-wa b{color:var(--teal)}
.nav-burger{display:none;width:44px;height:44px;border-radius:12px;background:#fff;box-shadow:var(--sh-1);place-items:center}
.nav-burger span{display:block;width:18px;height:2px;background:var(--ink);border-radius:2px;position:relative}
.nav-burger span::before,.nav-burger span::after{content:"";position:absolute;left:0;width:18px;height:2px;background:var(--ink);border-radius:2px}
.nav-burger span::before{top:-6px}.nav-burger span::after{top:6px}

/* ---------- Entrance / hero -------------------------------------------- */
.hero{
  position:relative;min-height:100svh;padding:88px 0 44px;
  display:flex;align-items:center;
  background:
    radial-gradient(120% 90% at 50% 0%,#FFFDF8 0%,#FBF7EF 46%,#F6F1E6 100%);
  overflow:hidden;
}
.hero::after{
  content:"";position:absolute;left:0;right:0;bottom:-1px;height:120px;
  background:var(--white);
  -webkit-mask:radial-gradient(120% 110% at 50% 100%,#000 60%,transparent 61%);
  mask:radial-gradient(120% 110% at 50% 100%,#000 60%,transparent 61%);
}
.leaf{position:absolute;opacity:.5;pointer-events:none}
.leaf-l{left:-70px;top:6%;width:280px;transform:rotate(-8deg)}
.leaf-r{right:-90px;top:0;width:320px;transform:scaleX(-1) rotate(-6deg)}

.hero-grid{
  position:relative;z-index:5;
  /* middle column widened from 1.72fr so the doorway reads at full size,
     but the copy column keeps enough room that the h1 stays on two lines */
  display:grid;grid-template-columns:minmax(230px,.8fr) minmax(440px,2.5fr) minmax(230px,.8fr);
  gap:20px;align-items:center;
}
/* capped at 54px so "Open the" stays on one line in the narrower copy
   column that the enlarged doorway leaves behind */
.hero-copy h1{font-size:clamp(34px,3.5vw,50px)}
.hero-copy h1 .accent{color:var(--teal)}
.squiggle{display:flex;gap:6px;margin:16px 0 18px}
.squiggle i{display:block;width:20px;height:5px;border-radius:4px;background:var(--yellow);transform:rotate(-38deg)}
.squiggle i:nth-child(2){background:var(--green);transform:rotate(-20deg)}
.squiggle i:nth-child(3){background:var(--yellow);transform:rotate(-52deg);width:14px}
.hero-copy p.lede{font-size:17px;color:var(--ink-60);max-width:27ch;margin-bottom:28px}
/* the tour CTA stands at the foot of the doorway. The wrapper centres it so the
   button's own :hover translate is not overwritten. */
.door-cta{
  position:absolute;left:0;right:0;bottom:1%;z-index:9;
  display:flex;justify-content:center;pointer-events:none;
}
/* The host stands on the threshold. Percentages are all derived from the door
   SVG so she stays locked to the floor line at every viewport width. */
.host-photo{
  position:absolute;left:46.2%;bottom:6.4%;height:67.6%;width:auto;z-index:8;
  pointer-events:none;filter:drop-shadow(0 12px 18px rgba(38,50,56,.18));
}
.host-shadow{                    /* contact shadow, so she is not pasted on */
  position:absolute;left:66%;bottom:4.6%;width:13%;height:2.6%;z-index:7;
  background:radial-gradient(50% 50% at 50% 50%,rgba(38,50,56,.34),rgba(38,50,56,0) 70%);
  pointer-events:none;
}
.door-cta .btn{pointer-events:auto;box-shadow:0 12px 28px rgba(31,122,140,.34)}

/* Doorway (hero visual) */
.doorframe{position:relative;aspect-ratio:1/1.02;width:min(100%,88vh);margin-inline:auto}
.doorframe .glowpool{
  position:absolute;left:8%;right:8%;bottom:-2%;height:16%;
  background:radial-gradient(60% 100% at 50% 50%,rgba(246,196,69,.55),rgba(246,196,69,0) 70%);
  filter:blur(6px);
}
.door-svg{position:absolute;inset:0;width:100%;height:100%;overflow:visible}
.peek{transform-origin:50% 60%;animation:peekdrift 22s ease-in-out infinite alternate}
@keyframes peekdrift{from{transform:scale(1.02) translateX(-.6%)}to{transform:scale(1.06) translateX(.6%)}}

.tags{display:flex;flex-wrap:wrap;gap:7px}
.tag{font-size:12px;font-weight:600;font-family:var(--head);padding:5px 12px;border-radius:999px;background:var(--t-teal);color:var(--teal-deep)}
.tag.y{background:var(--t-yellow);color:#8A6A0B}
.tag.g{background:var(--t-green);color:#2F7A4C}

/* Plan-a-visit rail */
.plan{
  background:#fff;border-radius:var(--r-panel);box-shadow:var(--sh-3);
  padding:22px 20px;position:relative;z-index:9;
}
.plan h3{font-size:20px;text-align:center;color:var(--teal-deep)}
.plan .sub{text-align:center;font-size:13px;color:var(--ink-40);margin:6px 0 16px}
.field{position:relative;margin-bottom:10px}
.field .fi{position:absolute;left:13px;top:50%;transform:translateY(-50%);width:17px;height:17px;opacity:.42;pointer-events:none}
.field input,.field select{
  width:100%;padding:13px 14px 13px 38px;border-radius:var(--r-btn);
  border:1.5px solid var(--line);background:#fff;font-family:var(--body);font-size:14.5px;color:var(--ink);
  transition:border-color .2s var(--ease),box-shadow .2s var(--ease);appearance:none;
}
.field select{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8'%3E%3Cpath d='M1 1l5 5 5-5' fill='none' stroke='%23263238' stroke-opacity='.45' stroke-width='1.6' stroke-linecap='round'/%3E%3C/svg%3E");
  background-repeat:no-repeat;background-position:right 14px center;background-size:11px}
.field input:focus,.field select:focus{outline:0;border-color:var(--teal);box-shadow:0 0 0 4px rgba(31,122,140,.12)}
.field.invalid input,.field.invalid select{border-color:#D9534F;box-shadow:0 0 0 4px rgba(217,83,79,.10)}
.field .err{display:none;font-size:12px;color:#C4443F;margin:5px 0 0 4px}
.field.invalid .err{display:block}
.field .lab{font-size:11.5px;font-weight:600;color:var(--ink-40);position:absolute;top:-8px;left:12px;background:#fff;padding:0 6px;letter-spacing:.02em}
.plan .safe{display:flex;align-items:center;justify-content:center;gap:7px;font-size:11.5px;color:var(--ink-40);margin-top:12px;text-align:center}
.plan .safe svg{width:14px;height:14px;flex:none;opacity:.5}
.plan-alt{margin-top:14px;background:var(--t-green);border-radius:16px;padding:11px 13px;display:flex;align-items:center;gap:10px}
.plan-alt .wa{width:30px;height:30px;border-radius:50%;background:#fff;display:grid;place-items:center;flex:none}
.plan-alt .wa svg{width:17px;height:17px;fill:#3BA55C}
.plan-alt div{font-size:12px;line-height:1.35;min-width:0}
.plan-alt span{color:var(--ink-40);display:block;font-size:11px}
.plan-alt .ph{margin-top:3px}
.plan-loc{margin-top:9px;background:var(--t-neutral);border-radius:16px;padding:11px 13px;display:flex;align-items:center;gap:10px;font-size:12px;line-height:1.35}
.plan-loc svg{width:16px;height:16px;flex:none;opacity:.55}
.plan-loc span{color:var(--ink-40);display:block;font-size:11px}

/* Clearly-marked placeholder for business data not yet supplied (brief §8) */
.ph{
  display:inline-block;font-family:var(--body);font-size:11.5px;font-weight:600;
  color:var(--teal-deep);background:repeating-linear-gradient(135deg,var(--t-teal),var(--t-teal) 7px,#fff 7px,#fff 14px);
  border:1px dashed rgba(31,122,140,.5);border-radius:7px;padding:1px 8px;letter-spacing:.01em;
}

/* ---------- Generic sections ------------------------------------------- */
.sec{padding:86px 0}
.sec-head{text-align:center;margin-bottom:52px}
.sec-head h2{font-size:clamp(27px,3vw,38px);display:inline-flex;align-items:center;gap:12px}
.sec-head h2 .sq{display:inline-flex;gap:5px}
.sec-head h2 .sq i{width:16px;height:4px;border-radius:3px;background:var(--yellow);transform:rotate(-40deg)}
.sec-head h2 .sq i:nth-child(2){background:var(--green);transform:rotate(-16deg)}
.sec-head p{color:var(--ink-60);margin-top:12px;font-size:16px;max-width:58ch;margin-inline:auto}

/* How it works */
.steps{display:grid;grid-template-columns:repeat(3,1fr);gap:22px;position:relative}
.step{text-align:center;padding:0 12px;position:relative}
.step .ring{width:82px;height:82px;border-radius:50%;margin:0 auto 18px;display:grid;place-items:center;position:relative}
.step .ring svg{width:34px;height:34px}
.step:nth-child(1) .ring{background:var(--t-teal)}
.step:nth-child(2) .ring{background:var(--t-green)}
.step:nth-child(3) .ring{background:var(--t-yellow)}
.step .num{position:absolute;right:-6px;top:-4px;width:30px;height:30px;border-radius:50%;
  display:grid;place-items:center;font-family:var(--head);font-weight:700;font-size:13px;color:#fff;background:var(--teal);box-shadow:var(--sh-1)}
.step:nth-child(2) .num{background:var(--green)}
.step:nth-child(3) .num{background:var(--yellow);color:var(--charcoal)}
.step h4{font-size:18px;margin-bottom:8px}
.step p{font-size:14.5px;color:var(--ink-60);max-width:24ch;margin-inline:auto}
.steps .dash{position:absolute;top:40px;width:12%;height:20px;opacity:.4}
.steps .dash svg{width:100%;height:100%}
.steps .d1{left:28%}.steps .d2{left:61%}

/* Why parents love Joydo */
.why{background:linear-gradient(180deg,var(--t-yellow) 0%,#FFFBF0 100%);position:relative}
.why::before{content:"";position:absolute;top:-52px;left:0;right:0;height:56px;background:inherit;
  -webkit-mask:radial-gradient(140% 100% at 50% 100%,#000 62%,transparent 63%);
  mask:radial-gradient(140% 100% at 50% 100%,#000 62%,transparent 63%)}
.pillars{display:grid;grid-template-columns:repeat(4,1fr);gap:26px}
.pillar{display:flex;gap:15px;align-items:flex-start}
.pillar .pi{width:56px;height:56px;border-radius:18px;display:grid;place-items:center;flex:none;box-shadow:var(--sh-1)}
.pillar .pi svg{width:26px;height:26px;fill:#fff}
.pillar:nth-child(1) .pi{background:var(--teal)}
.pillar:nth-child(2) .pi{background:var(--yellow)}
.pillar:nth-child(3) .pi{background:var(--green)}
.pillar:nth-child(4) .pi{background:var(--charcoal)}
.pillar h4{font-size:16.5px;color:var(--teal-deep);margin-bottom:5px}
.pillar:nth-child(2) h4{color:#8A6A0B}
.pillar:nth-child(3) h4{color:#2F7A4C}
.pillar:nth-child(4) h4{color:var(--charcoal)}
.pillar p{font-size:14px;color:var(--ink-60)}

/* =========================================================================
   SEVEN SPACES — BENTO GALLERY  (replaces the old uniform .zonegrid)
   Research-backed choices:
     • Varied cell sizes create hierarchy — one hero cell, two wide, four
       standard — instead of seven identical tiles competing for attention.
     • Generous, even gutters and a single radius keep it calm, not busy.
     • Hover is a state change, not decoration: the art pushes in, the scrim
       deepens, and the caption lifts to reveal the call to action.
     • It collapses 4 -> 2 -> 1 column, keeping the hero cell wide at every
       step so the hierarchy survives on a phone.
   The imagery is a crop of the same illustrated centre the tour flies
   through, so the gallery and the tour are literally the same world. Swap
   `thumb()` for a <img> when real photography exists — nothing else changes.
   ========================================================================= */
.bento{display:grid;grid-template-columns:repeat(4,1fr);grid-auto-rows:214px;gap:16px}

.bcard{
  position:relative;display:block;overflow:hidden;padding:0;border:0;cursor:pointer;
  border-radius:26px;background:var(--t-neutral);text-align:left;
  box-shadow:var(--sh-1);
  transition:transform .42s var(--ease),box-shadow .42s var(--ease);
}
.bcard:hover{transform:translateY(-5px);box-shadow:var(--sh-3)}
.bcard:focus-visible{outline:3px solid var(--teal);outline-offset:3px}

/* the bento rhythm: hero 2x2, two full-width-of-half banners, four standard */
.bcard:nth-child(1){grid-column:span 2;grid-row:span 2}
.bcard:nth-child(4){grid-column:span 2}
.bcard:nth-child(7){grid-column:span 2}

.bcard .art{position:absolute;inset:0;overflow:hidden}
.bcard .art svg{
  width:100%;height:100%;display:block;
  transform:scale(1.02);transition:transform .7s var(--ease);
}
.bcard:hover .art svg{transform:scale(1.09)}

/* scrim — dark enough for white text at AA, light enough to keep the art */
.bcard .scrim{
  position:absolute;inset:0;
  background:linear-gradient(to top,rgba(14,42,49,.84) 0%,rgba(14,42,49,.60) 16%,rgba(14,42,49,.20) 38%,rgba(14,42,49,0) 58%);
  transition:opacity .42s var(--ease);
}
.bcard:hover .scrim{opacity:.92}

.bcard .cap{position:absolute;left:0;right:0;bottom:0;padding:20px 22px;color:#fff;z-index:2}
.bcard .tl{
  display:inline-block;font-family:var(--head);font-weight:600;font-size:10.5px;
  letter-spacing:.06em;text-transform:uppercase;margin-bottom:8px;white-space:nowrap;
  padding:4px 10px;border-radius:20px;background:rgba(255,255,255,.18);
  backdrop-filter:blur(3px);-webkit-backdrop-filter:blur(3px);
}
.bcard h4{font-size:19px;color:#fff;text-shadow:0 1px 10px rgba(17,45,52,.4)}
.bcard:nth-child(1) h4{font-size:26px}
.bcard .go{
  display:flex;align-items:center;gap:7px;margin-top:9px;
  font-family:var(--head);font-weight:600;font-size:14px;color:#fff;
  opacity:0;transform:translateY(8px);
  transition:opacity .34s var(--ease),transform .34s var(--ease);
}
.bcard .go svg{width:16px;height:16px;color:#fff}
.bcard:hover .go,.bcard:focus-visible .go{opacity:1;transform:translateY(0)}

/* accent hairline — the zone's own colour, so the gallery and the tour agree */
.bcard::after{
  content:"";position:absolute;left:0;right:0;bottom:0;height:4px;z-index:3;
  background:var(--teal);transform:scaleX(0);transform-origin:0 50%;
  transition:transform .44s var(--ease);
}
.bcard:hover::after,.bcard:focus-visible::after{transform:scaleX(1)}
.bcard[data-accent="yellow"]::after{background:var(--yellow)}
.bcard[data-accent="green"]::after{background:var(--green)}

/* Touch and keyboard users never hover — show the CTA rather than hide it. */
@media (hover:none){
  .bcard .go{opacity:1;transform:none}
  .bcard::after{transform:scaleX(1)}
}


/* Closing band */
.closing{background:var(--teal);color:#fff;position:relative;overflow:hidden;padding:0}
.closing-in{display:grid;grid-template-columns:.85fr 1.15fr;gap:40px;align-items:center;min-height:330px}
.closing-art{align-self:end;position:relative}
.closing-art svg{width:100%;max-width:420px;display:block}
.closing-copy{padding:56px 0}
.quote{font-family:var(--head);font-size:clamp(24px,2.5vw,32px);font-weight:600;line-height:1.3;position:relative;padding-left:44px;margin-bottom:34px}
.quote::before{content:"";position:absolute;left:0;top:2px;width:30px;height:24px;
  background:currentColor;opacity:.5;
  -webkit-mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 24'%3E%3Cpath d='M13 24V12C13 5.4 8 .6 1 0v5c3.7.7 6 3.3 6 7H0v12h13zm19 0V12c0-6.6-5-11.4-12-12v5c3.7.7 6 3.3 6 7h-7v12h13z'/%3E%3C/svg%3E") center/contain no-repeat;
  mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 24'%3E%3Cpath d='M13 24V12C13 5.4 8 .6 1 0v5c3.7.7 6 3.3 6 7H0v12h13zm19 0V12c0-6.6-5-11.4-12-12v5c3.7.7 6 3.3 6 7h-7v12h13z'/%3E%3C/svg%3E") center/contain no-repeat}
.closing-box{border:2px dashed rgba(255,255,255,.4);border-radius:26px;padding:26px 28px;display:flex;align-items:center;gap:22px;flex-wrap:wrap}
.closing-box .cb-ico{width:52px;height:52px;flex:none;opacity:.9}
.closing-box .cb-ico svg{width:100%;height:100%;fill:none;stroke:#fff;stroke-width:1.6}
.closing-box p{font-family:var(--head);font-weight:600;font-size:19px;line-height:1.35;flex:1;min-width:200px}

/* Footer */
.foot{background:#fff;padding:56px 0 30px;border-top:1px solid var(--line)}
.foot-in{display:grid;grid-template-columns:1.3fr 1fr 1fr 1.1fr;gap:32px}
.foot img{width:126px;margin-bottom:14px}
.foot p{font-size:14px;color:var(--ink-60);max-width:30ch}
.foot h5{font-family:var(--head);font-size:14px;letter-spacing:.06em;text-transform:uppercase;color:var(--ink-40);margin:0 0 14px}
.foot ul{list-style:none;padding:0;margin:0;display:grid;gap:9px;font-size:14.5px;color:var(--ink-60)}
.foot ul a:hover{color:var(--teal)}
.foot-bar{margin-top:40px;padding-top:20px;border-top:1px solid var(--line);display:flex;justify-content:space-between;gap:16px;flex-wrap:wrap;font-size:12.5px;color:var(--ink-40)}

/* =========================================================================
   MARKETING CONTENT SECTIONS
   Offers, age bands, safety, parent voices, FAQ, visit.
   All six are rendered from JOYDO_CONFIG — no copy lives in the CSS or the
   markup. Only the approved brand tokens and the existing utility tints are
   used; no new colours are introduced.
   ========================================================================= */

/* ---------- What Joydo Offers ------------------------------------------- */
.offergrid{display:grid;grid-template-columns:repeat(3,1fr);gap:22px}
.offer{
  background:#fff;border:1px solid var(--line);border-radius:var(--r-card);
  padding:26px 24px;display:flex;flex-direction:column;gap:12px;
  transition:transform .24s var(--ease),box-shadow .24s var(--ease);
}
.offer:hover{transform:translateY(-4px);box-shadow:var(--sh-2)}
.offer .oi{width:50px;height:50px;border-radius:15px;display:grid;place-items:center;flex:none;background:var(--t-teal)}
.offer .oi svg{width:25px;height:25px;color:var(--teal)}
.offer[data-accent="yellow"] .oi{background:var(--t-yellow)}
.offer[data-accent="yellow"] .oi svg{color:#B78A0E}
.offer[data-accent="green"] .oi{background:var(--t-green)}
.offer[data-accent="green"] .oi svg{color:#3B9A61}
.offer h4{font-size:18px}
.offer p{font-size:14.5px;color:var(--ink-60);line-height:1.6}
.offer .ph{align-self:flex-start;margin-top:auto}

/* ---------- Age bands --------------------------------------------------- */
.ages{margin-top:46px;background:var(--t-neutral);border-radius:26px;padding:34px 30px}
.ages-head{display:flex;align-items:baseline;gap:14px;flex-wrap:wrap;margin-bottom:22px}
.ages-head h3{font-size:22px}
.bands{display:grid;grid-template-columns:repeat(4,1fr);gap:16px}
.band{background:#fff;border:1px solid var(--line);border-radius:18px;padding:20px 18px}
.band .rg{font-family:var(--head);font-weight:700;font-size:26px;color:var(--teal);line-height:1}
.band .lb{font-family:var(--head);font-weight:600;font-size:13px;letter-spacing:.04em;text-transform:uppercase;color:var(--ink-40);margin:8px 0 10px}
.band p{font-size:14px;color:var(--ink-60);line-height:1.55}

/* ---------- Safety ------------------------------------------------------ */
.sec.safety{background:var(--t-teal)}
.safelist{display:grid;grid-template-columns:repeat(2,1fr);gap:16px 30px;max-width:960px;margin-inline:auto}
.safeitem{display:flex;gap:14px;align-items:flex-start;background:#fff;border-radius:16px;padding:18px 20px}
.safeitem .ck{width:26px;height:26px;border-radius:50%;flex:none;display:grid;place-items:center;background:var(--t-green)}
.safeitem .ck svg{width:14px;height:14px;color:#2F7A4C}
.safeitem p{font-size:14.5px;color:var(--ink);line-height:1.55}
.safecreds{margin:30px auto 0;max-width:960px;display:flex;align-items:center;gap:12px;flex-wrap:wrap;justify-content:center;font-size:14px;color:var(--ink-60)}

/* ---------- Parent voices — featured-quote wall -------------------------
   Research-backed choices:
     • 3 quotes. Three to six is the reported sweet spot for a landing page;
       more becomes a wall nobody reads.
     • One featured quote carries the weight, two supporting ones give it
       company — a spotlight reads as conviction, a uniform row reads as
       filler.
     • Full name + relationship to the centre on every card; a quote with no
       attributable person is worth nothing.
     • Deliberately NO star ratings. Joydo cannot rate itself, and inventing
       a score is exactly the thing this page must not do.
   ----------------------------------------------------------------------- */
.vwall{display:grid;grid-template-columns:repeat(3,1fr);grid-auto-rows:1fr;gap:20px}

.voice{
  position:relative;background:#fff;border:1px solid var(--line);border-radius:24px;
  padding:28px 26px;display:flex;flex-direction:column;gap:14px;
  box-shadow:var(--sh-1);
  transition:transform .3s var(--ease),box-shadow .3s var(--ease),border-color .3s var(--ease);
}
.voice:hover{transform:translateY(-4px);box-shadow:var(--sh-2);border-color:rgba(31,122,140,.3)}

/* the spotlight: teal, two columns wide and two rows tall */
.voice.lead{
  grid-column:span 2;grid-row:span 2;
  background:linear-gradient(150deg,var(--teal) 0%,var(--teal-deep) 100%);
  border-color:transparent;color:#fff;padding:38px 40px;justify-content:center;
}
.voice.lead::before{                       /* soft light bloom, top-right */
  content:"";position:absolute;inset:0;border-radius:inherit;pointer-events:none;
  background:radial-gradient(120% 80% at 88% 4%,rgba(255,255,255,.20),rgba(255,255,255,0) 62%);
}
.voice.lead>*{position:relative}

.voice .qm{font-family:var(--head);font-weight:700;font-size:54px;line-height:.62;color:var(--yellow)}
.voice.lead .qm{font-size:76px;color:rgba(255,255,255,.5)}

.voice blockquote{font-size:16px;line-height:1.66;color:var(--ink);flex:1;margin:0}
.voice.lead blockquote{font-family:var(--head);font-weight:600;font-size:25px;line-height:1.45;color:#fff;flex:0}

.voice .who{display:flex;align-items:center;gap:13px;border-top:1px solid var(--line);padding-top:16px;margin-top:auto}
.voice.lead .who{border-top-color:rgba(255,255,255,.28);padding-top:22px;margin-top:26px}
.voice .av{width:44px;height:44px;border-radius:50%;flex:none;display:grid;place-items:center;background:var(--t-teal)}
.voice .av svg{width:22px;height:22px;color:var(--teal)}
.voice.lead .av{width:50px;height:50px;background:rgba(255,255,255,.18)}
.voice.lead .av svg{width:25px;height:25px;color:#fff}
.voice .nm{font-family:var(--head);font-weight:600;font-size:15.5px}
.voice.lead .nm{font-size:17px;color:#fff}
.voice .mt{font-size:13px;color:var(--ink-40)}
.voice.lead .mt{color:rgba(255,255,255,.72)}

.voice-note{
  margin:26px auto 0;max-width:660px;text-align:center;font-size:13px;color:#8A6A0B;
  background:var(--t-yellow);border:1px dashed rgba(183,138,14,.55);border-radius:14px;padding:12px 18px;
}

/* ---------- FAQ --------------------------------------------------------- */
.faqlist{max-width:820px;margin-inline:auto;display:grid;gap:12px}
.faqitem{background:#fff;border:1px solid var(--line);border-radius:16px;overflow:hidden}
.faqitem summary{
  cursor:pointer;list-style:none;padding:19px 22px;display:flex;align-items:center;gap:14px;
  font-family:var(--head);font-weight:600;font-size:16.5px;color:var(--ink);
}
.faqitem summary::-webkit-details-marker{display:none}
.faqitem summary:focus-visible{outline:3px solid var(--teal-soft);outline-offset:-3px}
.faqitem summary .cv{width:20px;height:20px;flex:none;margin-left:auto;color:var(--teal);
  transform:rotate(90deg);transition:transform .22s var(--ease)}   /* i-chev points right; 90deg = down */
.faqitem[open] summary .cv{transform:rotate(-90deg)}
.faqitem[open] summary{color:var(--teal-deep)}
.faqa{padding:0 22px 20px;font-size:15px;line-height:1.68;color:var(--ink-60)}
.faqa .ph{margin-top:8px}

/* ---------- Visit ------------------------------------------------------- */
.visitgrid{display:grid;grid-template-columns:1.1fr .9fr;gap:30px;align-items:start}
.vcard{background:#fff;border:1px solid var(--line);border-radius:22px;padding:30px 28px}
.vrow{display:flex;gap:16px;align-items:flex-start;padding:16px 0;border-bottom:1px solid var(--line)}
.vrow:last-of-type{border-bottom:0}
@media (min-width:761px){
  #vcard{display:grid;grid-template-columns:1fr 1fr;column-gap:26px}
  #vcard .vrow:first-child{grid-column:1/-1}
  #vcard .vrow:nth-last-child(2){border-bottom:0}
}
.vrow .vi{width:42px;height:42px;border-radius:13px;flex:none;display:grid;place-items:center;background:var(--t-teal)}
.vrow .vi svg{width:21px;height:21px;color:var(--teal)}
/* i-wa is the one solid glyph with no fill attribute — it follows `fill`. */
.vrow[data-k="wa"] .vi{background:var(--t-green)}
.vrow[data-k="wa"] .vi svg{fill:#3BA55C}
.vrow h5{font-family:var(--head);font-size:12.5px;letter-spacing:.06em;text-transform:uppercase;color:var(--ink-40);margin-bottom:5px}
.vrow .vv{font-size:15.5px;line-height:1.5;color:var(--ink)}
.vmap{
  border-radius:22px;overflow:hidden;border:1px solid var(--line);background:var(--t-neutral);
  padding:30px 28px;display:flex;flex-direction:column;gap:16px;
}
.vmap h4{font-size:19px}
.vmap p{font-size:14.5px;color:var(--ink-60);line-height:1.6}
.vmap .btn{align-self:flex-start}

/* =========================================================================
   DOORWAY TRANSITION  (brief §5.2)
   ========================================================================= */
.doorway{
  position:fixed;inset:0;z-index:80;display:none;place-items:center;
  background:radial-gradient(120% 100% at 50% 55%,#FBF6EC 0%,#F2E9DA 100%);
  perspective:1500px;
}
.doorway.on{display:grid}
.doorway-stage{position:relative;height:min(74vh,640px);aspect-ratio:320/420;transform-origin:50% 54%;
  transition:transform 1.15s var(--ease-cine),opacity .5s ease .75s}
.doorway.push .doorway-stage{transform:scale(3.6);opacity:0}
.doorway .bloom{
  position:absolute;left:50%;top:54%;width:10px;height:10px;pointer-events:none;
  transform:translate(-50%,-50%) scale(1);
  border-radius:50%;background:radial-gradient(circle,rgba(255,248,230,.98) 0%,rgba(246,196,69,.5) 36%,rgba(246,196,69,0) 70%);
  opacity:0;transition:transform 1.2s var(--ease-cine),opacity .9s ease;
}
.doorway.open .bloom{transform:translate(-50%,-50%) scale(14);opacity:.7}
.doorway.push .bloom{transform:translate(-50%,-50%) scale(80);opacity:1;transition:transform 1s var(--ease-cine),opacity .5s ease}
.dw-panel{transform-origin:var(--hinge) 50%;transition:transform 1.25s var(--ease-cine)}
.doorway.open .dw-l{transform:rotateY(-104deg)}
.doorway.open .dw-r{transform:rotateY(104deg)}
.dw-story{
  position:absolute;left:0;right:0;bottom:6%;z-index:3;text-align:center;font-family:var(--head);
  font-weight:600;font-size:19px;color:var(--teal-deep);opacity:0;transition:opacity .45s ease;
}
.doorway.open .dw-story{opacity:1}

/* =========================================================================
   VIRTUAL TOUR
   ========================================================================= */
.tour{
  position:fixed;inset:0;z-index:70;background:#EFE7D9;
  opacity:0;visibility:hidden;transition:opacity .7s ease,visibility 0s .7s;
}
.tour.on{opacity:1;visibility:visible;transition:opacity .7s ease}
.stage{position:absolute;inset:0;overflow:hidden;touch-action:none;
  background:radial-gradient(120% 90% at 50% 20%,#FDF9F1,#EFE3CE)}
.stage.grab{cursor:grab}
.stage.grab:active{cursor:grabbing}
.swipehint{
  position:absolute;z-index:13;left:50%;bottom:84px;transform:translateX(-50%) translateY(8px);
  background:rgba(38,50,56,.82);color:#fff;font-size:12.5px;font-weight:500;
  padding:8px 15px;border-radius:999px;white-space:nowrap;pointer-events:none;
  opacity:0;transition:opacity .4s var(--ease),transform .4s var(--ease);
}
.swipehint.on{opacity:1;transform:translateX(-50%) translateY(0)}
.world{
  position:absolute;left:0;top:0;width:1600px;height:1000px;transform-origin:0 0;
  transition:transform 1.5s var(--ease-cine);will-change:transform;
}
.world .scene{width:1600px;height:1000px;display:block}

/* hotspots */
.hs{
  position:absolute;transform-origin:50% 50%;
  transform:translate(-50%,-50%) scale(var(--inv,1));
  display:flex;align-items:center;padding:0;
  transition:opacity .5s var(--ease);
  /* fill-mode must be `backwards`, never `both` — a retained `to` value would
     out-rank .tour.zoomed .hs{opacity:.26} and break hotspot softening. */
  animation:hsIn .7s var(--ease) backwards;
}
@keyframes hsIn{from{opacity:0}to{opacity:1}}
.hs .hs-in{animation:hsPop .6s var(--ease) backwards;animation-delay:inherit}
@keyframes hsPop{from{transform:scale(.55)}to{transform:scale(1)}}
.hs-in{
  display:flex;align-items:center;gap:9px;
  background:var(--teal);color:#fff;border-radius:999px;padding:8px 16px 8px 8px;
  box-shadow:0 6px 18px rgba(31,122,140,.34);
  font-family:var(--head);font-weight:600;font-size:14.5px;white-space:nowrap;
  transition:transform .22s var(--ease),background-color .22s var(--ease);
}
.hs[data-accent="yellow"] .hs-in{background:var(--yellow);color:var(--charcoal);box-shadow:0 6px 18px rgba(246,196,69,.42)}
.hs[data-accent="green"] .hs-in{background:var(--green);box-shadow:0 6px 18px rgba(91,190,122,.36)}
.hs-dot{width:28px;height:28px;border-radius:50%;background:rgba(255,255,255,.22);display:grid;place-items:center;flex:none}
.hs[data-accent="yellow"] .hs-dot{background:rgba(38,50,56,.13)}
.hs-dot svg{width:16px;height:16px;fill:currentColor}
.hs::before{
  content:"";position:absolute;left:22px;top:50%;width:34px;height:34px;margin:-17px 0 0 -17px;
  border-radius:50%;background:currentColor;color:var(--teal);opacity:.34;
  animation:pulse 2.8s ease-out infinite;pointer-events:none;
}
.hs[data-accent="yellow"]::before{color:var(--yellow)}
.hs[data-accent="green"]::before{color:var(--green)}
@keyframes pulse{0%{transform:scale(.7);opacity:.42}70%{transform:scale(2.1);opacity:0}100%{opacity:0}}
.hs:hover .hs-in,.hs:focus-visible .hs-in{transform:scale(1.06)}
.hs .hint{
  position:absolute;left:50%;top:-38px;transform:translateX(-50%) translateY(4px);
  background:#fff;color:var(--ink);font-size:12.5px;font-weight:500;font-family:var(--body);
  padding:6px 12px;border-radius:10px;box-shadow:var(--sh-2);white-space:nowrap;
  opacity:0;pointer-events:none;transition:opacity .2s ease,transform .2s ease;
}
.hs:hover .hint,.hs:focus-visible .hint{opacity:1;transform:translateX(-50%) translateY(0)}
.tour.zoomed .hs{opacity:.26}
.tour.zoomed .hs.is-active{opacity:1}
.tour.zoomed .hs::before{animation:none;opacity:0}

/* zone liveliness.
   NOTE: .zone / .a-* elements live inside the <use> source tree, so they are
   styled by class on the ORIGINAL element — never by a descendant selector
   rooted at .stage. Every <use> instance mirrors the original's state. */
.zone{transition:opacity .7s var(--ease)}
.zone.dimmed{opacity:.55;filter:saturate(.75)}
.zone.is-active{opacity:1}

/* Character loops: small and natural, and only while the tour is on stage. */
@keyframes bob{0%,100%{transform:translateY(0)}50%{transform:translateY(-5px)}}
@keyframes bob2{0%,100%{transform:translateY(-4px)}50%{transform:translateY(1px)}}
@keyframes sway{0%,100%{transform:rotate(-1.6deg)}50%{transform:rotate(1.6deg)}}
@keyframes sway2{0%,100%{transform:rotate(1.1deg)}50%{transform:rotate(-1.1deg)}}
@keyframes popd{0%,100%{transform:translateY(0) scale(1);opacity:.85}50%{transform:translateY(-9px) scale(1.14);opacity:1}}
@keyframes pageflip{0%,100%{transform:rotate(0)}50%{transform:rotate(-8deg)}}
/* the book itself settles down while the reader zone is on stage, so the
   turning page is the thing the eye follows and not the whole book. */
@keyframes pageflipCalm{0%,100%{transform:rotate(0)}50%{transform:rotate(-3deg)}}
@keyframes floatup{0%{transform:translateY(0) rotate(-4deg);opacity:0}18%{opacity:.9}100%{transform:translateY(-52px) rotate(6deg);opacity:0}}
@keyframes balance{0%,100%{transform:rotate(-2.4deg)}50%{transform:rotate(2.4deg)}}
.a-bob,.a-bob2,.a-pop,.a-pop2,.a-float{transform-box:fill-box;transform-origin:50% 100%}
.a-sway,.a-sway2,.a-balance,.a-page,.a-page2{transform-box:view-box}
/* AMBIENT SCENE MOTION — OFF (23 Aug 2026, client decision).
   The centre is a still illustration; the turning book page is the only thing
   that moves. The class markers stay on the artwork and the keyframes above
   stay defined, so any single loop can be brought back by uncommenting its
   one line — nothing else has to be rebuilt.

html.tour-live .scene .a-bob{animation:bob 3.6s ease-in-out infinite}
html.tour-live .scene .a-bob2{animation:bob2 4.4s ease-in-out .5s infinite}
html.tour-live .scene .a-sway{animation:sway 6.5s ease-in-out infinite}
html.tour-live .scene .a-sway2{animation:sway2 7.8s ease-in-out infinite}
html.tour-live .scene .a-pop{animation:popd 3.2s ease-in-out infinite}
html.tour-live .scene .a-pop2{animation:popd 4.1s ease-in-out .9s infinite}
html.tour-live .scene .a-page{animation:pageflip 5.2s ease-in-out infinite}
html.tour-live .scene .a-balance{animation:balance 4.6s ease-in-out infinite}
html.tour-live .scene .a-float{animation:floatup 6s ease-in-out infinite}
*/

/* -------------------------------------------------------------------------
   LIBRARY NOOK — the page fold.

   Each book is: static LEFT leaf, static RIGHT leaf, and a TURNING leaf that
   is a copy of the right leaf sitting on top of it. The turning leaf pivots
   on the spine (transform-box:fill-box + transform-origin:left centre, so the
   pivot travels correctly through the <use> instances and the camera zoom).

   scaleX(1) -> scaleX(-1) mirrors the right leaf exactly onto the left leaf,
   so BOTH rest positions are pixel-identical to a static leaf underneath.
   That is what makes the loop seamless: the restart is invisible, no fade
   needed. skewY + scaleY give the sheet its curl as it lifts, and the face
   fill swaps at the edge-on frame so you see the back of the page after the
   spine. A soft shade wash sells the fold catching light.

   A real page whips over in about a second and then the book rests. So the
   travel is short (12%->36% of the cycle) and the rest is long: the eye reads
   "a page just turned", not "something is slowly morphing".
   ------------------------------------------------------------------------- */
@keyframes pgTurn{
  0%,12%   {transform:scaleX(1)    skewY(0)       scaleY(1);    animation-timing-function:cubic-bezier(.42,0,.88,.48)}
  18%      {transform:scaleX(.62)  skewY(-3deg)   scaleY(1.05)}
  24%      {transform:scaleX(.06)  skewY(-4.6deg) scaleY(1.09); animation-timing-function:cubic-bezier(.12,.5,.55,1)}
  30%      {transform:scaleX(-.62) skewY(-3deg)   scaleY(1.05)}
  36%,100% {transform:scaleX(-1)   skewY(0)       scaleY(1)}
}
/* front of the sheet until it is edge-on, back of the sheet after */
@keyframes pgFace{0%,23%{fill:#F5EDDD}25%,100%{fill:#FFFDF6}}
/* curl shading: a gradient across the sheet, faded in only while it is up,
   so both rest states stay pixel-identical to a plain flat leaf */
@keyframes pgShade{0%,12%{opacity:0}24%{opacity:.30}36%,100%{opacity:0}}
/* shadow the lifted sheet drops into the gutter */
@keyframes pgGutter{0%,12%{opacity:.05}24%{opacity:.30}36%,100%{opacity:.05}}

.a-turn{transform-box:fill-box;transform-origin:left center;will-change:transform}
.a-turn .pg-shade{opacity:0}
.a-gutter{opacity:.05}

/* Ambient: books breathe, pages stay put. The fold only runs once the
   visitor is actually standing in the Library Nook. */
html.tour-live .scene [data-zone-id="z-library"].is-active .a-turn{animation:pgTurn 5.6s linear infinite}
html.tour-live .scene [data-zone-id="z-library"].is-active .a-turn .pg-face{animation:pgFace 5.6s step-end infinite}
html.tour-live .scene [data-zone-id="z-library"].is-active .a-turn .pg-shade{animation:pgShade 5.6s ease-in-out infinite}
html.tour-live .scene [data-zone-id="z-library"].is-active .a-gutter{animation:pgGutter 5.6s ease-in-out infinite}
/* second reader is offset so the two books never turn in lockstep */
html.tour-live .scene [data-zone-id="z-library"].is-active .a-turn2{animation-duration:6.4s;animation-delay:-2.1s}
html.tour-live .scene [data-zone-id="z-library"].is-active .a-turn2 .pg-face{animation-duration:6.4s;animation-delay:-2.1s}
html.tour-live .scene [data-zone-id="z-library"].is-active .a-turn2 .pg-shade{animation-duration:6.4s;animation-delay:-2.1s}
html.tour-live .scene [data-zone-id="z-library"].is-active .a-gutter2{animation-duration:6.4s;animation-delay:-2.1s}
/* The books themselves are held completely still — no wobble at all — so the
   turning page is the single moving thing on screen.

html.tour-live .scene .a-page2{animation:pageflipCalm 6.8s ease-in-out -2s infinite}
html.tour-live .scene [data-zone-id="z-library"].is-active .a-page{animation:pageflipCalm 6.2s ease-in-out infinite}
*/

/* -------------------------------------------------------------------------
   CREATIVE STUDIO — three small activities.

   Every limb group sits inside a wrapper that places its JOINT at local
   (0,0), so `transform-origin:0 0` pivots exactly on the shoulder with no
   coordinate maths — and it keeps working through the camera zoom.
   ------------------------------------------------------------------------- */
@keyframes drawHand{0%,100%{transform:rotate(7deg)}50%{transform:rotate(-50deg)}}
/* the hand leads, the box follows a beat later and settles — that lag is what
   reads as "he pushed it" rather than "they both slid" */
@keyframes pushArm{0%,12%{transform:translateX(0)}28%{transform:translateX(5px)}46%,100%{transform:translateX(0)}}
@keyframes boxNudge{0%,16%{transform:translateX(0)}32%{transform:translateX(7px)}50%{transform:translateX(2px)}66%,100%{transform:translateX(0)}}
@keyframes ballA{0%,100%{transform:translateY(0)}50%{transform:translateY(-24px)}}
@keyframes ballB{0%,100%{transform:translateY(-24px)}50%{transform:translateY(0)}}
@keyframes jugR{0%,100%{transform:rotate(0)}50%{transform:rotate(-8deg)}}
@keyframes jugL{0%,100%{transform:rotate(0)}50%{transform:rotate(8deg)}}

.a-draw,.a-push,.a-jugR,.a-jugL{transform-box:view-box;transform-origin:0 0}

html.tour-live .scene [data-zone-id="z-creative"].is-active .a-draw{animation:drawHand 2.4s ease-in-out infinite}
html.tour-live .scene [data-zone-id="z-creative"].is-active .a-push{animation:pushArm 3.4s ease-in-out infinite}
html.tour-live .scene [data-zone-id="z-creative"].is-active .a-box {animation:boxNudge 3.4s ease-in-out infinite}
html.tour-live .scene [data-zone-id="z-creative"].is-active .a-ballA{animation:ballA 1.8s ease-in-out infinite}
html.tour-live .scene [data-zone-id="z-creative"].is-active .a-ballB{animation:ballB 1.8s ease-in-out infinite}
html.tour-live .scene [data-zone-id="z-creative"].is-active .a-jugR{animation:jugR 1.8s ease-in-out infinite}
html.tour-live .scene [data-zone-id="z-creative"].is-active .a-jugL{animation:jugL 1.8s ease-in-out infinite}

/* -------------------------------------------------------------------------
   GROW TOGETHER — the hands-up wave.

   One keyframe pair drives all four children. Each child sets its own --rd
   (raise delay) inline; custom properties inherit, so the arms read it out of
   the shorthand and the cascade needs no extra selectors. The overshoot on the
   way up is what makes it read as an eager hand shooting up rather than a
   mechanical sweep; coming down is a plain ease so it settles.

   The CSS angle ADDS to each arm's inline resting rotate(): seated arms rest
   at 75deg/105deg (splayed slightly out, matching the original k-sit arms) and
   the standing girl's hang at 90deg, so one shared -/+140deg lands all of them
   in a natural "both hands up".
   ------------------------------------------------------------------------- */
@keyframes raiseR{
  0%      {transform:rotate(0)}
  10%     {transform:rotate(0);          animation-timing-function:cubic-bezier(.28,1.5,.5,1)}
  26%     {transform:rotate(-140deg)}
  62%     {transform:rotate(-140deg);    animation-timing-function:cubic-bezier(.4,0,.7,.6)}
  78%,100%{transform:rotate(0)}
}
@keyframes raiseL{
  0%      {transform:rotate(0)}
  10%     {transform:rotate(0);          animation-timing-function:cubic-bezier(.28,1.5,.5,1)}
  26%     {transform:rotate(140deg)}
  62%     {transform:rotate(140deg);     animation-timing-function:cubic-bezier(.4,0,.7,.6)}
  78%,100%{transform:rotate(0)}
}
.a-up-r,.a-up-l{transform-box:view-box;transform-origin:0 0}

html.tour-live .scene [data-zone-id="z-together"].is-active .a-up-r{animation:raiseR 4.8s linear var(--rd,0s) infinite}
html.tour-live .scene [data-zone-id="z-together"].is-active .a-up-l{animation:raiseL 4.8s linear var(--rd,0s) infinite}

/* -------------------------------------------------------------------------
   SPEAK & SHINE — one 6.4s cycle, three beats of cause and effect.

     0.4 – 2.8s  she lifts and opens both arms on her line
     0.5 – 2.9s  the mic rings out underneath her, three pulses
     2.9 – 4.8s  the audience claps, five times, the two of them offbeat

   The rings scale up while their stroke thins, so an expanding ring reads as
   fading sound instead of a fattening doughnut. Animating the `r` attribute
   would be the obvious way and is deliberately avoided: SVG geometry-as-CSS
   is not Baseline, the same reason the page fold does not morph its path.
   ------------------------------------------------------------------------- */
@keyframes gestR{0%,6%{transform:rotate(0);animation-timing-function:cubic-bezier(.25,1.4,.5,1)}
  18%,34%{transform:rotate(-30deg)}46%,100%{transform:rotate(0)}}
@keyframes gestL{0%,6%{transform:rotate(0);animation-timing-function:cubic-bezier(.25,1.4,.5,1)}
  18%,34%{transform:rotate(30deg)}46%,100%{transform:rotate(0)}}

@keyframes micRing{
  0%,8%   {transform:scale(.55);opacity:0;  stroke-width:3}
  11%     {opacity:.7}
  30%     {transform:scale(2.8);opacity:0;  stroke-width:.9}
  100%    {transform:scale(2.8);opacity:0;  stroke-width:.9}
}
/* five quick claps, then hands rest again for the remaining ~25% of the cycle */
@keyframes clapR{0%,45%{transform:rotate(0)}
  48%{transform:rotate(50deg)}51%{transform:rotate(0)}
  54%{transform:rotate(50deg)}57%{transform:rotate(0)}
  60%{transform:rotate(50deg)}63%{transform:rotate(0)}
  66%{transform:rotate(50deg)}69%{transform:rotate(0)}
  72%{transform:rotate(50deg)}75%,100%{transform:rotate(0)}}
@keyframes clapL{0%,45%{transform:rotate(0)}
  48%{transform:rotate(-50deg)}51%{transform:rotate(0)}
  54%{transform:rotate(-50deg)}57%{transform:rotate(0)}
  60%{transform:rotate(-50deg)}63%{transform:rotate(0)}
  66%{transform:rotate(-50deg)}69%{transform:rotate(0)}
  72%{transform:rotate(-50deg)}75%,100%{transform:rotate(0)}}

.a-gest-r,.a-gest-l,.a-clap-r,.a-clap-l{transform-box:view-box;transform-origin:0 0}
.a-ring,.a-ring2,.a-ring3{transform-box:view-box;transform-origin:0 0;opacity:0;stroke-width:3}

html.tour-live .scene [data-zone-id="z-speak"].is-active .a-gest-r{animation:gestR 6.4s linear infinite}
html.tour-live .scene [data-zone-id="z-speak"].is-active .a-gest-l{animation:gestL 6.4s linear infinite}
html.tour-live .scene [data-zone-id="z-speak"].is-active .a-ring {animation:micRing 6.4s ease-out infinite}
html.tour-live .scene [data-zone-id="z-speak"].is-active .a-ring2{animation:micRing 6.4s ease-out .5s infinite}
html.tour-live .scene [data-zone-id="z-speak"].is-active .a-ring3{animation:micRing 6.4s ease-out 1s infinite}
html.tour-live .scene [data-zone-id="z-speak"].is-active .a-clap-r{animation:clapR 6.4s ease-in-out var(--cd,0s) infinite}
html.tour-live .scene [data-zone-id="z-speak"].is-active .a-clap-l{animation:clapL 6.4s ease-in-out var(--cd,0s) infinite}

/* -------------------------------------------------------------------------
   MOVE & BALANCE — three independent activities, deliberately NOT on a shared
   cycle: a beam walk, a ring swing and a set of squats have no causal link, so
   letting them run at 8s / 3.2s / 2.8s keeps the corner alive instead of
   pulsing in unison.
   ------------------------------------------------------------------------- */
/* two steps out, a pause at the far end, two steps back. The 2px rise at each
   half-step is the weight shift — without it the figure slides rather than walks */
@keyframes beamWalk{
  0%,6%    {transform:translate(0,0)}
  9.5%     {transform:translate(11px,-2px)}
  13%      {transform:translate(22px,0)}
  16.5%    {transform:translate(33px,-2px)}
  20%,40%  {transform:translate(44px,0)}
  43.5%    {transform:translate(33px,-2px)}
  47%      {transform:translate(22px,0)}
  50.5%    {transform:translate(11px,-2px)}
  54%,100% {transform:translate(0,0)}
}
/* legs alternate, and swing the other way on the return trip */
@keyframes stepL{0%,13%{transform:rotate(0)}16.5%{transform:rotate(-17deg)}20%,47%{transform:rotate(0)}
  50.5%{transform:rotate(17deg)}54%,100%{transform:rotate(0)}}
@keyframes stepR{0%,6%{transform:rotate(0)}9.5%{transform:rotate(-17deg)}13%,40%{transform:rotate(0)}
  43.5%{transform:rotate(17deg)}47%,100%{transform:rotate(0)}}

@keyframes ringSwing{0%,100%{transform:rotate(-6.5deg)}50%{transform:rotate(6.5deg)}}

@keyframes squatBody{0%,8%{transform:translateY(0)}42%,58%{transform:translateY(14.3px)}92%,100%{transform:translateY(0)}}
/* knees out, shins back by the same angle: the leg shortens 42 -> 27.7 and the
   foot lands back on its own starting point, so nothing slides on the floor */
@keyframes thighR{0%,8%{transform:rotate(0)}42%,58%{transform:rotate(-48.7deg)}92%,100%{transform:rotate(0)}}
@keyframes shinR {0%,8%{transform:rotate(0)}42%,58%{transform:rotate(97.4deg)}  92%,100%{transform:rotate(0)}}
@keyframes thighL{0%,8%{transform:rotate(0)}42%,58%{transform:rotate(48.7deg)}  92%,100%{transform:rotate(0)}}
@keyframes shinL {0%,8%{transform:rotate(0)}42%,58%{transform:rotate(-97.4deg)} 92%,100%{transform:rotate(0)}}
@keyframes squatArmR{0%,8%{transform:rotate(0)}  42%,58%{transform:rotate(-28deg)}92%,100%{transform:rotate(0)}}
@keyframes squatArmL{0%,8%{transform:rotate(0)}  42%,58%{transform:rotate(28deg)} 92%,100%{transform:rotate(0)}}

.a-legL,.a-legR,.a-swing,.a-sqarm-r,.a-sqarm-l,
.a-thighR,.a-thighL,.a-shinR,.a-shinL{transform-box:view-box;transform-origin:0 0}

html.tour-live .scene [data-zone-id="z-move"].is-active .a-walk  {animation:beamWalk 8s ease-in-out infinite}
html.tour-live .scene [data-zone-id="z-move"].is-active .a-legL  {animation:stepL 8s ease-in-out infinite}
html.tour-live .scene [data-zone-id="z-move"].is-active .a-legR  {animation:stepR 8s ease-in-out infinite}
html.tour-live .scene [data-zone-id="z-move"].is-active .a-swing {animation:ringSwing 3.2s ease-in-out infinite}
html.tour-live .scene [data-zone-id="z-move"].is-active .a-sqbody{animation:squatBody 2.8s ease-in-out infinite}
html.tour-live .scene [data-zone-id="z-move"].is-active .a-thighR{animation:thighR 2.8s ease-in-out infinite}
html.tour-live .scene [data-zone-id="z-move"].is-active .a-shinR {animation:shinR 2.8s ease-in-out infinite}
html.tour-live .scene [data-zone-id="z-move"].is-active .a-thighL{animation:thighL 2.8s ease-in-out infinite}
html.tour-live .scene [data-zone-id="z-move"].is-active .a-shinL {animation:shinL 2.8s ease-in-out infinite}
html.tour-live .scene [data-zone-id="z-move"].is-active .a-sqarm-r{animation:squatArmR 2.8s ease-in-out infinite}
html.tour-live .scene [data-zone-id="z-move"].is-active .a-sqarm-l{animation:squatArmL 2.8s ease-in-out infinite}

/* -------------------------------------------------------------------------
   BUILD & THINK — one 7.2s chain, hand before block every time.

     0.6 – 1.9s  the girl sweeps the purple ball in across the table
     1.6 – 2.7s  the boy reaches; the green block lands as his hand arrives
     2.6 – 3.5s  the small teal block tops the stack out
     3.2 – 3.8s  the whole model shudders about its base
     3.4 – 5.0s  the idea pops above it
     6.0 – 7.2s  the two loose pieces are taken back, ready to go again

   The bottom course never animates. A base block that blinked out to re-drop
   would leave everything above it hanging in mid-air for a beat.
   ------------------------------------------------------------------------- */
@keyframes ballIn{0%,10%{transform:translateX(34px)}20%,84%{transform:translateX(0)}91%,100%{transform:translateX(34px)}}

@keyframes blockC{
  0%,24%   {transform:translateY(-60px);opacity:0}
  27%      {opacity:1}
  33%      {transform:translateY(0)}
  35%      {transform:translateY(-3px)}   /* seat bounce */
  37%,84%  {transform:translateY(0);opacity:1}
  89%,100% {transform:translateY(0);opacity:0}
}
@keyframes blockE{
  0%,36%   {transform:translateY(-60px);opacity:0}
  39%      {opacity:1}
  45%      {transform:translateY(0)}
  47%      {transform:translateY(-3px)}
  49%,84%  {transform:translateY(0);opacity:1}
  89%,100% {transform:translateY(0);opacity:0}
}
/* the shudder decays, which is what makes it read as weight settling */
@keyframes towerWobble{0%,45%{transform:rotate(0)}47%{transform:rotate(1.6deg)}
  49%{transform:rotate(-1.1deg)}51%{transform:rotate(.6deg)}53%,100%{transform:rotate(0)}}

@keyframes reachB{0%,22%{transform:rotate(0)}32%,38%{transform:rotate(-31.2deg)}48%,100%{transform:rotate(0)}}
@keyframes reachG{0%,4%{transform:rotate(0)}10%{transform:rotate(-41.5deg)}20%,100%{transform:rotate(0)}}

@keyframes ideaPop{0%,46%{transform:scale(.3);opacity:0}50%{transform:scale(1.25);opacity:1}
  54%,64%{transform:scale(1);opacity:1}72%,100%{transform:scale(1);opacity:0}}
@keyframes rayOut{0%,46%{transform:scale(.5)}54%{transform:scale(1)}70%,100%{transform:scale(1.3)}}

.a-tower,.a-reach-b,.a-reach-g,.a-spark,.a-rays{transform-box:view-box;transform-origin:0 0}
.a-spark{opacity:0}

html.tour-live .scene [data-zone-id="z-build"].is-active .a-ball-in{animation:ballIn 7.2s ease-in-out infinite}
html.tour-live .scene [data-zone-id="z-build"].is-active .a-blk-c  {animation:blockC 7.2s ease-in infinite}
html.tour-live .scene [data-zone-id="z-build"].is-active .a-blk-e  {animation:blockE 7.2s ease-in infinite}
html.tour-live .scene [data-zone-id="z-build"].is-active .a-tower  {animation:towerWobble 7.2s ease-out infinite}
html.tour-live .scene [data-zone-id="z-build"].is-active .a-reach-b{animation:reachB 7.2s ease-in-out infinite}
html.tour-live .scene [data-zone-id="z-build"].is-active .a-reach-g{animation:reachG 7.2s ease-in-out infinite}
html.tour-live .scene [data-zone-id="z-build"].is-active .a-spark  {animation:ideaPop 7.2s ease-out infinite}
html.tour-live .scene [data-zone-id="z-build"].is-active .a-rays   {animation:rayOut 7.2s ease-out infinite}

/* -------------------------------------------------------------------------
   DISCOVERY LAB — she picks the glass up off the floor and scans with it.

     always       bubbles rise from the flask, three of them, 2.2s each
     0.6 – 1.6s   she reaches down to the glass lying on the floor
     1.6 – 2.5s   picks it up and raises it over the flask
     2.5 – 4.4s   scans across and back, leaning in
     5.0 – 6.2s   sets it down again, and her arm returns

   Two joints carry it: shoulder + wrist. Both bases are the FLOOR pose so
   CSS 0 is the grab, which is what lets the floor copy and the in-hand copy
   be swapped at 20% and 72% with no pop.
   ------------------------------------------------------------------------- */
/* Arm and wrist bases are the FLOOR pose, so CSS 0 IS the grab — which is why
   the copy swap at 20% and 72% needs no arithmetic and shows no pop. The lean
   is also 0 at both those instants; if it were not, the in-hand copy would sit
   a few units off the floor copy and the swap would flicker. */
@keyframes pickArm{
  0%,8%    {transform:rotate(-50deg)}    /* resting, hand near the table edge */
  18%,22%  {transform:rotate(0)}         /* down on the handle — SWAP WINDOW */
  34%      {transform:rotate(-73.4deg)}  /* raised, glass over the flask */
  44%      {transform:rotate(-55deg)}    /* scan across */
  54%      {transform:rotate(-73.4deg)}  /* scan back */
  68%,74%  {transform:rotate(0)}         /* set it down — SWAP WINDOW */
  84%,100% {transform:rotate(-50deg)}
}
@keyframes pickWrist{0%,22%{transform:rotate(0)}34%,54%{transform:rotate(-41.1deg)}68%,100%{transform:rotate(0)}}
/* 1% of 7.4s = 74ms, and the two copies are coincident, so this reads as nothing */
@keyframes magFloor{0%,20%{opacity:1}21%,71%{opacity:0}72%,100%{opacity:1}}
@keyframes magHeld {0%,20%{opacity:0}21%,71%{opacity:1}72%,100%{opacity:0}}
@keyframes bubUp{0%{transform:translateY(0);opacity:0}14%{opacity:.7}72%{opacity:.45}100%{transform:translateY(-34px);opacity:0}}
@keyframes leanIn{0%,24%{transform:rotate(0)}32%,58%{transform:rotate(6deg)}66%,100%{transform:rotate(0)}}

.a-lean,.a-arm-g,.a-wrist{transform-box:view-box;transform-origin:0 0}
.a-mag-held{opacity:0}

html.tour-live .scene [data-zone-id="z-discovery"].is-active .a-arm-g    {animation:pickArm 7.4s ease-in-out infinite}
html.tour-live .scene [data-zone-id="z-discovery"].is-active .a-wrist    {animation:pickWrist 7.4s ease-in-out infinite}
html.tour-live .scene [data-zone-id="z-discovery"].is-active .a-mag-floor{animation:magFloor 7.4s linear infinite}
html.tour-live .scene [data-zone-id="z-discovery"].is-active .a-mag-held {animation:magHeld 7.4s linear infinite}
html.tour-live .scene [data-zone-id="z-discovery"].is-active .a-lean     {animation:leanIn 7.4s ease-in-out infinite}
/* negative delays so the flask is already bubbling on the first frame */
html.tour-live .scene [data-zone-id="z-discovery"].is-active .a-bub1{animation:bubUp 2.2s ease-out infinite}
html.tour-live .scene [data-zone-id="z-discovery"].is-active .a-bub2{animation:bubUp 2.2s ease-out -.73s infinite}
html.tour-live .scene [data-zone-id="z-discovery"].is-active .a-bub3{animation:bubUp 2.2s ease-out -1.46s infinite}

/* Reduced motion: no fold. The leaf parks at scaleX(1), which is pixel-exact
   over the static right leaf, so the book simply looks like an open book. */
@media (prefers-reduced-motion:reduce){
  .a-turn,.a-turn .pg-face,.a-turn .pg-shade,.a-gutter,
  .a-draw,.a-push,.a-box,.a-ballA,.a-ballB,.a-jugR,.a-jugL,.a-up-r,.a-up-l,.a-gest-r,.a-gest-l,.a-clap-r,.a-clap-l,.a-ring,.a-ring2,.a-ring3,.a-walk,.a-legL,.a-legR,.a-swing,.a-sqbody,.a-sqarm-r,.a-sqarm-l,.a-thighR,.a-thighL,.a-shinR,.a-shinL,.a-ball-in,.a-blk-c,.a-blk-e,.a-tower,.a-reach-b,.a-reach-g,.a-spark,.a-rays,.a-arm-g,.a-wrist,.a-mag-floor,.a-mag-held,.a-lean,.a-bub1,.a-bub2,.a-bub3{animation:none!important}
  .a-turn{transform:scaleX(1)!important}
  .a-blk-c,.a-blk-e,.a-ball-in{opacity:1!important;transform:none!important}
  .a-spark{opacity:0!important}
  .a-bub1,.a-bub2,.a-bub3{opacity:0!important}
  .a-mag-floor{opacity:1!important}
  .a-mag-held{opacity:0!important}
  .a-draw,.a-push,.a-box,.a-ballA,.a-ballB,.a-jugR,.a-jugL,.a-up-r,.a-up-l,.a-gest-r,.a-gest-l,.a-clap-r,.a-clap-l,.a-walk,.a-legL,.a-legR,.a-swing,.a-sqbody,.a-sqarm-r,.a-sqarm-l,.a-thighR,.a-thighL,.a-shinR,.a-shinL,.a-tower,.a-reach-b,.a-reach-g,.a-lean{transform:none!important}
}
body.rm .a-turn,body.rm .a-turn .pg-face,body.rm .a-turn .pg-shade,body.rm .a-gutter,
body.rm .a-draw,body.rm .a-push,body.rm .a-box,body.rm .a-ballA,body.rm .a-ballB,
body.rm .a-jugR,body.rm .a-jugL,body.rm .a-up-r,body.rm .a-up-l,
body.rm .a-gest-r,body.rm .a-gest-l,body.rm .a-clap-r,body.rm .a-clap-l,
body.rm .a-ring,body.rm .a-ring2,body.rm .a-ring3,
body.rm .a-walk,body.rm .a-legL,body.rm .a-legR,body.rm .a-swing,
body.rm .a-sqbody,body.rm .a-sqarm-r,body.rm .a-sqarm-l,
body.rm .a-thighR,body.rm .a-thighL,body.rm .a-shinR,body.rm .a-shinL,
body.rm .a-ball-in,body.rm .a-blk-c,body.rm .a-blk-e,body.rm .a-tower,
body.rm .a-reach-b,body.rm .a-reach-g,body.rm .a-spark,body.rm .a-rays,
body.rm .a-arm-g,body.rm .a-wrist,body.rm .a-mag-floor,body.rm .a-mag-held,body.rm .a-lean,
body.rm .a-bub1,body.rm .a-bub2,body.rm .a-bub3{animation:none!important}
body.rm .a-turn{transform:scaleX(1)!important}
body.rm .a-blk-c,body.rm .a-blk-e,body.rm .a-ball-in{opacity:1!important;transform:none!important}
body.rm .a-spark{opacity:0!important}
body.rm .a-bub1,body.rm .a-bub2,body.rm .a-bub3{opacity:0!important}
body.rm .a-mag-floor{opacity:1!important}
body.rm .a-mag-held{opacity:0!important}

/* the marketing page steps aside while the experience is on stage */
.site .nav{transition:opacity .5s var(--ease),transform .5s var(--ease)}
.site.quiet .nav{opacity:0;transform:translateY(-14px);pointer-events:none}
body.tour-on .site{visibility:hidden}

/* tour chrome */
.tour-top{
  position:absolute;top:0;left:0;right:0;z-index:18;padding:16px 20px;
  display:flex;align-items:center;gap:14px;
  background:linear-gradient(180deg,rgba(253,249,241,.94),rgba(253,249,241,0));
  pointer-events:none;
}
.tour-top>*{pointer-events:auto}
.tour-top .tlogo{width:104px}
.tour-tools{margin-left:auto;display:flex;align-items:center;gap:10px}
.chip{
  display:inline-flex;align-items:center;gap:8px;background:#fff;border-radius:999px;
  padding:9px 15px;box-shadow:var(--sh-1);font-family:var(--head);font-weight:600;font-size:13.5px;color:var(--ink);
  transition:box-shadow .2s var(--ease),transform .2s var(--ease);
}
.chip:hover{box-shadow:var(--sh-2);transform:translateY(-1px)}
.chip svg{width:16px;height:16px;flex:none}
.chip.icon-only{padding:9px;width:38px;height:38px;justify-content:center}

.tour-bottom{position:absolute;left:0;right:0;bottom:0;z-index:12;padding:20px;display:flex;justify-content:center;pointer-events:none;
  transition:opacity .32s var(--ease),transform .32s var(--ease)}
.tour-bottom>*{pointer-events:auto}

/* zone information card */
.zcardpanel{
  position:absolute;z-index:14;right:24px;top:50%;transform:translateY(-46%) translateX(28px);
  width:372px;max-height:78vh;overflow:auto;background:#fff;border-radius:var(--r-panel);
  box-shadow:var(--sh-3);padding:20px;opacity:0;visibility:hidden;
  transition:opacity .38s var(--ease),transform .42s var(--ease),visibility 0s .42s;
}
.zcardpanel.on{opacity:1;visibility:visible;transform:translateY(-50%) translateX(0);transition:opacity .38s var(--ease),transform .42s var(--ease)}
.zp-top{display:flex;align-items:flex-start;gap:12px;margin-bottom:14px}
.zp-ico{width:44px;height:44px;border-radius:14px;background:var(--t-teal);display:grid;place-items:center;flex:none}
.zp-ico svg{width:22px;height:22px;fill:var(--teal)}
.zcardpanel[data-accent="yellow"] .zp-ico{background:var(--t-yellow)}
.zcardpanel[data-accent="yellow"] .zp-ico svg{fill:#B78A0E}
.zcardpanel[data-accent="green"] .zp-ico{background:var(--t-green)}
.zcardpanel[data-accent="green"] .zp-ico svg{fill:#3B9A61}
.zp-top h3{font-size:21px}
.zp-top .tl{font-size:12.5px;font-weight:600;font-family:var(--head);color:var(--teal);letter-spacing:.02em;margin-top:3px}
.zp-close{margin-left:auto;width:32px;height:32px;border-radius:50%;background:var(--t-neutral);display:grid;place-items:center;flex:none;transition:background-color .2s}
.zp-close:hover{background:#E7EBEC}
.zp-close svg{width:14px;height:14px;stroke:var(--ink-60);stroke-width:2}
.zp-art{border-radius:16px;overflow:hidden;aspect-ratio:16/9;background:var(--t-neutral);margin-bottom:14px}
.zp-art svg{width:100%;height:100%}
.zp-desc{font-size:14.5px;color:var(--ink-60);margin-bottom:14px}
.zp-tags{margin-bottom:18px}
.zp-actions{display:grid;gap:9px}

/* nudge / lead trigger toast */
.nudge{
  position:absolute;z-index:15;left:50%;bottom:88px;transform:translateX(-50%) translateY(14px);
  background:#fff;border-radius:22px;box-shadow:var(--sh-3);padding:14px 16px;
  display:flex;align-items:center;gap:14px;max-width:min(92vw,470px);
  opacity:0;visibility:hidden;transition:opacity .4s var(--ease),transform .4s var(--ease),visibility 0s .4s;
}
.nudge.on{opacity:1;visibility:visible;transform:translateX(-50%) translateY(0);transition:opacity .4s var(--ease),transform .4s var(--ease)}
/* never stack two identical CTAs */
.tour.nudging .tour-bottom{opacity:0;transform:translateY(24px);pointer-events:none}
.nudge .em{width:38px;height:38px;border-radius:12px;background:var(--t-yellow);display:grid;place-items:center;flex:none}
.nudge .em svg{width:20px;height:20px;fill:#C79A10}
.nudge p{font-size:14px;font-family:var(--head);font-weight:600;line-height:1.3}
.nudge .x{width:26px;height:26px;border-radius:50%;display:grid;place-items:center;flex:none;color:var(--ink-40);font-size:17px;line-height:1}

/* =========================================================================
   MODAL — plan a visit
   ========================================================================= */
.modal{position:fixed;inset:0;z-index:90;display:grid;place-items:center;padding:20px;
  opacity:0;visibility:hidden;transition:opacity .3s ease,visibility 0s .3s}
.modal.on{opacity:1;visibility:visible;transition:opacity .3s ease}
.modal-bg{position:absolute;inset:0;background:rgba(38,50,56,.42);backdrop-filter:blur(3px)}
.modal-box{
  position:relative;width:min(100%,430px);background:#fff;border-radius:var(--r-panel);
  box-shadow:var(--sh-3);padding:26px 24px;transform:translateY(12px) scale(.98);
  transition:transform .34s var(--ease);max-height:92vh;overflow:auto;
}
.modal.on .modal-box{transform:none}
.modal-box h3{font-size:23px;text-align:center;color:var(--teal-deep)}
.modal-box .sub{text-align:center;font-size:13.5px;color:var(--ink-40);margin:7px 0 20px}
.modal-close{position:absolute;right:14px;top:14px;width:34px;height:34px;border-radius:50%;
  background:var(--t-neutral);display:grid;place-items:center;transition:background-color .2s}
.modal-close:hover{background:#E7EBEC}
.modal-close svg{width:14px;height:14px;stroke:var(--ink-60);stroke-width:2}
.modal .success{display:none;text-align:center;padding:14px 0}
.modal.done .success{display:block;animation:popIn .5s var(--ease) both}
.modal.done .formwrap,.modal.done>.modal-box>h3,.modal.done>.modal-box>.sub{display:none}
@keyframes popIn{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:none}}
.success .badge{width:74px;height:74px;border-radius:50%;background:var(--t-green);display:grid;place-items:center;margin:0 auto 18px}
.success .badge svg{width:34px;height:34px;stroke:var(--green);stroke-width:2.4;fill:none}
.success h3{font-size:22px;margin-bottom:10px}
.success p{font-size:14.5px;color:var(--ink-60);margin-bottom:22px;max-width:34ch;margin-inline:auto}
.btn.loading{pointer-events:none;opacity:.85}
.btn.loading .lbl{opacity:.5}
.spinner{width:17px;height:17px;border-radius:50%;border:2.2px solid rgba(255,255,255,.35);border-top-color:#fff;
  animation:spin .7s linear infinite;display:none}
.btn.loading .spinner{display:block}
@keyframes spin{to{transform:rotate(360deg)}}
.note{font-size:11.5px;color:var(--ink-40);text-align:center;margin-top:14px;line-height:1.45}

/* mobile menu */
.mmenu{position:fixed;inset:0;z-index:85;background:#fff;padding:80px 26px 26px;
  opacity:0;visibility:hidden;transform:translateY(-10px);transition:opacity .28s var(--ease),transform .28s var(--ease),visibility 0s .28s}
.mmenu.on{opacity:1;visibility:visible;transform:none;transition:opacity .28s var(--ease),transform .28s var(--ease)}
.mmenu a{display:block;font-family:var(--head);font-weight:600;font-size:22px;padding:14px 0;border-bottom:1px solid var(--line)}
.mmenu .close{position:absolute;right:22px;top:22px;width:44px;height:44px;border-radius:14px;background:var(--t-neutral);display:grid;place-items:center;font-size:22px}

/* =========================================================================
   RESPONSIVE — mobile is a designed experience, not a squeezed desktop
   ========================================================================= */
@media (max-width:1120px){
  .hero-grid{grid-template-columns:1fr 1fr;gap:26px}
  .plan{grid-column:1/-1;max-width:420px;margin-inline:auto;width:100%}
  .pillars{grid-template-columns:repeat(2,1fr);gap:30px}
  .bento{grid-template-columns:repeat(2,1fr);grid-auto-rows:210px}
  .bcard:nth-child(1){grid-column:span 2;grid-row:span 2}
  .bcard:nth-child(4),.bcard:nth-child(7){grid-column:span 1}
  .bcard:nth-child(1) h4{font-size:24px}
  .foot-in{grid-template-columns:1fr 1fr}
  .offergrid{grid-template-columns:repeat(2,1fr)}
  .bands{grid-template-columns:repeat(2,1fr)}
  .vwall{grid-template-columns:repeat(2,1fr)}
  .voice.lead{grid-column:span 2;grid-row:span 1}
  .voice.lead blockquote{font-size:22px}
}
@media (max-width:900px){
  .nav-links{display:none}
  .nav-wa{display:none}
  .nav-burger{display:grid}
  .nav-in{justify-content:space-between}
  .brand img{width:112px}
  .closing-in{grid-template-columns:1fr}
  .closing-art{display:none}
  .closing-copy{padding:44px 0}
  .zcardpanel{
    right:0;left:0;top:auto;bottom:0;width:100%;max-height:60vh;
    border-radius:26px 26px 0 0;transform:translateY(100%);padding:8px 18px 18px;
  }
  .zp-art{aspect-ratio:16/8}
  .swipehint{bottom:74px}
  .zcardpanel.on{transform:translateY(0)}
  .zcardpanel::before{content:"";display:block;width:42px;height:4px;border-radius:3px;background:rgba(38,50,56,.16);margin:8px auto 14px}
  .nudge{bottom:auto;top:74px;left:12px;right:12px;transform:translateY(-14px);max-width:none}
  .nudge.on{transform:translateY(0)}
  .tour-bottom{justify-content:stretch;padding:14px}
  .tour-bottom .btn{width:100%}
  /* the bottom sheet carries its own Book a Visit — don't stack two bars */
  .tour.zoomed .tour-bottom{opacity:0;transform:translateY(24px);pointer-events:none}
  .tour.zoomed .tour-bottom>*{pointer-events:none}
  .progress .pt{display:none}
}
@media (max-width:760px){
  .sec{padding:62px 0}
  .hero{padding:92px 0 40px}
  .hero-grid{grid-template-columns:1fr;text-align:center}
  .hero-copy{order:1}
  .doorframe{order:2;max-width:440px;margin-inline:auto}
  .squiggle{justify-content:center}
  .hero-copy p.lede{margin-inline:auto}
  .steps{grid-template-columns:1fr;gap:34px}
  .steps .dash{display:none}
  .bento{gap:13px;grid-auto-rows:180px}
  .bcard .cap{padding:16px 17px}
  .bcard h4{font-size:17px}
  .foot-in{grid-template-columns:1fr;gap:26px}
  .sec-head{margin-bottom:36px}
  .visitgrid{grid-template-columns:1fr}
  .safelist{grid-template-columns:1fr}
  .vwall{grid-template-columns:1fr}
  .voice.lead{grid-column:span 1;padding:30px 26px}
  .voice.lead blockquote{font-size:20px}
  .ages{padding:26px 20px}
}
@media (max-width:520px){
  .bento{grid-template-columns:1fr;grid-auto-rows:220px}
  .bcard:nth-child(n){grid-column:span 1;grid-row:span 1}
  .bcard:nth-child(1) h4{font-size:22px}
  .pillars{grid-template-columns:1fr}
  .offergrid{grid-template-columns:1fr}
  .bands{grid-template-columns:1fr}
  .faqitem summary{font-size:15.5px;padding:17px 18px}
  .faqa{padding:0 18px 18px}
  .vcard,.vmap{padding:24px 20px}
  .tour-top{padding:12px}
  .tour-top .tlogo{width:74px}
  .tour-tools{gap:7px}
  .chip span{display:none}
  .chip{padding:9px;width:38px;height:38px;justify-content:center}
  .progress{padding:8px 11px}
  .dots i{width:6px;height:6px}
  .explore-menu{width:216px}
}

/* =========================================================================
   REDUCED MOTION  (brief §13)
   ========================================================================= */
@media (prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  *,*::before,*::after{animation-duration:.001ms!important;animation-iteration-count:1!important}
  .world{transition-duration:.28s}
  .doorway-stage,.dw-panel,.bloom{transition-duration:.001ms!important}
  .peek{animation:none!important}
  .hs::before{display:none}
}
body.rm .world{transition-duration:.26s}
body.rm .hs::before{display:none}
body.rm .live{animation:none!important}
/* ==========================================================================
   WordPress additions
   Elements the static template did not have: the DPDP consent checkbox, the
   bot honeypot and the server-error line. Kept at the end of the file, and
   scoped tightly, so a diff against the original stylesheet stays readable.
   ========================================================================== */

.field-consent{position:relative;margin:2px 0 4px}
.field-consent label{display:flex;gap:9px;align-items:flex-start;cursor:pointer;
  font-size:.82rem;line-height:1.4;color:var(--ink-60)}
/* .field input is styled as a full-width control; a checkbox must opt out of
   all of it — width, padding, border, background and the icon inset. */
.field-consent input[type=checkbox]{
  -webkit-appearance:auto;appearance:auto;
  flex:0 0 auto;width:16px;height:16px;min-width:16px;max-width:16px;
  margin:.12em 0 0;padding:0;border:0;border-radius:0;background:none;
  box-shadow:none;position:static;inset:auto;
  accent-color:var(--teal);cursor:pointer}
.field-consent a{color:var(--teal);text-decoration:underline}
.field-consent .err{margin-left:25px}
.field-consent.invalid label{color:#8A1F1F}

/* Honeypot: off-screen, never focusable, invisible to assistive tech. */
.hp{position:absolute!important;left:-9999px!important;width:1px;height:1px;
  overflow:hidden}

.form-error{margin:10px 0 0;padding:10px 12px;border-radius:var(--r-btn);
  background:#FDECEC;color:#8A1F1F;font-size:.86rem;line-height:1.4}
.form-error[hidden]{display:none}

/* The closing block's address line. */
.closing-loc{display:flex;gap:8px;align-items:center;justify-content:center;
  margin-top:16px;font-size:.9rem;opacity:.85}
.closing-loc svg{width:18px;height:18px;flex:0 0 auto}

.foot-legal{display:inline-flex;gap:14px;list-style:none;margin:0;padding:0}
.foot-legal a{color:inherit;opacity:.8}
.foot-legal a:hover{opacity:1}

/* Bug carried over from the source template: `.safe` is only styled as
   `.plan .safe`, so the copy inside the lead modal — which is not inside
   .plan — rendered its shield icon at its intrinsic 374px. Same defect in
   joydo-experience-template_18.html; verified before changing anything.
   This is the one deliberate visual difference from that file. */
.modal .safe{display:flex;align-items:center;justify-content:center;gap:7px;
  font-size:11.5px;color:var(--ink-40);text-align:center}
.modal .safe svg{width:14px;height:14px;flex:none;opacity:.5}

/* ==========================================================================
   User-agent default restoration
   The static template was authored against raw browser defaults. WordPress
   ships a reset (wp-block-library) that zeroes several of them, which made
   these elements render wider/shorter than in the template. Each value below
   is the UA default the template silently depended on, made explicit so the
   layout no longer relies on a reset that may change.
   ========================================================================== */

/* <figure> default `margin: 1em 40px`. Without it the testimonial cards fill
   their full 400px grid column instead of the template's 320px. */
.voice{margin:16px 40px}

/* <h5> default `margin-block-start: 1.67em`. The stylesheet only sets the
   bottom margin, so the top one came from the UA sheet. */
.vrow h5{margin-top:1.67em}

/* ==========================================================================
   Photo gallery lightbox
   New UI, not part of the approved template — namespaced `jgal-` so it cannot
   collide with template selectors. Built on the existing design tokens
   (--teal, --r-panel, --sh-3, --ease) so it reads as the same product.
   ========================================================================== */

.jgal{position:fixed;inset:0;z-index:120;display:grid;place-items:center;padding:24px}
.jgal[hidden]{display:none}

.jgal-bg{position:absolute;inset:0;background:rgba(20,30,34,.72);
  -webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px);opacity:0;
  transition:opacity .32s var(--ease)}
.jgal.open .jgal-bg{opacity:1}

.jgal-box{position:relative;width:min(100% - 16px,1080px);max-height:calc(100dvh - 48px);
  display:flex;flex-direction:column;background:var(--white);
  border-radius:var(--r-panel);box-shadow:var(--sh-3);overflow:hidden;
  opacity:0;transform:translateY(14px) scale(.985);
  transition:opacity .34s var(--ease),transform .34s var(--ease)}
.jgal.open .jgal-box{opacity:1;transform:none}

/* ---- header ---- */
.jgal-head{display:flex;align-items:flex-start;gap:18px;
  padding:22px 24px 16px;border-bottom:1px solid var(--line)}
.jgal-titles{min-width:0}
.jgal-head h3{font-family:var(--head);font-size:22px;line-height:1.2;color:var(--ink);margin:0}
.jgal-sub{margin:4px 0 0;font-size:13px;color:var(--ink-40);letter-spacing:.02em}
.jgal-x{margin-left:auto;flex:none;width:40px;height:40px;border:0;border-radius:50%;
  background:var(--t-neutral);color:var(--ink-60);cursor:pointer;display:grid;place-items:center;
  transition:background .2s var(--ease),color .2s var(--ease)}
.jgal-x svg{width:18px;height:18px}
.jgal-x:hover{background:var(--t-teal);color:var(--teal-deep)}

/* ---- stage ---- */
.jgal-stage{position:relative;flex:1;min-height:0;display:grid;
  grid-template-columns:auto 1fr auto;align-items:center;gap:10px;padding:18px 18px 6px}
.jgal-figure{grid-column:2;margin:0;min-width:0;display:flex;flex-direction:column;gap:12px}
.jgal-imgwrap{position:relative;display:grid;place-items:center;
  background:var(--t-neutral);border-radius:18px;overflow:hidden;min-height:220px}
.jgal-img{display:block;max-width:100%;max-height:min(60dvh,560px);width:auto;height:auto;
  object-fit:contain;opacity:0;transition:opacity .28s var(--ease)}
.jgal-img.ready{opacity:1}
.jgal-cap{margin:0;text-align:center;font-size:14px;color:var(--ink-60);line-height:1.5;min-height:1.5em}

.jgal-spinner{position:absolute;width:26px;height:26px;border-radius:50%;
  border:2.5px solid rgba(38,50,56,.14);border-top-color:var(--teal);
  animation:jgal-spin .7s linear infinite;opacity:0;transition:opacity .2s}
.jgal-imgwrap.loading .jgal-spinner{opacity:1}
@keyframes jgal-spin{to{transform:rotate(360deg)}}

/* ---- prev / next ---- */
.jgal-nav{flex:none;width:46px;height:46px;border:0;border-radius:50%;cursor:pointer;
  background:var(--white);color:var(--teal-deep);box-shadow:var(--sh-2);
  display:grid;place-items:center;
  transition:transform .2s var(--ease),background .2s var(--ease),opacity .2s var(--ease)}
.jgal-nav svg{width:20px;height:20px}
.jgal-prev{grid-column:1}
.jgal-prev svg{transform:rotate(180deg)}
.jgal-next{grid-column:3}
.jgal-nav:hover{background:var(--t-teal);transform:scale(1.06)}
.jgal-nav:disabled{opacity:.32;cursor:default;transform:none;background:var(--white)}

.jgal-count{position:absolute;right:22px;top:22px;margin:0;
  padding:5px 12px;border-radius:999px;background:rgba(38,50,56,.62);color:#fff;
  font-size:12px;font-weight:600;letter-spacing:.03em}

/* ---- thumbnail strip ---- */
.jgal-strip{display:flex;gap:10px;overflow-x:auto;scrollbar-width:thin;
  padding:14px 24px 18px;border-top:1px solid var(--line);margin-top:8px}
.jgal-strip:empty{display:none}
.jgal-thumb{flex:none;width:74px;height:56px;padding:0;border:0;border-radius:11px;
  overflow:hidden;cursor:pointer;background:var(--t-neutral);
  outline:2.5px solid transparent;outline-offset:2px;opacity:.62;
  transition:opacity .2s var(--ease),outline-color .2s var(--ease)}
.jgal-thumb img{width:100%;height:100%;object-fit:cover;display:block}
.jgal-thumb:hover{opacity:1}
.jgal-thumb[aria-current="true"]{opacity:1;outline-color:var(--teal)}

/* ---- footer ---- */
.jgal-foot{display:flex;justify-content:center;padding:0 24px 22px}
.jgal-foot .btn{gap:8px}
.jgal-foot .ico{width:18px;height:18px}

/* ---- focus visibility ---- */
.jgal button:focus-visible{outline:3px solid var(--teal);outline-offset:2px}

/* ---- small screens: chevrons overlay the image, strip stays scrollable ---- */
@media (max-width:760px){
  .jgal{padding:0}
  .jgal-box{width:100%;max-height:100dvh;border-radius:0}
  .jgal-stage{grid-template-columns:1fr;padding:12px 12px 4px}
  .jgal-figure{grid-column:1}
  .jgal-nav{position:absolute;top:50%;transform:translateY(-50%);z-index:2;width:42px;height:42px}
  .jgal-prev{left:14px}
  .jgal-next{right:14px}
  .jgal-nav:hover{transform:translateY(-50%) scale(1.06)}
  .jgal-img{max-height:52dvh}
  .jgal-head h3{font-size:19px}
}

@media (prefers-reduced-motion:reduce){
  .jgal-bg,.jgal-box,.jgal-img,.jgal-nav,.jgal-thumb{transition:none}
  .jgal-spinner{animation:none}
}

/* --- gallery layout corrections -------------------------------------------
   The stage was sized by the image's own max-height, so on shorter viewports
   the box hit its max-height and the caption overflowed down into the
   thumbnail strip. Let flexbox size the image instead: every ancestor gets
   min-height:0 (without it a flex item refuses to shrink below its content)
   and the image is capped to the space actually left over. */
.jgal-stage{min-height:0;overflow:hidden}
.jgal-figure{min-height:0;height:100%}
.jgal-imgwrap{flex:1 1 auto;min-height:120px}
.jgal-img{max-height:100%;max-width:100%}

/* The dialog is focused programmatically on open so screen readers announce
   the space name. That is not keyboard navigation, so it must not paint a
   focus ring around the entire panel — real controls keep theirs. */
.jgal-box:focus,
.jgal-box:focus-visible{outline:none}

/* Centre the thumbnail strip. `safe` keeps the first thumb reachable when the
   row overflows — plain `center` clips the start of an overflowing flex row. */
.jgal-strip{justify-content:safe center}

/* --- gallery empty state --------------------------------------------------
   Shown when a space has no photos yet. The tile still opens the popup, so
   this has to carry its weight: description, benefit chips, and a route into
   the tour via the footer button that stays visible. */
.jgal.is-empty .jgal-imgwrap{background:var(--t-teal);min-height:260px;padding:34px 28px}
.jgal.is-empty .jgal-count{display:none}
.jgal-empty{max-width:52ch;margin:auto;text-align:center;display:grid;gap:16px;justify-items:center}
.jgal-empty-desc{margin:0;font-size:17px;line-height:1.6;color:var(--ink)}
.jgal-empty-tags{list-style:none;margin:0;padding:0;display:flex;flex-wrap:wrap;gap:8px;justify-content:center}
.jgal-empty-tags li{background:var(--white);border-radius:999px;padding:6px 14px;
  font-size:12.5px;font-weight:600;color:var(--teal-deep)}
.jgal-empty-note{margin:0;font-size:13px;color:var(--ink-40)}
.jgal-empty-note a{color:var(--teal);font-weight:600;text-decoration:underline}

/* The `hidden` attribute only sets display:none in the UA sheet, so any author
   `display` rule silently defeats it. These elements are hidden via JS
   (single-image galleries, and the empty state), so they need it back. */
.jgal-nav[hidden],
.jgal-foot [hidden],
.jgal [hidden]{display:none !important}

/* Breathing room between the thumbnail strip and the tour button. */
.jgal-foot{padding-top:14px}
.jgal.is-empty .jgal-foot{padding-top:20px}

/* ==========================================================================
   Zone panel photo slider
   Replaces the SVG scene crop inside the tour's zone card when the space has
   photos. Sized to the existing .zp-art slot so the panel geometry — which the
   camera framing depends on via viewportRect() — does not change.
   ========================================================================== */

.zp-slider{position:relative;width:100%;aspect-ratio:16/10;border-radius:14px;
  overflow:hidden;background:var(--t-neutral);isolation:isolate}
.zp-shot{width:100%;height:100%;object-fit:cover;display:block;
  opacity:1;transition:opacity .22s var(--ease)}
.zp-shot.in{animation:zp-fade .26s var(--ease)}
@keyframes zp-fade{from{opacity:.25}to{opacity:1}}

.zp-arrow{position:absolute;top:50%;transform:translateY(-50%);z-index:2;
  width:30px;height:30px;border:0;border-radius:50%;cursor:pointer;
  background:rgba(255,255,255,.92);color:var(--teal-deep);
  box-shadow:0 2px 8px rgba(38,50,56,.22);
  display:grid;place-items:center;padding:0;
  transition:background .18s var(--ease),transform .18s var(--ease)}
.zp-arrow svg{width:15px;height:15px}
.zp-back{left:8px}
.zp-back svg{transform:rotate(180deg)}
.zp-fwd{right:8px}
.zp-arrow:hover{background:#fff;transform:translateY(-50%) scale(1.08)}

.zp-dots{position:absolute;left:0;right:0;bottom:8px;z-index:2;
  display:flex;gap:6px;justify-content:center}
.zp-dot{width:7px;height:7px;padding:0;border:0;border-radius:50%;cursor:pointer;
  background:rgba(255,255,255,.55);box-shadow:0 1px 3px rgba(38,50,56,.3);
  transition:background .18s var(--ease),width .18s var(--ease)}
.zp-dot[aria-selected="true"]{background:#fff;width:18px;border-radius:999px}

.zp-num{position:absolute;top:8px;right:8px;z-index:2;
  padding:3px 8px;border-radius:999px;background:rgba(38,50,56,.6);
  color:#fff;font-size:11px;font-weight:600;letter-spacing:.02em}

.zp-slider button:focus-visible{outline:2.5px solid var(--teal);outline-offset:2px}

@media (prefers-reduced-motion:reduce){
  .zp-shot,.zp-arrow,.zp-dot{transition:none}
  .zp-shot.in{animation:none}
}

/* Dots are white; against a pale photo they vanish. A soft scrim along the
   bottom edge guarantees contrast whatever the image happens to be. */
.zp-slider::after{content:"";position:absolute;left:0;right:0;bottom:0;height:46px;
  background:linear-gradient(to top,rgba(20,30,34,.42),rgba(20,30,34,0));
  pointer-events:none;z-index:1}
.zp-slider:not(:has(.zp-dots))::after{display:none}
.zp-dot{box-shadow:0 1px 4px rgba(0,0,0,.45)}

/* The photo was in normal flow with height:100%, which left a sliver of the
   aspect-ratio box uncovered at the bottom — the dots ended up in that gap,
   below the image rather than over it. Pin it to the box instead. */
.zp-slider{display:block}
.zp-shot{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}
.zp-dots{bottom:10px}

/* --- zone slider: dots below the photo ------------------------------------
   Supersedes the overlaid treatment above. Sitting in normal flow, the dots
   no longer depend on the photo's tone or on stacking order to be legible. */
.zp-slider::after{display:none}
.zp-dots{position:static;display:flex;gap:6px;justify-content:center;
  margin:9px 0 0;padding:0}
.zp-dot{width:7px;height:7px;border-radius:50%;background:rgba(38,50,56,.22);
  box-shadow:none;transition:background .18s var(--ease),width .18s var(--ease)}
.zp-dot[aria-selected="true"]{background:var(--teal);width:18px;border-radius:999px}
.zp-dot:hover{background:rgba(38,50,56,.4)}
.zp-dot[aria-selected="true"]:hover{background:var(--teal)}

/* The gallery brings its own rounding and needs to size to its content, so
   the fixed 16/9 clipping slot is released when one is present. */
.zp-art.has-gal{aspect-ratio:auto;overflow:visible;border-radius:0;background:none}

/* ==========================================================================
   Parent voices — photos + vertical carousel
   Supersedes the flat .vwall treatment. The featured quote keeps the left two
   columns; the rest live in a scroll-snapped rail on the right with up/down
   arrows. Native scrolling does the work, so the rail still functions with no
   JavaScript — the arrows only nudge scrollTop.
   ========================================================================== */

.vwall{align-items:stretch}

/* ---- card shell ---- */
.voice{position:relative;overflow:hidden;display:flex;flex-direction:column;isolation:isolate}
.voice-in{position:relative;z-index:2;display:flex;flex-direction:column;height:100%}

.voice-bg{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;z-index:0;
  border-radius:inherit}
/* Fixed scrim: the quote has to stay readable whatever photo is uploaded, so
   legibility cannot depend on the image being dark enough. */
.voice-scrim{position:absolute;inset:0;z-index:1;border-radius:inherit;
  background:linear-gradient(180deg,rgba(16,52,60,.62) 0%,rgba(16,52,60,.78) 100%)}
.voice.has-bg,
.voice.has-bg blockquote,
.voice.has-bg .nm{color:#fff}
.voice.has-bg .mt{color:rgba(255,255,255,.78)}
.voice.has-bg .qm{color:rgba(255,255,255,.5)}
.voice.has-bg .who{border-top-color:rgba(255,255,255,.22)}
.voice.has-bg .ph{background:rgba(255,255,255,.14);color:#fff;border-color:rgba(255,255,255,.4)}

/* ---- parent photo, top centre ---- */
.voice-photo{align-self:center;width:72px;height:72px;border-radius:50%;overflow:hidden;
  flex:none;margin-bottom:16px;background:var(--t-teal);
  display:grid;place-items:center;
  box-shadow:0 2px 10px rgba(38,50,56,.18);border:3px solid rgba(255,255,255,.9)}
.voice-photo img{width:100%;height:100%;object-fit:cover;display:block}
.voice-photo-fallback{display:grid;place-items:center;width:100%;height:100%}
.voice-photo-fallback svg{width:30px;height:30px;color:var(--teal)}
.voice.lead .voice-photo{width:88px;height:88px}

/* Caption centres under a top-centre photo rather than sitting bottom-left. */
.voice .who{display:block;text-align:center}
.voice .who .nm{display:block}
.voice .who .mt{display:block}
.voice .qm{text-align:center}
.voice blockquote{text-align:center}
.voice-in .who{margin-top:auto}

/* ---- the vertical rail ---- */
.vrail{grid-column:3;grid-row:1 / -1;display:flex;flex-direction:column;gap:8px;
  min-height:0;position:relative}
.vrail-track{flex:1 1 auto;min-height:0;overflow-y:auto;overscroll-behavior:contain;
  scroll-snap-type:y mandatory;scrollbar-width:none;
  display:flex;flex-direction:column;gap:20px;padding:2px}
.vrail-track::-webkit-scrollbar{display:none}
.vrail-track > .voice{scroll-snap-align:start;flex:none;margin:0}
.vrail-track:focus-visible{outline:3px solid var(--teal);outline-offset:3px;border-radius:20px}

.vrail-arrow{align-self:center;flex:none;width:40px;height:40px;border:0;border-radius:50%;
  cursor:pointer;background:var(--white);color:var(--teal-deep);box-shadow:var(--sh-2);
  display:grid;place-items:center;
  transition:background .18s var(--ease),transform .18s var(--ease),opacity .18s var(--ease)}
.vrail-arrow svg{width:18px;height:18px}
.vrail-up svg{transform:rotate(-90deg)}
.vrail-down svg{transform:rotate(90deg)}
.vrail-arrow:hover{background:var(--t-teal);transform:scale(1.07)}
.vrail-arrow:disabled{opacity:.3;cursor:default;transform:none;background:var(--white)}
.vrail-arrow:focus-visible{outline:3px solid var(--teal);outline-offset:2px}

/* ---- narrow screens: rail becomes a normal stack ---- */
@media (max-width:900px){
  .vrail{grid-column:1;grid-row:auto}
  .vrail-track{overflow:visible;scroll-snap-type:none}
  .vrail-arrow{display:none}
}

@media (prefers-reduced-motion:reduce){
  .vrail-arrow{transition:none}
  .vrail-track{scroll-behavior:auto}
}

/* Nothing to scroll — hide the arrows rather than show two dead controls. */
.vrail.is-idle .vrail-arrow{display:none}

/* --- voices rail: bound the viewport ---------------------------------------
   A carousel needs a window smaller than its contents. Laid out in normal
   flow the track grew to fit all three cards, so there was nothing to scroll
   AND its height drove the grid row, stretching the featured card to match.
   Taking the track out of flow fixes both: the row is now sized by the
   featured card alone, and the track scrolls inside whatever height that is. */
.vrail{position:relative;align-self:stretch;min-height:420px;display:block}
.vrail-track{position:absolute;left:0;right:0;top:48px;bottom:48px;
  display:flex;flex-direction:column;gap:20px;padding:2px}
.vrail-arrow{position:absolute;left:50%;transform:translateX(-50%);margin:0}
.vrail-up{top:0}
.vrail-down{bottom:0}
.vrail-arrow:hover{transform:translateX(-50%) scale(1.07)}

@media (max-width:900px){
  /* Back to a plain stack; no viewport, no arrows. */
  .vrail{min-height:0}
  .vrail-track{position:static;inset:auto}
}

/* `grid-row: 1 / -1` resolves -1 against the EXPLICIT grid, and .vwall's rows
   are implicit — so it collapsed to a single row and the rail came out half
   the height of the featured card. The featured card spans two rows; match it. */
.vrail{grid-row:span 2}

/* The template sets `.voice.lead > * { position: relative }` (0-2-0) to lift
   its children above the card background. That out-specifies a bare
   `.voice-bg` (0-1-0), so the photo and scrim were dropped back into flow and
   rendered as inset blocks instead of filling the card. Match the specificity. */
.voice > .voice-bg,
.voice.lead > .voice-bg{position:absolute;inset:0;width:100%;height:100%;
  object-fit:cover;z-index:0;border-radius:inherit}
.voice > .voice-scrim,
.voice.lead > .voice-scrim{position:absolute;inset:0;z-index:1;border-radius:inherit}
.voice > .voice-in,
.voice.lead > .voice-in{position:relative;z-index:2}

/* ==========================================================================
   Voices rail — two visible tiles, aligned with the featured card
   Supersedes the single-tile treatment above.

   The viewport is sized to hold two typical cards plus the gap, and the
   featured card is given the same height so the two columns start and end on
   the same line. Cards use min-height rather than a fixed height: a longer
   quote grows the card instead of being clipped, and the arrow step is
   measured from the DOM (see voices.js), so the two stay in agreement.

   Arrows now overlay the cards instead of sitting above and below them —
   otherwise the rail's outer box would be taller than the featured card by
   two arrow heights and the columns could not align.
   ========================================================================== */

.vwall{--v-card:310px;--v-gap:20px}

.vrail{
  height:calc(var(--v-card) * 2 + var(--v-gap));
  min-height:0;
  align-self:start;
}
.vrail-track{
  top:0;bottom:0;left:0;right:0;
  gap:var(--v-gap);
  padding:0;
  scroll-padding-top:0;
}
.vrail-track > .voice{min-height:var(--v-card);height:auto}

/* Featured card matches the rail exactly, top to bottom. */
.voice.lead{height:calc(var(--v-card) * 2 + var(--v-gap));align-self:start}

/* Arrows float over the top and bottom edges of the rail. */
.vrail-arrow{
  position:absolute;left:50%;transform:translateX(-50%);
  z-index:4;width:38px;height:38px;
  background:rgba(255,255,255,.94);
  box-shadow:0 3px 12px rgba(38,50,56,.28);
}
.vrail-up{top:10px}
.vrail-down{bottom:10px}
.vrail-arrow:hover{background:#fff;transform:translateX(-50%) scale(1.08)}
.vrail-arrow:disabled{opacity:0;pointer-events:none}

@media (max-width:900px){
  /* Plain stack on narrow screens — a fixed viewport would fight page scroll. */
  .vrail,.voice.lead{height:auto}
  .vrail-track{position:static}
  .vrail-track > .voice{min-height:0}
}

/* .voice carries the UA figure margin (16px 40px) that the template relies on,
   so the featured card sits 16px lower than the rail, which is a plain div.
   Give the rail the same vertical margin so the two columns line up. */
.vrail{margin:16px 0}

/* Centred inside the rail the arrows sat on top of the caption text. Straddle
   them on the top and bottom edges instead — they read as controls for the
   column and clear the card content. */
.vrail-up{top:0;bottom:auto;transform:translate(-50%,-50%)}
.vrail-down{bottom:0;top:auto;transform:translate(-50%,50%)}
.vrail-up:hover{transform:translate(-50%,-50%) scale(1.08)}
.vrail-down:hover{transform:translate(-50%,50%) scale(1.08)}

/* --- voices: quote typography + no stray rules ----------------------------
   Featured quote reads larger; the divider above the caption is dropped so
   the card is clean now the placeholder outline is gone. */
.voice.lead blockquote{font-size:30px;line-height:1.38}
.voice:not(.lead) blockquote{font-size:16.5px;line-height:1.55}
.voice .who{border-top:0;padding-top:6px}

/* Rail cards are promotable — give them an affordance, but only once the
   script has marked them (is-pickable), never on a no-JS page. */
.voice.is-pickable{cursor:pointer;transition:transform .2s var(--ease),box-shadow .2s var(--ease)}
.voice.is-pickable:hover{transform:translateY(-3px);box-shadow:var(--sh-3)}
.voice.is-pickable:focus-visible{outline:3px solid var(--teal);outline-offset:3px}
/* The featured card is the destination, not a control. */
.voice.lead{cursor:default}

/* The featured card takes programmatic focus after a promote; it is not a
   control, so do not paint a focus ring on it. */
.voice.lead:focus,
.voice.lead:focus-visible{outline:none}

/* The template's footer logo carried no width/height attributes, so it scaled
   on its own. Adding them (worth keeping — they reserve space and prevent
   layout shift) means the height attribute is used literally unless CSS says
   otherwise, squashing a 420x182 logo into 126x182. `.brand img` already pairs
   its width with height:auto; the footer rule was missing it. */
.foot img{height:auto}

/* ==========================================================================
   Brand marks
   .wa was built for a monochrome glyph sitting in a tinted disc
   (background + fill:#3BA55C). The WhatsApp brand mark is already a green
   circle, so the disc and the fill override both have to come off or the
   logo sits inside a second, paler circle.
   ========================================================================== */
.nav-wa .wa,
.plan-alt .wa{background:none}
.nav-wa .wa svg,
.plan-alt .wa svg{width:100%;height:100%;fill:none}
.vrow[data-k="wa"] .vi svg{width:20px;height:20px}

/* Footer social links. */
.foot-social{display:flex;gap:12px;list-style:none;margin:16px 0 0;padding:0}
.foot-social a{display:grid;place-items:center;width:34px;height:34px;border-radius:50%;
  overflow:hidden;transition:transform .18s var(--ease),opacity .18s var(--ease);opacity:.92}
.foot-social svg{width:100%;height:100%;display:block}
.foot-social a:hover{transform:translateY(-2px);opacity:1}
.foot-social a:focus-visible{outline:3px solid var(--teal);outline-offset:3px}

/* `.foot ul { display: grid }` (0-1-1) out-specifies a bare `.foot-social`
   (0-1-0) and stacked the icons in one column. Match the specificity.
   Third clash of this kind with the template sheet — when adding a component
   inside an existing region, check what that region already styles. */
.foot ul.foot-social{display:flex;flex-wrap:wrap;gap:12px;margin:16px 0 0}

/* ==========================================================================
   Top white strip
   WordPress gives siblings in a flow layout a 24px block gap, so <main> got
   margin-block-start:24px. The header template-part before it is an empty box
   — its <header class="nav"> is absolutely positioned — so that margin had
   nothing to sit against and collapsed straight out through .site,
   .wp-site-blocks and <body>, shifting the whole page down 24px. The hero's
   background then started at 24 while the absolute nav stayed at 0, showing
   the page's white background behind the header.

   The design spaces its own sections, so the block gap inside .site is not
   wanted at all.
   ========================================================================== */
.site > * + *{margin-block-start:0}
.wp-site-blocks > * + *{margin-block-start:0}

/* The brand mark is a filled circle, so it needs no wrapper disc and should
   fill the space it is given rather than sit small inside it. */
.nav-wa .wa{width:30px;height:30px}
.plan-alt .wa{width:32px;height:32px}

/* ---------- Activity space tiles: photographic cover ---------------------
   A space with photos shows its first one as the tile cover; the rest keep
   the illustrated scene. These rules mirror .bcard .art svg exactly so the
   two kinds of tile behave identically — same slow zoom on hover, same
   cover-crop fill — and the grid does not read as two different components.
   ----------------------------------------------------------------------- */
.bcard .art img{
  width:100%;height:100%;display:block;
  object-fit:cover;
  /*
   * Biased upward, not centred. The two wide banner tiles are about 2.9:1
   * against a 16:9 source, so a cover-crop discards ~19% off the top and the
   * same off the bottom. In a photograph of people the heads are almost always
   * in the upper third, and a dead-centre crop decapitated the child at the
   * microphone in Speak & Shine. 30% keeps the faces and sacrifices floor,
   * which is the right trade in every photo of this kind.
   *
   * It costs nothing on the hero tile: that one is 1.38:1 against a 1.78:1
   * source, so it crops horizontally and the vertical position is moot.
   */
  object-position:center 30%;
  transform:scale(1.02);transition:transform .7s var(--ease);
}
.bcard:hover .art img{transform:scale(1.09)}
@media (prefers-reduced-motion:reduce){
  .bcard .art img{transition:none}
  .bcard:hover .art img{transform:scale(1.02)}
}

/* ---------- Activity spaces: the text version ---------------------------
   A plain, readable summary of the seven spaces beneath the tile grid. It
   exists because the descriptions previously lived only in the JavaScript the
   tour reads, which put them out of reach of anything that does not run JS —
   including most AI answer engines. Styled as real content, not hidden text:
   concealed keywords are a penalty risk and this is genuinely worth reading.
   ----------------------------------------------------------------------- */
.spaces-detail{
  margin-top:clamp(34px,4vw,54px);
  display:grid;gap:clamp(18px,2.4vw,30px);
  grid-template-columns:repeat(auto-fit,minmax(270px,1fr));
}
.spaces-detail-item h3{font-size:17px;margin-bottom:7px;color:var(--ink)}
.spaces-detail-item p{color:var(--ink-60);font-size:14.5px;line-height:1.6}
.spaces-detail-bens{
  margin-top:7px;font-size:12.5px;color:var(--ink-40);
  font-family:var(--head);font-weight:600;letter-spacing:.01em;
}
