/*
 * The /anneloes/ profile page's stylesheet -- the only page that loads it, and
 * the only page that does NOT load site.css: functions.php dequeues site.css
 * there and enqueues this file in its place, so the two never paint together.
 * That is why rules here repeat names site.css also defines (.sec, .card,
 * .btn, .wrap, .head, .lede, .eb, .rise, .aurwrap, .key, .vid, .g2, .g3):
 * they are the same design system, expressed for a cream ground instead of
 * the abyss one, and the spec rules out refactoring site.css to share them.
 *
 * EVERY rule below is scoped under .ans, the page wrapper, except four
 * unscoped rules that have nowhere else to live: three `html` rules --
 * scroll-padding for the fixed nav, its narrow-screen value, and
 * scroll-behavior under reduced motion -- and `.grecaptcha-badge` (see its
 * own comment, in the embedded-forms section below). The three `html` rules
 * carry the same values site.css:25-26 and :423 already set site-wide, so
 * loading this file alongside another page would change nothing there.
 * `.grecaptcha-badge` has no such site-wide twin, but is safe unscoped for a
 * different reason: this file loads only on this one page (see above), so an
 * unscoped selector here still cannot reach any other page. Everything else,
 * being scoped, could not reach anywhere else at all. That combination is
 * also what makes the file safe to hand to add_editor_style(): see
 * editor.css's own comment on why that does not mean it applies in this
 * page's own canvas.
 *
 * --gold-dk (#B8932F) MUST NEVER be used for text. It measures 2.54:1 on
 * --cream2, the bottom stop of every .lite gradient: below AA's 4.5:1 for
 * body text and below 3:1 for large text alike, and the gold gradients built
 * from it run lighter still, to 1.38:1 at their midpoints. Four elements keep
 * gold text as a knowing, countable exception (see the block above
 * .lite h2 .ac); everything else that wants emphasis uses var(--navy) at
 * 11.95:1. Borders, underlines and shadows are not text and are unrestricted
 * -- but a gradient stop that lands under text is, which is how .gt joined
 * the list.
 * Run `python3 scripts/check-contrast.py` after touching any colour here.
 */

/* ══════════════════════════════════════════════════════════════════════════
   TOKENS — lifted from the approved mockup (docs/mockups/anneloes-v13.html),
   which took them from earthwise-arena-v6.html so the two sites are one
   system. Balance is inverted: cream is the ground, cyan appears only in the
   Arena section. They live on .ans rather than :root so the page cannot
   redefine a token another page's stylesheet is reading, and the mockup's
   body rule is merged in below them for the same reason: the wrapper is what
   this page has instead of a body of its own.
   ══════════════════════════════════════════════════════════════════════════ */
.ans{
  --abyss:#040814;--deep:#070E1E;--deep2:#0A1526;
  --cy:#5BC8E8;--cy-lo:#A8ECFA;--cy-dk:#2E9EC4;--cyg:rgba(91,200,232,.5);
  --gold:#F5C86A;--gold-lo:#FCE7BC;--gold-dk:#B8932F;--goldg:rgba(245,200,106,.5);
  --cream:#FBF9F1;--cream2:#F3F0E4;--navy:#1B2A5E;--ink:#16224A;
  /* Slate for the one line that must sit visibly below a navy heading without
     repeating navy. 4.54:1 on --cream2; the mockup's #8894B0 was 2.88:1. */
  --slate-tx:#646D82;
  --tx:#C3D0E0;--mute:#7A8CA3;--mute-l:#6B6754;
  --line:rgba(255,255,255,.1);--line-l:#E2DDCB;
  /* The mockup pulled Poppins from fonts.googleapis.com. The theme already
     self-hosts Poppins 400/500/600/700 through theme.json, and this page uses
     no other weight, so it takes the preset the rest of the site takes --
     no @import, no third-party request, one font file set for the whole theme. */
  --f:var(--wp--preset--font-family--display);
  --fb:-apple-system,BlinkMacSystemFont,'Segoe UI',Arial,sans-serif;
  --wrap:1220px;--gut:clamp(18px,4.5vw,60px);--ease:cubic-bezier(.16,1,.3,1);
  /* No overflow-x:hidden (unlike the mockup's body rule): the mask hid a
     horizontal overflow instead of fixing it, and would hide the next one
     just as quietly. */
  margin:0;font-family:var(--fb);font-size:16.5px;line-height:1.62;
  color:#4A4636;background:var(--cream);-webkit-font-smoothing:antialiased;
}
.ans *{box-sizing:border-box}
/* Every nav link on this page is an in-page anchor and the nav is fixed, so
   without this each target lands with its heading hidden behind the bar. The
   values match site.css:25-26 -- same nav height, same problem. */
html{scroll-behavior:smooth;-webkit-text-size-adjust:100%;scroll-padding-top:80px}
@media(max-width:1040px){html{scroll-padding-top:70px}}
.ans::after{content:"";position:fixed;inset:0;z-index:200;pointer-events:none;opacity:.045;mix-blend-mode:overlay;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='220' height='220'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='220' height='220' filter='url(%23n)'/%3E%3C/svg%3E")}
.ans img{max-width:100%;height:auto;display:block}
.ans a{text-decoration:none;color:inherit}
.ans .wrap{max-width:var(--wrap);margin:0 auto;padding:0 var(--gut);position:relative;z-index:3}

/* Gutenberg's block gap, switched off inside the page exactly as site.css:44-48
   switches it off for the rest of the site. site.css is dequeued here, so
   without this repetition core's `:where(.is-layout-flow|constrained) > *
   {margin-block-start:24px}` opens a 24px strip of body background between
   every full-bleed band -- a visible seam wherever two bands share an edge --
   and its `:last-child{margin-block-end:0}` companion drops the closing
   paragraph's margin at the foot of a card. Lowest specificity that outranks
   core, so every component rule below still wins.

   `.ans` is itself the page's `.is-layout-flow` element (the block editor
   puts that class on the group itself, not just its descendants), so a
   descendant-only selector never matches `.ans`'s own children -- an element
   cannot be its own descendant. That left `main` (the first in-flow child;
   `.skip` and `nav` are position:fixed) with core's 24px margin-block-start
   uncancelled, collapsing up through `.ans` to `body`. site.css:44-45 covers
   the equivalent case with a second, direct-child selector; mirrored here. */
.ans > *,
.ans :where(.is-layout-flow,.is-layout-constrained) > *{margin-block-start:0}
.ans :where(.is-layout-flow,.is-layout-constrained) > :where(p){margin-block-end:1em}
.ans :where(.is-layout-flow,.is-layout-constrained) > :where(h1,h2,h3){margin-block-end:.5em}
.ans :where(.is-layout-flow,.is-layout-constrained) > :where(h4){margin-block-end:.4em}

.ans h1,.ans h2,.ans h3,.ans h4{font-family:var(--f);margin:0 0 .5em;line-height:1.13;font-weight:600;letter-spacing:-.024em;color:#fff}
.ans .lite h1,.ans .lite h2,.ans .lite h3,.ans .lite h4{color:var(--navy)}
.ans h1{font-size:clamp(32px,5vw,62px);font-weight:700;letter-spacing:-.038em}
.ans h2{font-size:clamp(24px,3.05vw,40px)}
.ans h2 .ac{color:var(--cy);display:block}
.ans h2 .ac.inl{display:inline}

/* Accepted contrast violation -- see the design spec, section 3.3.
   EXACTLY these four elements keep gold text, all of them large and
   gradient-clipped, each measured at its gradient's LIGHTEST stop -- the worst
   point on the run, not a representative one:

     .lite h2 .ac      #E9CB84 at 58%   1.38:1 on --cream2
     .lite h2.allgold  #E9CB84 at 58%   1.38:1
     .brand            #F5C86A at 50%   1.38:1
     .gt               #B8932F at 100%  2.54:1

   All four are under the 3:1 large-text threshold, and .brand at its 19px
   clamp() floor is semibold rather than bold, so it is properly measured
   against 4.5:1 and misses both. Kept as the page's gold identity, knowingly.
   A fifth element wanting gold text is a new decision, not this one extended
   -- scripts/check-contrast.py enumerates all four so the exemption stays
   countable, and quotes the same worst stops. Everything smaller is navy, per
   the rule in this file's header. */
.ans .lite h2 .ac{color:var(--gold-dk)}
.ans .lite h2.allgold{color:var(--gold-dk)}

.ans h3{font-size:clamp(17px,1.85vw,22px)}
.ans h4{font-size:16px;margin-bottom:.4em}
.ans p{margin:0 0 1em;max-width:64ch}
.ans strong{color:#fff;font-weight:600}.ans .lite strong{color:var(--navy)}
.ans .lede{font-size:clamp(16px,1.8vw,19px);line-height:1.5;color:#DEE8F4}
.ans .lite .lede{color:var(--ink)}
.ans .sm{font-size:13px;line-height:1.55;color:var(--mute)}
.ans .lite .sm{color:var(--mute-l)}
.ans .eb{font-family:var(--f);font-size:11.5px;font-weight:600;letter-spacing:.19em;text-transform:uppercase;color:var(--cy);margin:0 0 16px}
/* Navy, not the mockup's gold: 11.5px is small text at 2.54:1 in gold. This is
   the rule site.css:70 already applies to the same class on the cream bands. */
.ans .lite .eb{color:var(--navy)}
/* No overflow:hidden here (unlike the mockup) -- see site.css:71-79. .aur
   extends 28% past this box on every side, and overflow past a box's own
   bottom edge makes it a scroll container; the browser then resolves an
   in-page anchor's scroll-into-view against that container instead of the
   page. Every link in this page's nav is an in-page anchor, so that bug would
   eat all of them. The clipping .aur and .key need is on .aurwrap instead. */
.ans .sec{padding:clamp(54px,7.5vw,112px) 0;position:relative;isolation:isolate;perspective:1500px}
.ans .lite{background:linear-gradient(172deg,var(--cream),var(--cream2));color:#4A4636}
.ans .lite.flat{background:var(--cream)}
.ans .dark{background:var(--abyss);color:var(--tx)}

.ans .head{max-width:min(1120px,96%);margin-bottom:clamp(28px,3.4vw,48px)}
.ans .head h2{max-width:none}
.ans .head p{max-width:74ch}

/* ── ambience ───────────────────────────────────────────────────────────── */
/* .aurwrap is exactly .sec's padding box, so it is the same containing block
   .aur resolved its inset:-28% against when .sec did the clipping: identical
   on screen, but the clip stays local. Matches site.css:97, and it is the
   class editor.css hides -- .aur alone would still paint in the canvas. */
.ans .aurwrap{position:absolute;inset:0;overflow:hidden;pointer-events:none}
.ans .aur{position:absolute;inset:-28%;z-index:0;pointer-events:none;filter:blur(80px);opacity:.8}
.ans .aur i{position:absolute;display:block;border-radius:50%;mix-blend-mode:screen}
.ans .aur i:nth-child(1){width:50%;padding-bottom:42%;left:2%;top:4%;background:radial-gradient(circle,rgba(91,200,232,.28),transparent 68%);animation:ans-fl1 28s ease-in-out infinite}
.ans .aur i:nth-child(2){width:44%;padding-bottom:38%;right:2%;bottom:4%;background:radial-gradient(circle,rgba(122,107,255,.2),transparent 68%);animation:ans-fl2 34s ease-in-out infinite}
.ans .aur i:nth-child(3){width:40%;padding-bottom:34%;left:40%;top:32%;background:radial-gradient(circle,rgba(245,200,106,.14),transparent 68%);animation:ans-fl3 40s ease-in-out infinite}
/* Every @keyframes in this file is prefixed ans-. Keyframe names are global,
   not scoped by a selector, and site.css:103-105 already defines fl1/fl2/fl3
   with different values -- inert on the front end, where site.css is dequeued,
   but both sheets load together in the editor canvas and the later definition
   would silently win for both pages. */
@keyframes ans-fl1{0%,100%{transform:translate3d(0,0,0) scale(1)}50%{transform:translate3d(5%,-4%,0) scale(1.12)}}
@keyframes ans-fl2{0%,100%{transform:translate3d(0,0,0) scale(1.04)}50%{transform:translate3d(-6%,5%,0) scale(.94)}}
@keyframes ans-fl3{0%,100%{transform:translate3d(0,0,0) scale(.96)}50%{transform:translate3d(4%,-6%,0) scale(1.1)}}
.ans .lite .aur{opacity:.34;filter:blur(100px)}
.ans .lite .aur i:nth-child(1){background:radial-gradient(circle,rgba(27,42,94,.15),transparent 68%);mix-blend-mode:multiply}
.ans .lite .aur i:nth-child(2){background:radial-gradient(circle,rgba(184,147,47,.17),transparent 68%);mix-blend-mode:multiply}
.ans .lite .aur i:nth-child(3){background:radial-gradient(circle,rgba(46,158,196,.1),transparent 68%);mix-blend-mode:multiply}
.ans .key{position:absolute;inset:0;z-index:1;pointer-events:none;background:radial-gradient(600px circle at var(--lx,50%) var(--ly,32%),rgba(91,200,232,.1),transparent 62%)}
.ans .lite .key{background:radial-gradient(640px circle at var(--lx,50%) var(--ly,32%),rgba(255,255,255,.5),transparent 62%)}


/* ── cards ──────────────────────────────────────────────────────────────── */
/* No transform-style:preserve-3d here, unlike the mockup -- see site.css:114-123.
   It puts the card's subtree in a 3D rendering context whose hit test Chromium
   resolves against document-space coordinates while painting it in
   section-space, which on this site already moved the clickable area of three
   call-to-action buttons 164px away from the pixels they were drawn on. The
   .door cards below each carry a button, so the same trap is set here. What
   the flag buys is depth on .bk .cov's translateZ, which is worth less than a
   button that cannot be clicked; the cover keeps its translateY hover lift. */
.ans .card{position:relative;border-radius:16px;padding:26px clamp(18px,2vw,28px);
  border:1px solid rgba(91,200,232,.2);will-change:transform;
  background:linear-gradient(162deg,rgba(91,200,232,.085),rgba(10,21,38,.85));
  box-shadow:inset 0 1px 0 rgba(255,255,255,.13),0 24px 56px -32px rgba(0,0,0,.9);
  transition:transform .6s var(--ease),box-shadow .6s var(--ease),border-color .45s;
  display:flex}
/* The cursor-following sheen. The mockup made it an empty <div class="spec">;
   site.css:130-137 moved it to a pseudo-element because the block editor
   renders an empty group as a full-width "Group blocks together" placeholder,
   one per card. Identical on screen, absent from the block tree -- so this
   page's patterns must NOT emit a .spec element. --mx/--my are set by the
   page's JavaScript on the card itself, and the pseudo-element inherits them. */
.ans .card::after{content:"";position:absolute;inset:0;border-radius:inherit;pointer-events:none;opacity:0;transition:opacity .45s;
  background:radial-gradient(300px circle at var(--mx,50%) var(--my,50%),rgba(255,255,255,.12),transparent 58%)}
.ans .card:hover::after{opacity:1}
.ans .card:hover{border-color:rgba(91,200,232,.55);box-shadow:inset 0 1px 0 rgba(255,255,255,.22),0 44px 82px -34px rgba(0,0,0,.95),0 0 52px -24px var(--cyg)}
.ans .card p{max-width:none}
/* margin:0 matters now that .lift is sometimes a <figure> (the endorsements
   cards) rather than only a <div> -- a bare figure carries a UA default
   margin (1em 40px) that a div does not; zeroing it here is a no-op for the
   existing div usages. */
.ans .card .lift{transition:transform .6s var(--ease);flex:1;display:flex;flex-direction:column;width:100%;margin:0}
.ans .card:hover .lift{transform:translateY(-3px)}
.ans .card a,.ans .card .btn,.ans .card .tlink{position:relative;z-index:6}
.ans .lite .card{background:linear-gradient(162deg,#fff,#FCFAF3);border-color:var(--line-l);
  box-shadow:inset 0 1px 0 #fff,0 1px 2px rgba(27,42,94,.04),0 20px 44px -30px rgba(27,42,94,.28)}
.ans .lite .card:hover{border-color:rgba(184,147,47,.55);box-shadow:0 40px 72px -34px rgba(27,42,94,.3)}
.ans .lite .card::after{background:radial-gradient(280px circle at var(--mx,50%) var(--my,50%),rgba(184,147,47,.1),transparent 58%)}

.ans .g2{display:grid;grid-template-columns:repeat(2,1fr);gap:clamp(16px,2.2vw,28px)}
.ans .g3{display:grid;grid-template-columns:repeat(3,1fr);gap:clamp(15px,1.9vw,24px)}
.ans .g4{display:grid;grid-template-columns:repeat(4,1fr);gap:clamp(14px,1.7vw,20px)}
/* One collapse scale for all three grids: four-up (.g4) thins to two-up at
   1050px, then every grid -- .g2, .g3 and .g4 alike -- goes one-up together
   at 880px. Below 880 nothing is left wide: .g4 used to keep its two-up
   stage until 620px, leaving it two-up directly above .g2/.g3's one-up
   between 620 and 880 -- a wide grid sitting over narrow ones. Collapsing
   all three at the same 880px breakpoint removes that band entirely, so no
   width leaves a wide grid below a narrow one. */
@media(max-width:1050px){.ans .g4{grid-template-columns:repeat(2,1fr)}}
@media(max-width:880px){.ans .g2,.ans .g3,.ans .g4{grid-template-columns:1fr}}

/* gradient headline text — background-image, never the shorthand */
/* .gt is the fourth accepted gold-text exception -- see the block above
   .lite h2 .ac. Its run ends on --gold-dk, so the tail of the hero headline
   paints at 2.54:1 against a 3:1 threshold; the rest of the run is navy and
   passes. The gradient is the approved design and is not to be darkened to
   improve the figure. */
.ans .gt{background-image:linear-gradient(134deg,var(--navy) 2%,#2F4585 36%,#7A6A2E 72%,var(--gold-dk) 100%);
  -webkit-background-clip:text;background-clip:text;color:transparent;-webkit-text-fill-color:transparent}
.ans .gt-cy{background-image:linear-gradient(134deg,#EAF9FE 0%,var(--cy) 50%,var(--cy-dk) 100%);
  -webkit-background-clip:text;background-clip:text;color:transparent;-webkit-text-fill-color:transparent}

/* ── buttons ────────────────────────────────────────────────────────────── */
.ans .btn{position:relative;display:inline-flex;align-items:center;justify-content:center;font-family:var(--f);font-weight:600;font-size:15.5px;
  padding:15px 30px;border-radius:100px;text-decoration:none;overflow:hidden;will-change:transform;
  transition:transform .5s var(--ease),box-shadow .5s var(--ease),border-color .45s}
/* Direct child only (site.css:167-171): the label span carries the sheen's
   stacking context, but a visually-hidden "(opens in a new tab)" note nested
   inside it -- and half this page's buttons leave the site -- must keep its
   position:absolute, or width/height/overflow stop applying and the note
   renders at full size in the middle of the button. */
.ans .btn > span{position:relative;z-index:2}
.ans .btn::after{content:"";position:absolute;inset:0;z-index:1;opacity:0;transition:opacity .4s;
  background:radial-gradient(130px circle at var(--mx,50%) var(--my,50%),rgba(255,255,255,.4),transparent 60%)}
.ans .btn:hover::after{opacity:1}
.ans .btn-p{background:linear-gradient(150deg,var(--cy-lo),var(--cy) 48%,var(--cy-dk));color:#03212B;
  box-shadow:inset 0 1px 0 rgba(255,255,255,.6),0 12px 30px -12px var(--cyg)}
.ans .btn-p:hover{box-shadow:inset 0 1px 0 rgba(255,255,255,.7),0 24px 52px -14px var(--cyg),0 0 72px -22px var(--cyg)}
.ans .btn-a{background:linear-gradient(150deg,var(--gold-lo),var(--gold) 48%,var(--gold-dk));color:#2C1F03;
  box-shadow:inset 0 1px 0 rgba(255,255,255,.72),0 12px 30px -12px var(--goldg)}
.ans .btn-a:hover{box-shadow:inset 0 1px 0 rgba(255,255,255,.82),0 24px 52px -14px var(--goldg),0 0 72px -22px var(--goldg)}
.ans .btn-n{background:linear-gradient(150deg,#2B3F7E,var(--navy) 60%,#121E45);color:#F7F5EC;
  box-shadow:inset 0 1px 0 rgba(255,255,255,.2),0 12px 30px -12px rgba(27,42,94,.6)}
.ans .btn-n:hover{box-shadow:inset 0 1px 0 rgba(255,255,255,.28),0 24px 52px -14px rgba(27,42,94,.6)}
.ans .btn-g{border:1px solid rgba(91,200,232,.45);color:#fff;background:rgba(91,200,232,.06);backdrop-filter:blur(8px)}
.ans .btn-g:hover{border-color:var(--cy);box-shadow:0 18px 42px -18px var(--cyg)}
.ans .lite .btn-g{border-color:rgba(27,42,94,.28);color:var(--navy);background:#fff}
.ans .lite .btn-g:hover{border-color:var(--gold-dk);box-shadow:0 18px 42px -18px rgba(184,147,47,.5)}
.ans .btns{display:flex;gap:13px;flex-wrap:wrap;margin-top:30px}

/* Navy, not gold: 13.5px is small text, and the underline takes the same
   colour so the link and its rule read as one thing rather than two. */
.ans .tlink{font-family:var(--f);font-size:13.5px;font-weight:600;letter-spacing:.03em;color:var(--navy);
  position:relative;display:inline-block;padding-bottom:3px}
.ans .tlink::after{content:"";position:absolute;left:0;right:0;bottom:0;height:1px;background:var(--navy);
  transform:scaleX(0);transform-origin:left;transition:transform .5s var(--ease)}
.ans .tlink:hover::after{transform:scaleX(1)}
.ans .dark .tlink{color:var(--cy)}.ans .dark .tlink::after{background:var(--cy)}

/* ── nav ────────────────────────────────────────────────────────────────── */
.ans nav{position:fixed;top:0;left:0;right:0;z-index:120;transition:background .5s var(--ease),backdrop-filter .5s,box-shadow .5s}
.ans nav.stuck{background:rgba(251,249,241,.84);backdrop-filter:blur(18px) saturate(160%);box-shadow:0 1px 0 rgba(27,42,94,.09),0 10px 30px -24px rgba(27,42,94,.6)}
/* nowrap, not wrap: this row's three items (brand, menu, standalone button)
   are sized to fit above the 980px breakpoint -- measured with a genuine
   narrow viewport (not a width-constrained .wrap, which pins .brand at its
   25px clamp() ceiling and understates how much it has already shrunk by
   then): at 981px the row is 875px of content in a 966px wrap, 91px to
   spare, rising to 139px by 1040px. `wrap` on a `flex` row inside a
   `position:fixed` nav is a silent failure mode, not a safety net: if a
   future edit (a longer brand, a sixth item, a translated label) ever does
   overflow the row, wrapping grows the fixed bar's height and pushes it,
   out of flow, over the hero below -- which reserves only enough
   padding-top for one line. `nowrap` turns that into a visible squeeze in
   the bar itself, which is easy to notice and fix at the time, instead of
   an invisible overlap discovered later. site.css's own nav .wrap carries
   no flex-wrap at all (site.css:186), i.e. the same nowrap default --
   this brings .ans back in line with that rather than diverging from it. */
.ans nav .wrap{display:flex;align-items:center;justify-content:space-between;gap:22px;padding-top:15px;padding-bottom:15px;flex-wrap:nowrap}
/* .brand is one of the four accepted gold-text exceptions -- see the block
   above .lite h2 .ac. Its weight and clamp are the approved design and are not
   to be nudged to flatter the measurement. */
.ans .brand{
  display:inline-block;font-family:var(--f);font-weight:600;
  font-size:clamp(19px,2vw,25px);letter-spacing:-.028em;line-height:1.3;
  padding:2px 0 3px;white-space:nowrap;position:relative;
  background-image:linear-gradient(96deg,
    #16224A 0%, #1B2A5E 22%, #2F4585 34%,
    #7A6A3A 43%, #B8932F 47%, #F5C86A 50%, #B8932F 53%, #7A6A3A 57%,
    #2F4585 66%, #1B2A5E 78%, #16224A 100%);
  background-size:300% 100%;background-position:100% 50%;
  -webkit-background-clip:text;background-clip:text;
  color:transparent;-webkit-text-fill-color:transparent;
  animation:ans-sheen 10s cubic-bezier(.45,0,.55,1) infinite;
}
.ans .brand::after{
  content:"";position:absolute;left:0;right:0;bottom:-3px;height:1px;
  background:linear-gradient(90deg,transparent,var(--gold-dk),transparent);
  transform:scaleX(0);transition:transform .6s var(--ease);
}
.ans .brand:hover::after{transform:scaleX(1)}
@keyframes ans-sheen{0%{background-position:100% 50%}60%,100%{background-position:0% 50%}}
@media (prefers-reduced-motion:reduce){.ans .brand{animation:none;background-position:46% 50%}}
.ans nav ul{display:flex;gap:26px;list-style:none;margin:0;padding:0;font-family:var(--f);font-size:14px;font-weight:500}
.ans nav ul a{color:#5A688F;position:relative;transition:color .4s}
.ans nav ul a::after{content:"";position:absolute;left:0;right:0;bottom:-7px;height:1px;background:var(--gold-dk);
  transform:scaleX(0);transform-origin:left;transition:transform .5s var(--ease)}
.ans nav ul a:hover{color:var(--navy)}
.ans nav ul a:hover::after{transform:scaleX(1)}
.ans nav .btn{padding:11px 24px;font-size:13.5px}
/* the standalone .mag "Enquire" button already covers desktop; the in-menu
   li.cta duplicate is mobile-menu-only (shown again below), same reasoning
   and same house pattern as site.css:193-197 (its comment explains why: not
   announced twice by a screen reader on desktop) */
.ans nav ul li.cta{display:none}

/* The mockup simply hid the menu below 980px, which left every phone with no
   navigation at all on a page whose sections are reachable only through it.
   Collapse to a burger instead, the way the rest of the site does
   (site.css:198-221), scoped and sized for this page's cream bar. */
.ans .burger{
  display:none;
  width:42px;height:42px;padding:0;
  background:none;border:1px solid var(--line-l);border-radius:10px;
  cursor:pointer;flex-direction:column;gap:4px;
  align-items:center;justify-content:center;
}
.ans .burger i{
  display:block;width:18px;height:1.5px;
  background:var(--navy);border-radius:2px;
  transition:transform .4s var(--ease),opacity .3s;
}
.ans nav.open .burger i:nth-child(1){transform:translateY(5.5px) rotate(45deg)}
.ans nav.open .burger i:nth-child(2){opacity:0}
.ans nav.open .burger i:nth-child(3){transform:translateY(-5.5px) rotate(-45deg)}

@media(max-width:980px){
  .ans .burger{display:flex}
  /* Below this width the visible row is brand + burger + the standalone
     Enquire button, and the three no longer fit one line (measured: brand up
     to 244px, button 100px, burger 42px, two 22px gaps, two 18px gutters --
     ~407px minimum against a 390px viewport). Wrapping pushed the fixed nav
     taller and, being out of flow, over the hero below it. Move the button
     into the menu instead, the way site.css:206/219-220 does for the rest of
     the site. */
  .ans nav>.wrap>.btn{display:none}
  .ans nav ul{
    display:none;order:3;width:100%;
    flex-direction:column;gap:0;padding:8px 0 4px;
  }
  .ans nav.open ul{display:flex}
  .ans nav ul li{border-top:1px solid var(--line-l)}
  .ans nav ul a{display:block;padding:13px 2px}
  .ans nav ul a::after{display:none}
  .ans nav ul li.cta{display:block}
  /* site.css:220 tints its in-menu duplicate with --cy, its accent for text
     on a dark bar. This page's nav is light (cream), and --gold-dk -- the
     equivalent accent here -- is barred from text (see the accepted-
     violation comment above .lite h2 .ac: exactly four elements, a fifth is
     a new decision). --navy is already this page's link-hover and heading
     colour on cream, so it reads as emphasis without adding a fifth. */
  .ans nav ul li.cta a{color:var(--navy);font-weight:600}
  /* The open menu needs an opaque ground: it overlays the hero, and a
     translucent bar leaves the focused link unreadable against the portrait
     behind it (WCAG 2.4.11). */
  .ans nav.open{
    background:var(--cream);
    box-shadow:0 1px 0 rgba(27,42,94,.09),0 18px 40px -24px rgba(27,42,94,.6);
  }
}

/* The first thing in the tab order: a fixed nav plus ~40 in-page links is a
   long way to tab past to reach the article. Off-screen until focused --
   position:fixed rather than absolute, so it is still on screen when it is
   focused part-way down the page rather than parked at the document top. */
.ans .skip{
  position:fixed;left:12px;top:-60px;z-index:300;
  padding:12px 20px;border-radius:100px;
  font-family:var(--f);font-weight:600;font-size:14px;
  background:var(--navy);color:#F7F5EC;
  transition:top .3s var(--ease);
}
.ans .skip:focus{top:12px}

/* The skip link's target: tabindex="-1" in the template makes it
   programmatically focusable (a fragment link alone only sets the
   sequential-focus starting point -- it does not move focus unless the
   target itself can take it, and browsers are inconsistent about it, Safari/
   VoiceOver notably). Suppressed here, not left to the browser default:
   main's box is the entire content column, and a focus rectangle around all
   of it reads as a rendering fault, not a focus indicator. Safe to suppress
   because tabindex="-1" removes it from sequential tab order -- the skip
   link is the only way to focus it -- and the skip link itself is already
   visibly focused immediately before activation, which is the cue a sighted
   keyboard user needs that focus moved at all. */
.ans main:focus{outline:none}

/* ── richer gradient headings ───────────────────────────────────────────── */
/* Both selectors here are accepted gold-text exceptions, and this is the rule
   that makes them so: the gradient's #E9CB84 midpoint is lighter than the
   --gold-dk they inherit above, at 1.38:1. See the block above .lite h2 .ac. */
.ans .lite h2 .ac,.ans .lite h2.allgold{
  background-image:linear-gradient(104deg,#8F6A1C 0%,var(--gold-dk) 26%,#E9CB84 58%,var(--gold-dk) 100%);
  -webkit-background-clip:text;background-clip:text;color:transparent;-webkit-text-fill-color:transparent}
.ans .dark h2 .ac{background-image:linear-gradient(104deg,#DCF4FC,var(--cy) 48%,var(--cy-dk));
  -webkit-background-clip:text;background-clip:text;color:transparent;-webkit-text-fill-color:transparent}

/* ── hero ───────────────────────────────────────────────────────────────── */
.ans .hero{padding:clamp(112px,14vw,168px) 0 clamp(48px,7vw,88px)}
.ans .hero-g{display:grid;grid-template-columns:1.08fr .92fr;gap:clamp(34px,5vw,66px);align-items:center}
.ans .hero h1{margin-bottom:24px;max-width:13ch}
/* The prototype gave the lede an inline style="max-width:46ch;margin-bottom:
   24px" -- core/paragraph has no "width" support to back that with a real
   block attribute (only spacing.margin/padding, color, typography, border),
   so an unsupported inline style on the saved block would read as invalid
   the first time the editor touches the page (the same class of mismatch
   company.php's earthwise_company_pattern_logo_block() already fixed for
   images). A scoped selector reproduces the same measure instead, matching
   .hero h1 above and #cta .lede in site.css. */
.ans .hero .lede{max-width:46ch;margin-bottom:24px}
/* Navy, not gold: 11.5px uppercase is the smallest text on the page. */
.ans .rolebar{font-family:var(--f);font-size:11.5px;font-weight:600;letter-spacing:.15em;text-transform:uppercase;
  color:var(--navy);margin:0 0 26px;line-height:1.9;max-width:none;text-wrap:balance}
/* preserve-3d is kept here, unlike on .card: .frame's only descendants are the
   portrait and a decorative rule, so there is no hit target to misplace, and
   the parallax the page's JavaScript drives needs the depth to be real. */
.ans .frame{position:relative;transform-style:preserve-3d;will-change:transform}
.ans .plate{position:relative;border-radius:16px;overflow:hidden;background:#fff;transform:translateZ(56px);
  box-shadow:inset 0 1px 0 #fff,0 3px 8px rgba(27,42,94,.08),0 40px 84px -34px rgba(27,42,94,.46);
  border:1px solid var(--line-l)}
.ans .plate img{width:100%;transition:transform 1.2s var(--ease)}
/* The prototype's .plate wrapped a bare <img>; common rule 7 requires the
   portrait to be a real wp:image block instead, which always wraps its <img>
   in a <figure class="wp-block-image">. Two core rules land on that figure
   uninvited: `.wp-block-image{margin:0 0 1em}`, output because functions.php
   declares add_theme_support('wp-block-styles'), and global styles'
   `:root :where(.is-layout-flow) > *{margin-block-start:24px}` -- confirmed
   live on this install via `curl .../team/ | grep wp-block-image`, not
   assumed away. This rule cancels both, matching .pphoto .wp-block-image in
   site.css, which resets the same two rules on the /team/ page for the same
   reason. */
.ans .plate .wp-block-image{margin:0;display:block}
.ans .frame:hover .plate img{transform:scale(1.035)}
.ans .plate::after{content:"";position:absolute;inset:0;pointer-events:none;
  background:linear-gradient(198deg,rgba(255,255,255,.2) 0%,transparent 32%,rgba(27,42,94,.1) 100%)}
.ans .rule{position:absolute;inset:-18px;border:1px solid rgba(184,147,47,.42);border-radius:18px;
  transform:translateZ(18px);pointer-events:none}
.ans .rule::before,.ans .rule::after{content:"";position:absolute;width:12px;height:12px;border:1px solid var(--gold-dk)}
.ans .rule::before{top:-1px;left:-1px;border-right:0;border-bottom:0}
.ans .rule::after{bottom:-1px;right:-1px;border-left:0;border-top:0}
/* .rule bleeds a fixed 18px outside the portrait on every side, and --gut is
   only 18px below a 400px viewport -- so on the narrowest phones it lands
   flush with the viewport edge, and the parallax's perspective magnifies the
   translateZ another ~2px past it. Below 620px the hero is a single column and
   the frame is full width, so the bleed comes in and the depth goes flat. */
@media(max-width:620px){.ans .rule{inset:-12px;border-radius:14px;transform:none}}

/* ── thesis ─────────────────────────────────────────────────────────────── */
/* One declaration, not the mockup's two: it declared .th twice and used
   !important on the second to beat the first. */
.ans .th{display:grid;grid-template-columns:2px minmax(0,1fr) minmax(0,.82fr);gap:clamp(22px,4vw,52px)}
.ans .th .rail{border-radius:2px;
  background:linear-gradient(180deg,var(--gold),var(--gold-dk) 32%,rgba(27,42,94,.3) 72%,transparent)}
.ans .th .open{font-family:var(--f);font-weight:500;font-size:clamp(18px,2.1vw,24px);line-height:1.42;
  color:var(--navy);letter-spacing:-.02em;max-width:48ch;margin-bottom:1.1em}
.ans .th p{max-width:58ch;font-size:16.5px;line-height:1.68}
/* Same reasoning as .ans .hero .lede above: the prototype's h2 carried an
   inline style="max-width:21ch" that core/heading has no support to back. */
.ans .th h2{max-width:21ch}
.ans .gapdef{margin:32px 0;padding:28px 32px;border-radius:16px;max-width:54ch;
  background:linear-gradient(162deg,#fff,#FCFAF3);border:1px solid var(--line-l);
  border-left:2px solid var(--gold-dk);
  box-shadow:inset 0 1px 0 #fff,0 20px 44px -30px rgba(27,42,94,.3)}
.ans .gapdef .eb{margin-bottom:10px}
.ans .gapdef .def{font-family:var(--f);font-weight:500;font-size:18.5px;line-height:1.5;color:var(--navy);
  letter-spacing:-.018em;margin:0;max-width:none}

/* ── thesis: the Elowyn Tree column ─────────────────────────────────────── */
.ans .treecol{align-self:start;padding-top:26px}
.ans .treewrap{position:relative;will-change:transform}
/* 100%, not the mockup's 118%. At 118% this box hung 9% past each edge of its
   column, which was invisible only because the mockup hid the whole column
   below 1000px and masked the rest with overflow-x:hidden -- both deliberately
   gone. Once the column reflows full-width on a phone, that bleed is roughly
   25px of horizontal scrollbar. The glow keeps its size: it is a radial
   gradient that was already transparent past 68% of a farthest-corner radius,
   so shrinking the box by the same 1.18 and scaling the stops with it
   (44 -> 52, 68 -> 80) puts every colour transition back within a pixel of
   where it was -- 0.368W from centre, against 0.367W before. */
.ans .treewrap::before{content:"";position:absolute;left:50%;top:46%;width:100%;padding-bottom:100%;
  transform:translate(-50%,-50%);border-radius:50%;z-index:0;
  background:radial-gradient(circle,rgba(245,200,106,.34),rgba(91,200,232,.14) 52%,transparent 80%);
  filter:blur(34px);animation:ans-breathe 11s ease-in-out infinite}
@keyframes ans-breathe{0%,100%{transform:translate(-50%,-50%) scale(1);opacity:.85}
  50%{transform:translate(-50%,-50%) scale(1.09);opacity:1}}
/* Common rule 7 requires the tree image to be a real wp:image block, which
   wraps it in a <figure class="wp-block-image"> the prototype's bare <img>
   never had. Core's `.wp-block-image{margin:0 0 1em}` (from
   add_theme_support('wp-block-styles')) and global styles'
   `:where(.is-layout-flow) > *{margin-block-start:24px}` both land on it --
   reset the same way .ans .plate .wp-block-image does above, so
   .ans .treewrap img's own margin:0 auto centring below still has a
   zero-margin box to centre within. */
.ans .treewrap .wp-block-image{margin:0}
.ans .treewrap img{position:relative;z-index:2;width:100%;max-width:400px;margin:0 auto;
  filter:drop-shadow(0 30px 40px rgba(27,42,94,.26)) drop-shadow(0 6px 12px rgba(27,42,94,.14));
  animation:ans-sway 14s ease-in-out infinite}
@keyframes ans-sway{0%,100%{transform:translate3d(0,0,0) rotate(0deg)}
  50%{transform:translate3d(0,-12px,0) rotate(.5deg)}}
/* Navy, not gold: 11.5px uppercase. */
.ans .treecap{position:relative;z-index:2;font-family:var(--f);font-size:11.5px;font-weight:500;
  letter-spacing:.13em;text-transform:uppercase;color:var(--navy);text-align:center;margin:14px 0 0}
.ans .treecap span{display:block;text-transform:none;letter-spacing:0;font-weight:400;font-size:13.5px;
  color:#5A688F;margin-top:8px;line-height:1.55;max-width:34ch;margin-left:auto;margin-right:auto}

/* Below 1000px the aside stacks under the thesis text rather than being
   hidden. The prototype hid it, which also removed the pull quote and the
   tree's caption -- content, not decoration, and absent on every phone.
   These two blocks sit after every rule they narrow, not with .th above: a
   media query adds no specificity, so an unqualified .ans .treecol declared
   later would simply win back its wide-screen padding. */
@media(max-width:1000px){
  .ans .th{grid-template-columns:2px minmax(0,1fr)}
  .ans .treecol{grid-column:2;padding-top:8px}
  .ans .treewrap img{max-width:320px}
}
@media(max-width:620px){
  .ans .th{grid-template-columns:1fr}
  .ans .th .rail{display:none}
  .ans .treecol{grid-column:1}
}

/* ── pull quote (sits under the Tree) ───────────────────────────────────── */
.ans .pq{margin:26px 0 0;padding:30px 28px 24px;border-radius:16px;max-width:none;position:relative;overflow:hidden;
  background:linear-gradient(162deg,#fff,#FCFAF3);border:1px solid var(--line-l);
  box-shadow:inset 0 1px 0 #fff,0 1px 2px rgba(27,42,94,.04),0 24px 50px -30px rgba(27,42,94,.34)}
/* The opening quotation mark used to be a CSS `content` glyph, which most
   screen readers announce ("left double quotation mark") before the quote it
   decorates. `speak:never` has too little support to rely on, so the glyph
   now comes from markup inside an aria-hidden span and this rule only places
   and styles it. */
.ans .pq .qm{position:absolute;left:20px;top:6px;
  font-family:var(--f);font-weight:700;font-size:56px;line-height:1;
  color:var(--gold);opacity:.32;pointer-events:none}
.ans .pq p{font-family:var(--f);font-weight:500;font-size:15.5px;line-height:1.55;color:var(--navy);
  letter-spacing:-.014em;margin:0 0 18px;max-width:none;position:relative;z-index:2}
.ans .pq footer{all:unset;display:flex;align-items:center;gap:13px;padding-top:16px;
  border-top:1px solid var(--line-l);font-size:12px;color:#5A688F}
/* Common rule 7 requires the quote portrait to be a real wp:image block, so
   its <figure class="wp-block-image"> -- not the <img> -- is now the
   footer's direct flex child. Besides `flex:none` (the prototype's bare
   <img> got that for free, as a plain flex item with no box of its own to
   collapse), the figure also needs its margin reset: core's
   `.wp-block-image{margin:0 0 1em}` (from add_theme_support(
   'wp-block-styles')) and global styles' `:where(.is-layout-flow) > *
   {margin-block-start:24px}` both land on it. .ans .pq footer img below
   keeps sizing the <img> inside it exactly as before. */
.ans .pq footer .wp-block-image{margin:0;flex:none}
.ans .pq footer img{width:44px;height:44px;border-radius:50%;object-fit:cover;object-position:center 16%;flex:none;
  box-shadow:0 0 0 1px rgba(184,147,47,.5),0 8px 16px -6px rgba(27,42,94,.35)}
/* p, not the prototype's span: fix round 1 item 2 makes this byline a real,
   editable wp:paragraph rather than a hand-typed string, and core/paragraph
   always saves a <p>. margin:0 cancels .ans p{margin:0 0 1em} (a plain
   element selector, so it would otherwise apply here too) now that this is
   a real paragraph rather than an inline span with no margin of its own. */
.ans .pq footer p{font-family:var(--f);font-size:11.5px;color:#5A688F;line-height:1.45;min-width:0;margin:0}
/* .name, not the prototype's inline <b>: fix round 2 item 1 splits the name
   and title into two separate wp:paragraph blocks rather than one paragraph
   with the name wrapped in inline bold -- editing across an inline run's
   boundary in the block editor is fragile, and the two are distinct fields
   anyway. This rule carries the styling .ans .pq footer b used to; no
   display:block needed, since a <p> is block-level already. */
.ans .pq footer .name{font-size:14.5px;color:var(--navy);font-weight:600;letter-spacing:-.02em;margin-bottom:1px}

/* ── publication list ───────────────────────────────────────────────────── */
.ans .publist{list-style:none;margin:0;padding:0}
/* Restructured for Task 11. The prototype wrapped both lines of an entry in
   one <a>, but per this run's convention (a stacked label/value pair is two
   blocks, not one block with inline formatting) .t and .m are now two
   separate real wp:paragraph blocks -- a shape core/list-item's single
   rich-text content model cannot hold (each entry is a wp:group li instead;
   see anneloes-books.php). So the padding/hover/dot treatment that lived on
   .publist a moves to .publist li, the whole row, and only the title (.t)
   carries a real link; the citation line (.m) stays plain text. */
.ans .publist li{border-top:1px solid var(--line-l);padding:16px 0 16px 18px;position:relative;
  transition:padding-left .5s var(--ease)}
.ans .publist li::before{content:"";position:absolute;left:2px;top:24px;width:6px;height:6px;border-radius:50%;
  background:var(--gold);box-shadow:0 0 9px var(--goldg);transition:transform .5s var(--ease)}
/* :has(a:hover), not a plain :hover on the row: only .t's <a> is clickable
   (the citation line .m is plain text, per the comment above), so the whole
   row lighting up on a hover anywhere in it -- including over .m, which does
   nothing -- promised more than the click target delivers. Scoped to the
   link itself so the affordance and the behaviour agree; a plain :hover on
   .t's <a> alone can't reach these two rules, since they style the li (the
   :before dot sits at the row's left edge, not under the title), which is
   why this needs :has() rather than a descendant selector. */
.ans .publist li:has(a:hover){padding-left:28px}
.ans .publist li:has(a:hover)::before{transform:scale(1.4)}
/* margin matters now that .t/.m are real wp:paragraph blocks (<p>s), not the
   prototype's <span>s: .ans p{margin:0 0 1em} is an element selector and
   would otherwise apply here, same trap as .ans .chip and the endorsements
   byline. .ans a{color:inherit} already makes the title's <a> read as navy
   with no separate hover rule needed. */
.ans .publist .t{display:block;font-family:var(--f);font-weight:600;font-size:16px;color:var(--navy);
  letter-spacing:-.022em;line-height:1.32;margin:0 0 4px}
.ans .publist .m{display:block;font-size:13px;color:#6B6754;line-height:1.5;margin:0}

/* ── endorsements ───────────────────────────────────────────────────────── */
/* This grid never actually applies: .ans .card .lift (specificity 0,3,0)
   sets display:flex;flex-direction:column, and every .end element in these
   patterns also carries .lift, so .lift's flex wins over this rule's grid on
   specificity regardless of source order. .lift.end has been a flex column,
   not a two-column grid, since the endorsements pattern was first written --
   fix round 1 item 1 removed the pattern's one intervening wrapper div on
   the strength of that (a <figure>'s caption must be its first or last
   child, so the portrait, the blockquote and the figcaption are now direct
   children, stacked by this flex column), and it cost nothing, because the
   grid below was already dead. Left in place rather than deleted, since
   removing it changes nothing either way and a future reader may still
   expect .end to explain its own two-column shape from the prototype. */
.ans .end{display:grid;grid-template-columns:auto 1fr;gap:20px;align-items:center}
.ans .end img{width:76px;height:76px;border-radius:50%;object-fit:cover;flex:none;
  box-shadow:0 0 0 1px rgba(184,147,47,.5),0 12px 26px -10px rgba(27,42,94,.4)}
/* Common rule 7 requires each portrait to be a real wp:image block, so its
   <figure class="wp-block-image"> is now one of .end's flex children (see
   the comment above .ans .end). Core's `.wp-block-image{margin:0 0 1em}`
   (from add_theme_support('wp-block-styles')) and global styles'
   `:where(.is-layout-flow) > *{margin-block-start:24px}` both land on it;
   reset both so the 76px sizing on .ans .end img above still reads as the
   whole image. */
.ans .end .wp-block-image{margin:0}
/* Same reasoning as .ans .th h2: the prototype's h2 in this section and in
   #proof both carried an inline max-width style core/heading has no support
   to back. */
.ans #endorsements .head h2{max-width:20ch}
/* The prototype paired a bare <q> with a sibling <p class="by"> -- no
   programmatic relationship, so a screen reader announces a floating
   quotation and then an unattached name. Fix round 1 item 1: the patterns
   now use <figure class="lift end"><figure class="wp-block-image">…
   </figure><blockquote><p>…</p></blockquote><figcaption class="by">…
   </figcaption></figure> -- portrait, quote and caption all direct children
   of the outer figure, per the HTML content model's requirement that a
   figcaption be its figure's first or last child to actually caption it. So
   these rules move from .end q (which no longer exists) to .end
   blockquote/blockquote p. quotes:none is dropped with it: that property
   only ever affected the <q> element's own generated marks, and blockquote
   has none to suppress. */
.ans .end blockquote{margin:0}
.ans .end blockquote p{font-family:var(--f);font-weight:500;font-size:clamp(17px,1.9vw,21px);line-height:1.42;
  color:var(--navy);letter-spacing:-.022em;margin:0 0 12px}
.ans .end .by{font-family:var(--f);font-size:12px;color:#5A688F;line-height:1.5}
/* Fix round 1 item 2: the endorser's name and title are now real, editable
   wp:paragraph blocks inside the <figcaption>, not a hand-typed string.
   .ans .end .by is a class selector, so the <figcaption> itself needed no
   change; margin:0 cancels .ans p{margin:0 0 1em} (an element selector) now
   that real <p>s exist here for it to match. */
.ans .end .by p{margin:0}
/* .name, not the prototype's inline <b>: fix round 2 item 1 splits the name
   and title into two separate wp:paragraph blocks rather than one paragraph
   with the name wrapped in inline bold -- same reasoning as .ans .pq
   footer .name above. Carries the styling .ans .end .by b used to; no
   display:block needed, since a <p> is block-level already. */
.ans .end .by .name{font-size:15px;color:var(--navy);font-weight:600;letter-spacing:-.02em}
@media(max-width:620px){.ans .end{grid-template-columns:1fr}}

/* ── endorsements, AI & games (#endorsements-ai) ─────────────────────────── */
/* The second set of endorsements -- not a section of its own, but a block
   inside #arena's .wrap (see themes/earthwise/inc/anneloes-endorsements-ai.php).
   Dark ground, portrait stacked above the quote rather than beside it.
   Same card anatomy as .end above -- a
   <figure class="lift end-v"> holding a wp:image, a <blockquote> and a
   <figcaption class="by"> as direct children -- so only the axis, the
   centring and the dark-ground colours differ.

   .card .lift.end-v (0,4,0), not a bare .end-v (0,2,0): .ans .card .lift
   already sets display:flex;flex-direction:column at 0,3,0, so a bare rule
   would lose on specificity and never apply. That is exactly the trap
   .ans .end fell into and documents above; here the flex column is wanted,
   and the only thing this adds is the centring, so it is written at a
   specificity that actually reaches. */
.ans .card .lift.end-v{text-align:center}
/* The circle -- the clip, the size and the keyline -- is on the wp:image
   <figure>, and the <img> fills it.

   Auto margins centre the figure rather than align-items on the column, so
   the <blockquote> and the <figcaption> keep the column's full width -- the
   byline's border-top has to span the card, not just the width of the name.
   The margin also cancels core's .wp-block-image{margin:0 0 1em} and global
   styles' :where(.is-layout-flow) > *{margin-block-start:24px}, both of
   which land on this figure; same reset as .ans .end .wp-block-image.

   scale(1.02) is not a crop preference and not a fix for a bad asset -- the
   supplied portraits are 1280px squares whose circular crop is centred and
   tangent, so the clip already coincides with it. It exists only to pull the
   image's own antialiased edge inside the clip: the ground outside that
   baked circle is white, so a sub-pixel of it surviving reads as a pale
   fringe against the dark card, which 1.00 shows and 1.02 does not (compared
   side by side at 1.00/1.02/1.05). Two per cent of a 1280px source is not
   visible as a crop.

   (The first versions of these three files could not be made to work this
   way at all: each had a circular crop baked in that was inset from its
   square AND off-centre, by a different amount per image, so no single clip
   coincided with all three and each card showed a dark crescent of its own
   size. That was an asset problem and it was fixed in the assets -- see
   scripts/import-anneloes-media.sh -- rather than compensated for here.) */
.ans .end-v .wp-block-image{width:88px;height:88px;margin:0 auto 18px;border-radius:50%;
  overflow:hidden;box-shadow:0 0 0 1px rgba(245,200,106,.55),0 14px 30px -10px rgba(0,0,0,.85)}
.ans .end-v img{width:100%;height:100%;object-fit:cover;display:block;transform:scale(1.02)}
.ans .end-v blockquote{margin:0 0 18px}
/* Lighter and smaller than .end blockquote p: these three quotes run about
   twice the length of the four in #endorsements, and this section is
   three-up where that one is two-up. margin:0 cancels .ans p{margin:0 0 1em}
   (an element selector), the same trap the byline and the chips hit. */
.ans .end-v blockquote p{font-family:var(--f);font-weight:400;
  font-size:clamp(15px,1.6vw,17px);line-height:1.55;letter-spacing:-.014em;
  color:#EAF6FB;margin:0}
/* margin-top:auto pins the byline to the bottom of the flex column, so the
   three cards' rules line up across the row however unevenly the quotes
   wrap. .end has no equivalent: its cards are two-up and its quotes are one
   line each, so nothing there needs pinning. */
.ans .end-v .by{font-family:var(--f);font-size:12px;line-height:1.5;color:var(--mute);
  margin-top:auto;padding-top:16px;border-top:1px solid rgba(245,200,106,.28)}
.ans .end-v .by p{margin:0}
/* White, not --navy: this section is on the dark ground. Same split into a
   real .name paragraph rather than an inline <b> as .ans .end .by .name. */
.ans .end-v .by .name{font-size:15px;font-weight:600;color:#fff;letter-spacing:-.02em;margin:0 0 3px}
/* These blocks are not their own section -- they sit inside #arena's .wrap,
   below the two product columns (see themes/earthwise/inc/
   anneloes-endorsements-ai.php for why). So the vertical separation .sec's
   own padding used to provide has to come from the .head instead. */
.ans .head.endorsements-ai{margin-top:clamp(48px,6vw,88px)}
/* Same reasoning as .ans #endorsements .head h2: the prototype carried an
   inline max-width style on its heading that core/heading has no support to
   back. An h3 here, not an h2 -- it is a subsection of "Two halves of the
   same system". */
.ans .head.endorsements-ai h3{max-width:24ch}
/* One gradient across the whole line, white through cyan into gold, rather
   than an accent span on part of it: it is the run that carries the meaning
   here (cyan is the agentic track, gold the human and organisational one --
   see docs/mockups/README.md), and a CSS treatment on the heading cannot be
   lost by an editor retyping the words, which an inline span can.
   background-image, never the shorthand -- see .ans .gt. The darkest stop is
   --gold at #F5C86A, measured against the section ground in
   scripts/check-contrast.py; gold is only a contrast problem on the cream
   sections, and this one is dark. */
.ans .head.endorsements-ai h3{
  background-image:linear-gradient(96deg,#FFFFFF 0%,#DCF4FC 22%,var(--cy-lo) 42%,#E8C77E 68%,var(--gold) 100%);
  -webkit-background-clip:text;background-clip:text;color:transparent;-webkit-text-fill-color:transparent}
/* Gold, where every other card on this page is cyan (dark) or gold-on-cream
   (light). Scoped to .g3.endorsements-ai rather than to .card, because
   .ans .card and .ans .card:hover are page-wide; these selectors are one
   class more specific than each of those, so they win on specificity
   wherever they land in the file. The sheen is .ans .card's ::after, not
   the prototype's empty .spec div -- see the comment above that rule. */
.ans .g3.endorsements-ai .card{border-color:rgba(245,200,106,.34);
  background:linear-gradient(162deg,rgba(245,200,106,.055),rgba(91,200,232,.05) 46%,rgba(10,21,38,.88));
  box-shadow:inset 0 1px 0 rgba(255,255,255,.14),0 24px 56px -32px rgba(0,0,0,.9),0 0 34px -26px rgba(245,200,106,.7)}
.ans .g3.endorsements-ai .card:hover{border-color:rgba(245,200,106,.75);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.24),0 44px 82px -34px rgba(0,0,0,.95),0 0 56px -22px rgba(245,200,106,.55)}
.ans .g3.endorsements-ai .card::after{
  background:radial-gradient(300px circle at var(--mx,50%) var(--my,50%),rgba(245,200,106,.16),transparent 58%)}
/* The grid goes one-up at 880px (see .g2/.g3/.g4 above); shrink the portrait
   at the same breakpoint so it is not the single largest thing on a phone. */
@media(max-width:880px){.ans .end-v .wp-block-image{width:74px;height:74px}}

/* ── proof ──────────────────────────────────────────────────────────────── */
.ans #proof .head h2{max-width:18ch}
/* Navy, not gold: 10.5px uppercase. */
.ans .pf .k{font-family:var(--f);font-size:10.5px;font-weight:600;letter-spacing:.17em;text-transform:uppercase;color:var(--navy);margin:0 0 11px}
.ans .pf .v{font-family:var(--f);font-weight:600;font-size:17px;line-height:1.32;color:var(--navy);letter-spacing:-.024em;margin:0 0 8px}
.ans .chips{display:flex;flex-wrap:wrap;gap:9px;margin-top:26px}
/* margin:0 matters now that each chip is a real wp:paragraph.chip (a <p>),
   not the prototype's <span>: .ans p{margin:0 0 1em} is an element selector
   and would otherwise apply here, adding an unwanted 16px of bottom margin
   per chip -- a taller row, and a 25px rather than 9px gap wherever it
   wraps. */
.ans .chip{margin:0;font-family:var(--f);font-size:11.5px;font-weight:500;color:var(--navy);padding:8px 16px;border-radius:100px;
  border:1px solid var(--line-l);background:linear-gradient(162deg,#fff,#FCFAF3);
  box-shadow:inset 0 1px 0 #fff,0 8px 20px -14px rgba(27,42,94,.4);
  transition:transform .45s var(--ease),border-color .45s,box-shadow .45s}
.ans .chip:hover{transform:translateY(-2px);border-color:rgba(184,147,47,.6);box-shadow:0 16px 30px -18px rgba(27,42,94,.4)}
/* Navy, not gold: an award year at 11.5px. It loses its colour distinction
   from the rest of the chip and keeps only its weight -- bold but not gold
   still reads as emphasised. */
.ans .chip b{color:var(--navy)}
.ans .dark .chip{color:#DCEBF3;border-color:rgba(91,200,232,.24);background:rgba(91,200,232,.07);box-shadow:none}

/* ── work ───────────────────────────────────────────────────────────────── */
/* Same reasoning as .ans .th h2 above: the prototype's h2 carried an inline
   style="max-width:24ch" that core/heading has no support to back. */
.ans #work .head h2{max-width:24ch}

/* ── doors ──────────────────────────────────────────────────────────────── */
/* Navy, not gold: 12.5px. */
.ans .door .num{font-family:var(--f);font-weight:700;font-size:12.5px;letter-spacing:.16em;color:var(--navy);margin:0 0 14px}
/* Slate, not navy: this line sits directly under a navy h3, and making both
   navy would flatten the hierarchy the line exists to provide. The mockup's
   #8894B0 measured 2.88:1; --slate-tx is the same hue and saturation at 4.54:1. */
.ans .door .who{font-family:var(--f);font-size:11px;font-weight:600;letter-spacing:.13em;text-transform:uppercase;color:var(--slate-tx);margin:0 0 14px}
.ans .door p{font-size:15px;line-height:1.6}
.ans .door ul{list-style:none;margin:2px 0 20px;padding:0}
.ans .door li{font-size:14px;line-height:1.5;padding:8px 0 8px 19px;position:relative;border-top:1px solid rgba(27,42,94,.07)}
.ans .door li:first-child{border-top:0}
.ans .door li::before{content:"";position:absolute;left:2px;top:15px;width:6px;height:6px;border-radius:50%;
  background:var(--gold);box-shadow:0 0 9px var(--goldg)}
.ans .door .btns{margin-top:auto;padding-top:6px}

/* ── speaking ───────────────────────────────────────────────────────────── */
/* Same reasoning as .ans .th h2 above: the prototype's h2 carried an inline
   style="max-width:17ch" that core/heading has no support to back. */
.ans #speaking .head h2{max-width:17ch}
.ans .spk{display:grid;grid-template-columns:1fr .8fr;gap:clamp(28px,4.5vw,58px);align-items:start}
@media(max-width:980px){.ans .spk{grid-template-columns:1fr}}
.ans .topics{list-style:none;margin:0;padding:0}
.ans .topics li{padding:21px 0;border-top:1px solid var(--line-l);position:relative;transition:padding-left .55s var(--ease)}
.ans .topics li::after{content:"";position:absolute;left:0;top:-1px;height:1px;width:100%;
  background:linear-gradient(90deg,var(--gold-dk),var(--gold),transparent);
  transform:scaleX(0);transform-origin:left;transition:transform .7s var(--ease)}
.ans .topics li:hover{padding-left:14px}
.ans .topics li:hover::after{transform:scaleX(1)}
.ans .topics h4{font-size:19px;letter-spacing:-.024em;margin-bottom:5px;line-height:1.28}
.ans .topics p{font-size:14px;line-height:1.6;margin:0;color:#5A688F;max-width:52ch}
.ans .sig-t{font-family:var(--f);font-size:9.5px;font-weight:700;letter-spacing:.14em;color:#2C1F03;
  background:linear-gradient(140deg,var(--gold),var(--gold-dk));padding:3px 10px;border-radius:100px;
  margin-left:9px;vertical-align:middle;display:inline-block}
/* .stages is a name this file introduces: the prototype had no class here at
   all, just a bare div with an inline style="margin-top:30px;padding-top:
   22px;border-top:1px solid var(--line-l)". margin-top and padding-top ARE
   expressible as core/group spacing attributes (checked block.json's
   supports.spacing: margin top/bottom and padding are both supported, same
   as .ans .fmt's margin-top below) -- those two are house-style convenience,
   not forced. The border is the genuinely forced part: checked block.json's
   supports.__experimentalBorder too, and colour IS supported in general, but
   WordPress's border-colour control binds to a theme.json palette slug or a
   literal colour, not an arbitrary CSS custom property -- and promoting
   --line-l to a palette entry to satisfy it would leak this page's own token
   into every other page's colour picker, which is exactly the isolation
   this file exists to keep. The border needed a real CSS rule regardless, so
   the margin/padding travelled with it into the same selector rather than
   being split out as block attributes elsewhere. */
.ans .stages{margin-top:30px;padding-top:22px;border-top:1px solid var(--line-l)}
/* The prototype zeroed this specific .chips instance's own 26px margin-top
   with an inline style, since .stages' own padding-top already provides the
   gap above it; without this the two would stack to 48px. */
.ans .stages .chips{margin-top:0}
/* The showreel is not a link and does not lift. The mockup said so twice, in a
   separate earlier .reel block -- cursor:default and a transform:none hover --
   which the rule below then overrode, so the panel lifted anyway; that is the
   same duplicate-declaration defect R4 exists to fix, and two explicit
   statements of intent outrank an accident of it. One rule, no hover
   transform; the shadow still deepens, which the author never contradicted. */
.ans .reel{position:relative;border-radius:16px;overflow:hidden;border:1px solid var(--line-l);background:#000;display:block;cursor:default;
  box-shadow:0 30px 64px -34px rgba(27,42,94,.45);transition:box-shadow .6s var(--ease)}
.ans .reel:hover{box-shadow:0 44px 82px -34px rgba(27,42,94,.5)}
.ans .reel .ov{position:absolute;inset:0;display:grid;place-items:center;background:linear-gradient(180deg,rgba(4,8,20,.04),rgba(4,8,20,.4))}
.ans .reel .ov i{width:62px;height:62px;border-radius:50%;display:grid;place-items:center;
  background:linear-gradient(150deg,var(--gold-lo),var(--gold) 48%,var(--gold-dk));
  box-shadow:inset 0 1px 0 rgba(255,255,255,.7),0 16px 34px -10px rgba(0,0,0,.55);transition:transform .5s var(--ease)}
.ans .reel:hover .ov i{transform:scale(1.1)}
.ans .reel .ov svg{width:20px;height:20px;margin-left:3px;fill:#2C1F03}
/* Common rule 7 requires the showreel to be a real wp:image block, which
   wraps it in a <figure class="wp-block-image"> the prototype's bare <img>
   never had. Core's `.wp-block-image{margin:0 0 1em}` and global styles'
   margin-block-start would otherwise inset the image inside .reel's
   border-radius/overflow:hidden box -- reset the same way .ans .plate
   .wp-block-image and .ans .treewrap .wp-block-image already do. */
.ans .reel .wp-block-image{margin:0}
/* The prototype's inline style="margin-top:20px" on the fmt card, separating
   it from the reel image above it in the same column -- moved to a class
   selector for the same reason as .ans .stages above (core/group's spacing
   support covers margin, but the house convention on this file is a
   dedicated CSS rule over an inline style JSON attribute; see the comment on
   .ans .th h2). */
.ans .fmt{margin-top:20px}
.ans .fmt dl{margin:0;display:grid;grid-template-columns:auto 1fr;gap:10px 18px;font-size:14px}
.ans .fmt dt{font-family:var(--f);font-weight:600;color:var(--navy);white-space:nowrap}
.ans .fmt dd{margin:0;color:#5A688F}

/* ── books ──────────────────────────────────────────────────────────────── */
/* Same reasoning as .ans .th h2 above: the prototype's h2 carried an inline
   style="max-width:26ch" that core/heading has no support to back. Its
   "allgold" class is unrelated to this and is one of the four accepted
   gold-text exceptions -- see the block above .lite h2 .ac. */
.ans #books .head h2{max-width:26ch}
.ans .bk{display:grid;grid-template-columns:auto 1fr;gap:24px;align-items:center}
/* The translateZ pair is inert now that .card no longer preserves 3D (see the
   note there); the cover still lifts, on the translateY. */
.ans .bk .cov{width:158px;flex:none;margin:0;transform:translateZ(40px);transition:transform .7s var(--ease);
  filter:drop-shadow(0 18px 32px rgba(27,42,94,.3)) drop-shadow(0 3px 7px rgba(27,42,94,.16))}
.ans .bk:hover .cov{transform:translateZ(58px) translateY(-6px)}
/* Navy, not gold: 10.5px uppercase. */
.ans .bk .aw{font-family:var(--f);font-size:10.5px;font-weight:600;letter-spacing:.12em;text-transform:uppercase;
  color:var(--navy);margin:0 0 10px;line-height:1.6}
.ans .bk p{font-size:14px;line-height:1.56;margin-bottom:12px}
/* The prototype made the whole card a single <a>; no core block can both
   contain block content (the heading, the award line, the blurb) and be a
   hyperlink, so only the <h3> carries a real <a> now -- perfectly normal
   inside a heading's rich text, unlike an href on a wp:group's own tag. This
   stretches that one link across the whole card so the click target is
   unchanged.
   .ans .card a{position:relative} (above) makes the <a> ITSELF a positioned
   ancestor, so an ::after generated on it takes its containing block from
   the <a>'s own inline box, not from .card two levels up -- inset:0 would
   then only cover the title text, not the card (CSS2.1 10.1). Overridden
   back to position:static here (higher specificity than the general .card a
   rule: two classes plus two elements beats two classes plus one) so the
   search for a positioned ancestor continues past the <a> to .card itself
   (.ans .card{position:relative}, unconditional), which is what inset:0 then
   sizes against. z-index:6 becomes moot on a static element, which is fine:
   .card::after (the cursor sheen) has pointer-events:none, so nothing
   competes with this overlay for clicks regardless of stacking order. */
.ans .bk h3 a{position:static}
.ans .bk h3 a::after{content:"";position:absolute;inset:0}
/* .tlink ("More on the book ->") is plain text here, not a link, but the
   generic .card a,.card .btn,.card .tlink rule still puts it at z-index:6,
   above this stretched-link overlay -- making it the one part of an
   otherwise fully clickable card that swallows the click instead of
   activating h3's link underneath. pointer-events:none lets clicks fall
   through to the overlay everywhere .tlink's own text sits. */
.ans .bk .tlink{pointer-events:none}
@media(max-width:620px){.ans .bk{grid-template-columns:1fr}.ans .bk .cov{width:130px}}
/* The prototype's inline style="margin-top:26px;font-size:15.5px" on the
   "also co-editor" paragraph, between the book cards and the publications
   list. core/paragraph's spacing.margin and typography.fontSize both
   support this natively (checked block.json) -- a class is the house-style
   choice on this file over an inline style JSON attribute, same as
   .ans .fmt's margin-top above, not something the block schema forced. */
.ans .books-note{margin-top:26px;font-size:15.5px}
/* The prototype's inline style="margin-top:clamp(34px,4vw,54px)" on the
   publications wrapper, marking it as a new content group after the trilogy
   note. core/group's spacing.margin supports top/bottom natively -- house
   style, not forced, same as .ans .books-note above. */
.ans .pubs{margin-top:clamp(34px,4vw,54px)}
/* The prototype's inline style="margin-top:18px" on the "read the full CV"
   link, pulling it closer to the list above it than the default paragraph
   spacing would. core/paragraph supports spacing.margin natively -- house
   style, not forced, same as .ans .books-note above. */
.ans .pubs .sm{margin-top:18px}

/* ── arena (dark) ───────────────────────────────────────────────────────── */
.ans .dark h2,.ans .dark h3,.ans .dark h4{color:#fff}
.ans .dark .eb{color:var(--cy)}
.ans .dark .lede{color:#DEE8F4}
.ans .shot{border-radius:16px;overflow:hidden;position:relative;
  box-shadow:0 40px 90px -34px rgba(0,0,0,.92),inset 0 1px 0 rgba(255,255,255,.1);
  border:1px solid rgba(91,200,232,.22)}

/* ── build blocks: two products, side by side ───────────────────────────── */
.ans .duo{display:grid;grid-template-columns:1fr 1fr;gap:clamp(22px,3vw,38px);align-items:start}
@media(max-width:900px){.ans .duo{grid-template-columns:1fr}}
.ans .prod{display:flex;flex-direction:column;height:100%}
.ans .prod .tagline{font-family:var(--f);font-size:10.5px;font-weight:600;letter-spacing:.17em;
  text-transform:uppercase;color:var(--cy);margin:18px 0 8px}
.ans .prod h3{font-size:clamp(20px,2.2vw,26px);margin-bottom:10px}
.ans .prod p{font-size:15px;line-height:1.62;color:#BFD0DE;max-width:none}
.ans .prod .foot{margin-top:auto}

/* progression bars — the benchmark story */
.ans .prog{margin:18px 0 4px;padding:16px 0 4px;border-top:1px solid rgba(91,200,232,.18)}
.ans .prog .row{margin-bottom:13px}
.ans .prog .row:last-child{margin-bottom:0}
.ans .prog .top{display:flex;justify-content:space-between;align-items:baseline;gap:12px;margin-bottom:6px}
/* .lb/.vl are real wp:paragraph blocks now (a stacked label/value pair is two
   blocks, not one with inline formatting -- see anneloes-building.php), so
   .ans p{margin:0 0 1em} (an element selector) would otherwise add unwanted
   space below this flex row; both reset it here, same reasoning as .ans
   .trac .l below. */
.ans .prog .lb{font-family:var(--f);font-size:11.5px;font-weight:500;letter-spacing:.06em;color:var(--mute);margin:0}
.ans .prog .vl{font-family:var(--f);font-size:15.5px;font-weight:700;letter-spacing:-.028em;color:#DCEBF3;margin:0}
.ans .prog .track{height:5px;border-radius:100px;background:rgba(255,255,255,.07);overflow:hidden}
.ans .prog .fill{height:100%;border-radius:100px;background:linear-gradient(90deg,var(--cy-dk),var(--cy))}
.ans .prog .row.hi .vl{color:#fff}
.ans .prog .row.hi .fill{background:linear-gradient(90deg,var(--cy),var(--cy-lo))}
.ans .prog .note{font-family:var(--f);font-size:11.5px;color:var(--mute);margin:12px 0 0;line-height:1.5}

/* traction figures */
.ans .trac{display:flex;gap:26px;flex-wrap:wrap;margin:18px 0 4px;padding:16px 0 4px;
  border-top:1px solid rgba(91,200,232,.18)}
.ans .trac .n{font-family:var(--f);font-weight:700;font-size:clamp(21px,2.4vw,28px);line-height:1;
  letter-spacing:-.035em;display:block;margin-bottom:5px;
  background-image:linear-gradient(148deg,#fff,var(--cy));-webkit-background-clip:text;background-clip:text;
  color:transparent;-webkit-text-fill-color:transparent}
/* .n/.l are real wp:paragraph blocks now (same stacked label/value-pair
   convention as .ans .prog .lb/.vl above); .n already resets its own margin
   via display:block;margin-bottom:5px above, but .l had none, so
   .ans p{margin:0 0 1em} would otherwise add unwanted space beneath each
   stat. */
.ans .trac .l{font-family:var(--f);font-size:10.5px;font-weight:500;letter-spacing:.11em;
  text-transform:uppercase;color:var(--mute);margin:0}

/* ── talks ──────────────────────────────────────────────────────────────── */
/* Same reasoning as .ans .th h2 above: the prototype's h2 carried an inline
   style="max-width:24ch" that core/heading has no support to back. */
.ans #talks .head h2{max-width:24ch}
/* The prototype's inline style="margin-top:26px" on the "more talks and
   interviews" link, the only bare <p> directly under #talks .wrap.
   core/paragraph supports spacing.margin natively -- house style, not
   forced, same as .ans .books-note above; a context selector rather than a
   new class since nothing else needs the name. */
.ans #talks .wrap > p{margin-top:26px}
/* Each talk card is a real <button>: it swaps a thumbnail for a YouTube
   iframe on activation, which a <div> with a click handler gives no keyboard
   or screen-reader access to. display/width/padding/text-align/font/color
   below are that element's reset -- a <div>'s defaults, restored. Written as
   one rule rather than the mockup's shape plus an override, for the reason
   the doubled .th declaration was collapsed above. */
.ans .vid{position:relative;display:block;width:100%;padding:0;text-align:left;
  font:inherit;color:inherit;
  border-radius:16px;overflow:hidden;border:1px solid var(--line-l);background:#0A1526;
  box-shadow:0 24px 54px -32px rgba(27,42,94,.45);cursor:pointer;
  transition:transform .6s var(--ease),box-shadow .6s var(--ease),border-color .45s}
.ans .vid:hover{transform:translateY(-5px);border-color:rgba(184,147,47,.55);box-shadow:0 42px 78px -34px rgba(27,42,94,.5)}
.ans .vid.vid-playing:hover{transform:none}
.ans .vid .thumb{aspect-ratio:16/9;width:100%;object-fit:cover;display:block}
.ans .vid .ov{position:absolute;top:0;left:0;right:0;bottom:0;display:grid;place-items:center;
  background:linear-gradient(180deg,rgba(4,8,20,.06),rgba(4,8,20,.5))}
.ans .vid .ov i{width:56px;height:56px;border-radius:50%;display:grid;place-items:center;
  background:linear-gradient(150deg,var(--gold-lo),var(--gold) 48%,var(--gold-dk));
  box-shadow:inset 0 1px 0 rgba(255,255,255,.7),0 14px 30px -10px rgba(0,0,0,.6);transition:transform .5s var(--ease)}
.ans .vid:hover .ov i{transform:scale(1.1)}
.ans .vid .ov svg{width:19px;height:19px;margin-left:3px;fill:#2C1F03}
.ans .vid iframe{position:absolute;inset:0;width:100%;height:100%;border:0}
.ans .vtitle{font-family:var(--f);font-weight:600;font-size:16px;color:var(--navy);letter-spacing:-.022em;
  margin:14px 0 0;line-height:1.35}

/* ── embedded forms ─────────────────────────────────────────────────────── */
.ans .embed{margin-top:6px}
/* Kajabi's own stylesheet (form_embed-*.css, served from their CDN, CORS-
   blocked from reading directly) sets .kajabi-form--inline{padding:40px 20px}
   -- theirs, not ours, confirmed by inspecting the computed style since the
   rule itself isn't readable. That 40px top padding was the whole of the
   reported gap between each card's heading/description and its first field:
   the container itself has no visible background, border or shadow (fully
   transparent, confirmed the same way), so unlike the horizontal padding --
   which insets the fields from the card edge, real visual work, left alone
   -- the top and bottom padding are pure empty space with nothing to
   protect. .embed's own margin-top above (6px, or 14px inline on the
   Subscribe card) already sets this page's intended gap after the heading;
   Kajabi's 40px was stacking a second, much larger gap on top of it.
   Reduced, not zeroed: a few pixels of clearance keeps the first field from
   sitting flush against the description text, and the bottom is kept a
   little taller than the top since it is the button's only clearance from
   the card's own edge, with no heading above it to already provide some. */
.ans .embed .kajabi-form--inline{padding:4px 20px 22px!important}
.ans .embed input,.ans .embed select,.ans .embed textarea{font-family:var(--fb)!important;font-size:15px!important;
  border-radius:12px!important;border:1px solid rgba(27,42,94,.18)!important;background:#fff!important;
  color:var(--navy)!important;padding:13px 16px!important}
/* No outline:none here, unlike the mockup: these embedded fields are the only
   text inputs on the page, and suppressing the outline left a keyboard user
   with nothing but a border tint and a .28-alpha glow to locate the caret by.
   The gold border and glow stay as the design's focus treatment; the page's
   own :focus-visible ring sits on top of them. */
.ans .embed input:focus,.ans .embed textarea:focus{border-color:var(--gold-dk)!important;
  box-shadow:0 0 0 3px rgba(245,200,106,.28)!important}
.ans .embed button,.ans .embed input[type=submit]{font-family:var(--f)!important;font-weight:600!important;
  border-radius:100px!important;border:0!important;padding:14px 28px!important;cursor:pointer;
  background:linear-gradient(150deg,var(--gold-lo),var(--gold) 48%,var(--gold-dk))!important;color:#2C1F03!important;
  box-shadow:inset 0 1px 0 rgba(255,255,255,.72),0 12px 30px -12px var(--goldg)!important}
.ans .embed label{font-family:var(--f)!important;font-size:12.5px!important;color:#5A688F!important}

/* Google's reCAPTCHA, loaded by the two embedded forms' own third-party
   script, appends `.grecaptcha-badge` directly to <body> -- outside .ans,
   which every other rule in this file is scoped under (see this file's
   header). This one is deliberately UNSCOPED so it can reach it. That is
   safe only because functions.php dequeues this stylesheet everywhere except
   /anneloes/ (see the substitution described in the design spec's §3.2): no
   other page ever loads this file, so an unscoped selector here still cannot
   reach anything outside this one page.
   The badge is `position:fixed;right:-186px;width:256px` -- 70px left
   visible at the right edge -- which was widening this page's own
   `document.documentElement.scrollWidth` by exactly that 70px and forcing a
   horizontal scrollbar (measured at a 1328px viewport: scrollWidth was
   clientWidth+70). Every other page on the site clamps this kind of overflow
   with site.css's `body{overflow-x:hidden}` (site.css:27), but this page
   dequeues site.css and does not reinstate that mask on its own .ans rule
   (see the comment in the TOKENS block above) because the mask was hiding a
   real overflow rather than fixing one -- doing so here would silently
   re-hide the next genuine overflow this page grows, which is exactly what
   that removal was for. `display:none` removes the badge from layout
   entirely rather than clipping it, so it fixes the cause instead of masking
   it again. Hiding the badge without the attribution text below would break
   Google's reCAPTCHA terms, which require one or the other. */
.grecaptcha-badge{display:none!important}

/* ── bio ────────────────────────────────────────────────────────────────── */
.ans .bio{max-width:70ch}
.ans .bio p{font-size:15.5px;line-height:1.72;max-width:none}
.ans .bio details{margin-top:22px;border-top:1px solid var(--line-l);padding-top:20px}
/* Navy, not gold: 13.5px. */
.ans .bio summary{font-family:var(--f);font-size:13.5px;font-weight:600;letter-spacing:.03em;color:var(--navy);cursor:pointer;list-style:none}
.ans .bio summary::-webkit-details-marker{display:none}
/* The "+" was a CSS `content` glyph on `summary::before`, which some screen
   readers announce as "plus sign" before the summary text. The glyph now
   comes from an aria-hidden <span class="mk"> in markup instead (see
   anneloes-bio.php), and CSS still supplies its two-state look via ::before
   on that span -- "+" closed, "−" open, swapped by the details element's own
   [open] attribute -- rather than the single-glyph-rotated-45deg illusion
   this rule used before anneloes-bio.php supplied real open/close markup. */
.ans .bio summary .mk{font-weight:400;display:inline-block}
.ans .bio summary .mk::before{content:"＋ "}
.ans .bio details[open] summary .mk::before{content:"－ "}
.ans .bio .inner{padding-top:16px;animation:ans-unf .6s var(--ease) both}
@keyframes ans-unf{from{opacity:0;transform:translateY(-8px)}to{opacity:1;transform:none}}

/* ── footer ─────────────────────────────────────────────────────────────── */
.ans > footer{background:var(--cream2);border-top:1px solid var(--line-l);padding:38px 0 36px;font-size:13.5px;color:#5A688F}
.ans > footer .wrap{display:flex;justify-content:space-between;gap:22px;flex-wrap:wrap;align-items:center}
.ans > footer .links{display:flex;gap:20px;flex-wrap:wrap;font-family:var(--f);font-weight:500}
.ans > footer .social{display:flex;gap:10px;align-items:center}
.ans footer .social a{width:36px;height:36px;border-radius:50%;display:grid;place-items:center;
  border:1px solid var(--line-l);background:linear-gradient(162deg,#fff,#FCFAF3);
  box-shadow:inset 0 1px 0 #fff,0 8px 18px -14px rgba(27,42,94,.5);
  transition:transform .45s var(--ease),border-color .45s,box-shadow .45s}
.ans footer .social a:hover{transform:translateY(-3px);border-color:rgba(184,147,47,.6);box-shadow:0 14px 26px -14px rgba(27,42,94,.5)}
.ans footer .social svg{width:17px;height:17px;fill:var(--navy);transition:fill .4s}
.ans footer .social a:hover svg{fill:var(--gold-dk)}
/* Navy, not gold: 13.5px footer links. */
.ans > footer a:hover{color:var(--navy)}

/* ── reveals / a11y / motion ────────────────────────────────────────────── */
/* .rise/.in, not the mockup's own reveal class name, so that editor.css's
   "reveal everything" override, functions.php's <noscript> fallback and the
   reduced-motion block below all already cover this page: each carries a
   selector list that would otherwise have to grow a name for this one file,
   and a reveal nobody reveals is an invisible page. */
.ans .rise{opacity:0;transform:translateY(28px);transition:opacity .9s var(--ease),transform 1s var(--ease)}
.ans .rise.in{opacity:1;transform:none}
.ans .d1{transition-delay:.08s}.ans .d2{transition-delay:.16s}.ans .d3{transition-delay:.24s}
/* Two rules, because no single colour clears WCAG 1.4.11's 3:1 against both of
   this page's grounds: navy is 11.95:1 on --cream2 but 1.47:1 on --abyss, and
   --cy is 10.35:1 on --abyss but 1.69:1 on cream. The mockup's --gold-dk was
   2.54:1 on cream and failed -- 1.4.11 is non-text contrast, a different
   criterion from the large-text carve-out above, which covers text colour
   only, so the exemption does not stretch to a focus indicator. */
.ans a:focus-visible,.ans button:focus-visible,.ans input:focus-visible,.ans summary:focus-visible{outline:2px solid var(--navy);outline-offset:3px;border-radius:4px}
.ans .dark a:focus-visible,.ans .dark button:focus-visible,.ans .dark input:focus-visible,.ans .dark summary:focus-visible{outline-color:var(--cy)}
@media (prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  .ans *,.ans *::before,.ans *::after{animation:none!important;transition-duration:.01ms!important}
  .ans .rise{opacity:1;transform:none}
  .ans .key{display:none}
}
/* Forced-colors mode discards background-image, so gradient-clipped text
   renders with a transparent fill and disappears entirely -- including the
   page's own name in the hero. Restore a real fill and let the OS palette
   pick the colour. */
@media (forced-colors:active){
  .ans .brand,
  .ans .gt,
  .ans .gt-cy,
  .ans .trac .n,
  .ans .lite h2 .ac,
  .ans .dark h2 .ac,
  .ans .head.endorsements-ai h3,
  .ans .lite h2.allgold{
    background-image:none;
    -webkit-text-fill-color:currentColor;
    color:CanvasText;
  }
}
@media(max-width:980px){
  .ans .hero-g{grid-template-columns:1fr;gap:44px}
  .ans .plate{transform:none}
}
