/* ==========================================================================
   A. P. GOODMAN — PORTAL SYSTEM
   Palettes · portal gate · dust · cubic slide transitions · admin theme toggle
   Hello Elementor · no Elementor Pro, no addon plugins, no JS libraries

   Colours sampled from the source art in Pictures\Photoshop\APG.
   ========================================================================== */

/* ===== SITE DEFAULT THEME =================================================
   THIS IS THE ONE LINE TO CHANGE. It is what every public visitor sees.
   Options: b1 (blue / Legacy Academy) · b2 (amber / Astoria's Secret)
            b3 (green / Immortal Eyes)
   Currently: b2
   ======================================================================== */

:root {
  --apg-accent:      var(--apg-b2-core);
  --apg-accent-soft: var(--apg-b2);
  --apg-accent-pale: var(--apg-b2-pale);
  --apg-accent-bg:   var(--apg-b2-bg);
  --apg-portal-img:  url("/wp-content/uploads/2026/08/apg-portal-b2.webp");
  --apg-bg-img:      url("/wp-content/uploads/2026/08/apg-bg-b2.webp");
}

/* 1 · PALETTE TOKENS ------------------------------------------------------ */

:root {
  --apg-void:      #0A0B0E;
  --apg-void-soft: #121317;
  --apg-parchment: #F4F7FA;
  --apg-logo:      #E92290;

  --apg-b1-core: #0598FC; --apg-b1: #4AB8FF; --apg-b1-pale: #D3E9FF; --apg-b1-bg: #0C1F2D;
  --apg-b2-core: #FFA200; --apg-b2: #FFA911; --apg-b2-pale: #FAB462; --apg-b2-bg: #1F0400;
  --apg-b3-core: #00FF86; --apg-b3: #05F97A; --apg-b3-pale: #96FFD1; --apg-b3-bg: #001B12;
}

/* 2 · THEME SWITCHING -----------------------------------------------------
   Driven by the admin-only radio toggle. Pure CSS via :has() — no JS.
   ------------------------------------------------------------------------ */

body:has(#apg-t1:checked) {
  --apg-accent: var(--apg-b1-core); --apg-accent-soft: var(--apg-b1);
  --apg-accent-pale: var(--apg-b1-pale); --apg-accent-bg: var(--apg-b1-bg);
  --apg-portal-img: url("/wp-content/uploads/2026/08/apg-portal-b1.webp");
  --apg-bg-img:     url("/wp-content/uploads/2026/08/apg-bg-b1.webp");
}
body:has(#apg-t2:checked) {
  --apg-accent: var(--apg-b2-core); --apg-accent-soft: var(--apg-b2);
  --apg-accent-pale: var(--apg-b2-pale); --apg-accent-bg: var(--apg-b2-bg);
  --apg-portal-img: url("/wp-content/uploads/2026/08/apg-portal-b2.webp");
  --apg-bg-img:     url("/wp-content/uploads/2026/08/apg-bg-b2.webp");
}
body:has(#apg-t3:checked) {
  --apg-accent: var(--apg-b3-core); --apg-accent-soft: var(--apg-b3);
  --apg-accent-pale: var(--apg-b3-pale); --apg-accent-bg: var(--apg-b3-bg);
  --apg-portal-img: url("/wp-content/uploads/2026/08/apg-portal-b3.webp");
  --apg-bg-img:     url("/wp-content/uploads/2026/08/apg-bg-b3.webp");
}

/* 3 · ADMIN-ONLY TOGGLE UI ------------------------------------------------
   Hidden from the public: only renders when WordPress adds .logged-in to
   <body>, i.e. only when you are signed in. Logged-out visitors get
   display:none and never download or see it.
   ------------------------------------------------------------------------ */

.apg-theme-toggle { display: none; }

body.logged-in .apg-theme-toggle {
  display: flex;
  position: fixed;
  right: 14px;
  bottom: 14px;
  z-index: 99999;
  gap: 8px;
  padding: 8px 10px;
  border-radius: 999px;
  background: rgba(10,11,14,.82);
  backdrop-filter: blur(8px);
  border: 1px solid rgba(255,255,255,.14);
  align-items: center;
}
.apg-theme-toggle input { position: absolute; opacity: 0; pointer-events: none; }
.apg-theme-toggle label {
  width: 20px; height: 20px;
  border-radius: 50%;
  cursor: pointer;
  border: 2px solid rgba(255,255,255,.28);
  transition: transform .18s, border-color .18s;
}
.apg-theme-toggle label:hover { transform: scale(1.18); }
.apg-swatch-1 { background: #0598FC; }
.apg-swatch-2 { background: #FFA200; }
.apg-swatch-3 { background: #00FF86; }
#apg-t1:checked ~ label[for="apg-t1"],
#apg-t2:checked ~ label[for="apg-t2"],
#apg-t3:checked ~ label[for="apg-t3"] {
  border-color: #fff;
  transform: scale(1.22);
}
.apg-toggle-tag {
  font-size: 10px;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: rgba(255,255,255,.55);
  margin-right: 2px;
}

/* 4 · PORTAL GATE --------------------------------------------------------- */

.apg-gate {
  position: relative;
  min-height: 100svh;
  display: grid;
  place-items: center;
  background: radial-gradient(circle at 50% 45%, var(--apg-accent-bg) 0%, var(--apg-void) 68%);
  overflow: hidden;
  contain: layout paint;
}

.apg-portal {
  width: min(74vw, 62svh);
  aspect-ratio: 1;
  background-image: var(--apg-portal-img);
  background-size: contain;
  background-position: center;
  background-repeat: no-repeat;
  animation: apg-jelly 7s ease-in-out infinite, apg-breathe 4.5s ease-in-out infinite;
  will-change: transform, filter;
}

@keyframes apg-jelly {
  0%,100% { transform: scale(1)     skewX(0deg)    rotate(0deg);   }
  25%     { transform: scale(1.015) skewX(.7deg)   rotate(.4deg);  }
  50%     { transform: scale(.99)   skewX(-.5deg)  rotate(-.3deg); }
  75%     { transform: scale(1.02)  skewX(.3deg)   rotate(.2deg);  }
}
@keyframes apg-breathe {
  0%,100% { filter: brightness(1)    drop-shadow(0 0 26px var(--apg-accent)); }
  50%     { filter: brightness(1.14) drop-shadow(0 0 54px var(--apg-accent)); }
}

.apg-gate-inner {
  position: absolute;
  z-index: 3;
  text-align: center;
  width: min(90vw, 640px);
}
.apg-gate-inner h1 {
  color: var(--apg-parchment);
  font-size: clamp(1.5rem, 4vw, 2.8rem);
  line-height: 1.15;
  margin: 0 0 .6rem;
  text-shadow: 0 2px 26px var(--apg-void), 0 0 60px var(--apg-void);
}
.apg-gate-sub {
  color: var(--apg-accent-pale);
  font-size: clamp(.92rem, 1.6vw, 1.1rem);
  margin: 0;
  text-shadow: 0 2px 18px var(--apg-void);
}
.apg-enter {
  position: absolute;
  bottom: 6vh; left: 50%;
  transform: translateX(-50%);
  z-index: 3;
  color: var(--apg-accent-pale);
  font-size: .78rem;
  letter-spacing: .22em;
  text-transform: uppercase;
  animation: apg-blink 2.4s ease-in-out infinite;
  pointer-events: none;
}
@keyframes apg-blink { 0%,100% { opacity: 1 } 50% { opacity: .28 } }

/* 5 · MAGICAL DUST ------------------------------------------------------- */

.apg-dust { position: absolute; inset: 0; pointer-events: none; overflow: hidden; contain: strict; }
.apg-dust > * {
  position: absolute;
  width: 4px; height: 4px;
  border-radius: 50%;
  background: var(--apg-accent-pale);
  box-shadow: 0 0 8px 2px var(--apg-accent);
  opacity: 0;
  animation: apg-float 20s linear infinite;
  will-change: transform, opacity;
}
@keyframes apg-float {
  0%   { transform: translate3d(0, 25vh, 0) scale(.6); opacity: 0; }
  12%  { opacity: .7; }
  85%  { opacity: .45; }
  100% { transform: translate3d(5vw, -90vh, 0) scale(1.25); opacity: 0; }
}
.apg-dust > *:nth-child(1){left:6%;animation-delay:0s;animation-duration:19s;z-index:0}
.apg-dust > *:nth-child(2){left:15%;animation-delay:-3s;animation-duration:24s;z-index:4}
.apg-dust > *:nth-child(3){left:24%;animation-delay:-7s;animation-duration:16s;z-index:0}
.apg-dust > *:nth-child(4){left:33%;animation-delay:-11s;animation-duration:27s;z-index:4}
.apg-dust > *:nth-child(5){left:42%;animation-delay:-2s;animation-duration:21s;z-index:0}
.apg-dust > *:nth-child(6){left:51%;animation-delay:-15s;animation-duration:18s;z-index:4}
.apg-dust > *:nth-child(7){left:60%;animation-delay:-6s;animation-duration:25s;z-index:0}
.apg-dust > *:nth-child(8){left:69%;animation-delay:-9s;animation-duration:20s;z-index:4}
.apg-dust > *:nth-child(9){left:78%;animation-delay:-13s;animation-duration:23s;z-index:0}
.apg-dust > *:nth-child(10){left:86%;animation-delay:-4s;animation-duration:17s;z-index:4}
.apg-dust > *:nth-child(11){left:92%;animation-delay:-17s;animation-duration:26s;z-index:0}
.apg-dust > *:nth-child(12){left:97%;animation-delay:-8s;animation-duration:22s;z-index:4}

/* 6 · LAYERED BACKGROUND ------------------------------------------------- */

.apg-bg {
  position: fixed; inset: 0; z-index: 0;
  background-image: var(--apg-bg-img);
  background-size: cover;
  background-position: center;
  animation: apg-bg-drift 45s ease-in-out infinite;
  will-change: transform;
}
.apg-bg::after {
  content: ""; position: absolute; inset: 0;
  background: radial-gradient(circle at 50% 40%, transparent 0%, var(--apg-void) 88%);
  opacity: .85;
}
@keyframes apg-bg-drift {
  0%,100% { transform: scale(1.06) translate3d(0,0,0); }
  50%     { transform: scale(1.12) translate3d(-1.5%,-1%,0); }
}

/* 7 · INTERIOR SECTIONS -------------------------------------------------- */

.apg-section {
  position: relative; z-index: 2;
  padding: clamp(3rem, 7vw, 5.5rem) 1.25rem;
  color: var(--apg-parchment);
}
.apg-section h2 {
  color: var(--apg-accent);
  font-size: clamp(1.45rem, 3.2vw, 2.15rem);
  margin: 0 0 .6rem;
}
.apg-section h3 { color: var(--apg-parchment); }
.apg-section a { color: var(--apg-accent-pale); }
.apg-section a:hover { color: var(--apg-accent); }
.apg-lede { color: rgba(244,247,250,.82); max-width: 62ch; }
.apg-books img {
  transition: transform .4s cubic-bezier(.65,0,.35,1), filter .4s;
}
.apg-books img:hover {
  transform: translateY(-8px) scale(1.03);
  filter: drop-shadow(0 12px 30px var(--apg-accent));
}
.apg-cta .wp-block-button__link {
  background: var(--apg-accent);
  color: var(--apg-void);
  font-weight: 700;
  border-radius: 999px;
}

/* 8 · SCROLL PORTAL (interior) ------------------------------------------- */

@keyframes apg-portal-enter { from{transform:scale(1);opacity:1} to{transform:scale(6);opacity:0} }
.apg-portal-scroll {
  animation: apg-portal-enter linear both;
  animation-timeline: scroll(root block);
  animation-range: 0 100vh;
  will-change: transform, opacity;
}

/* 9 · CUBIC SLIDE PAGE TRANSITIONS --------------------------------------
   Cross-document View Transitions. Pure CSS, zero payload, no CLS.
   Unsupported browsers just navigate normally.
   ---------------------------------------------------------------------- */

@view-transition { navigation: auto; }
@keyframes apg-slide-out-left { to   { transform: translateX(-28%) scale(.96); opacity: 0; } }
@keyframes apg-slide-in-right { from { transform: translateX(100%)  scale(.96); opacity: 0; } }
::view-transition-old(root) { animation: apg-slide-out-left .62s cubic-bezier(.65,0,.35,1) both; }
::view-transition-new(root) { animation: apg-slide-in-right .62s cubic-bezier(.65,0,.35,1) both; }

/* 10 · ACCESSIBILITY ----------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  .apg-portal, .apg-dust > *, .apg-bg, .apg-portal-scroll, .apg-enter { animation: none !important; }
  .apg-dust { display: none; }
  ::view-transition-old(root), ::view-transition-new(root) { animation: none !important; }
}

/* 11 · MOBILE ------------------------------------------------------------ */

@media (max-width: 767px) {
  .apg-dust > *:nth-child(even) { display: none; }
  .apg-bg { animation-duration: 70s; }
  .apg-portal { width: 88vw; }
}


/* 12 · GATE COMPOSITION FIX
   Stack portal above type instead of overlapping. Keeps the ring readable
   and stops the H1 competing with the glow.
   ------------------------------------------------------------------------ */

.apg-gate {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: clamp(.5rem, 2.5vh, 1.75rem);
  padding: 10vh 1.25rem 12vh;
  background: radial-gradient(circle at 50% 45%,
    color-mix(in srgb, var(--apg-accent-bg) 70%, transparent) 0%,
    color-mix(in srgb, var(--apg-void) 92%, transparent) 68%);
}
.apg-portal {
  width: min(58vw, 42svh);
  flex: none;
}
.apg-gate-inner {
  position: static;
  width: min(92vw, 700px);
}
.apg-gate-inner h1 {
  font-size: clamp(1.45rem, 3.6vw, 2.5rem);
  text-shadow: 0 2px 18px var(--apg-void);
}
.apg-gate-sub { color: rgba(244,247,250,.78); }
.apg-enter { bottom: 4vh; }

@media (max-width: 767px) {
  .apg-portal { width: 72vw; }
  .apg-gate { padding: 8vh 1.25rem 12vh; }
}


/* 13 · READABILITY SCRIM
   The background art is busy. Frost the content sections so body copy keeps
   contrast without hiding the artwork behind it.
   ------------------------------------------------------------------------ */

.apg-section {
  background: color-mix(in srgb, var(--apg-void) 76%, transparent);
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
}
.apg-section + .apg-section { border-top: 1px solid rgba(255,255,255,.06); }
.apg-lede { color: rgba(244,247,250,.9); }
.apg-section h3 a { text-decoration: none; }
.apg-section h3 a:hover { text-decoration: underline; }


/* 14 · FULL-BLEED FIX
   Hello Elementor constrains .entry-content, which was letterboxing the gate
   and leaving the background art showing in bands at the sides.
   ------------------------------------------------------------------------ */

.apg-gate, .apg-section {
  width: 100vw;
  max-width: 100vw;
  margin-left: 50%;
  transform: translateX(-50%);
}
.apg-section > .wp-block-group__inner-container,
.apg-section { box-sizing: border-box; }
.apg-section {
  padding-left: max(1.25rem, calc((100vw - 1100px) / 2));
  padding-right: max(1.25rem, calc((100vw - 1100px) / 2));
}
body { overflow-x: hidden; }


/* =========================================================================
   15 · PORTAL v2 — bigger + responsive, centred cue, dust, scroll entry
   Pseudo-element based: no page markup needed, applies site-wide.
   Cubic slide page transitions are UNCHANGED (confirmed correct).
   ========================================================================= */

/* 15a · Portal sizing — scales off whichever of width/height is scarcer,
   so it never overflows a short laptop or a narrow phone. */

.apg-portal {
  position: relative;
  width: clamp(240px, min(54vw, 58svh), 860px);
  aspect-ratio: 1;
  flex: none;
}
@media (max-width: 767px)  { .apg-portal { width: min(86vw, 44svh); } }
@media (max-height: 620px) { .apg-portal { width: min(42vw, 66svh); } }
@media (min-width: 1600px) { .apg-portal { width: clamp(240px, min(46vw, 64svh), 860px); } }

/* 15b · "Scroll to enter" centred inside the ring.
   Generated on .apg-portal (aria-hidden); the original <p> stays in the DOM
   for screen readers. */

.apg-enter {
  position: absolute !important;
  width: 1px; height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.apg-portal::after {
  content: "Scroll to enter";
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  color: var(--apg-accent-pale);
  font-size: clamp(.6rem, 1.05vw, .82rem);
  letter-spacing: .24em;
  text-transform: uppercase;
  text-shadow: 0 2px 12px var(--apg-void), 0 0 26px var(--apg-void);
  animation: apg-blink 2.4s ease-in-out infinite;
  pointer-events: none;
}

/* 15c · Dust concentrated INSIDE the ring opening.
   Percentage-positioned so it scales with the portal at any size. */

.apg-portal::before {
  content: "";
  position: absolute;
  inset: 20%;
  border-radius: 50%;
  background-image: radial-gradient(circle 1.6px at 22% 34%, var(--apg-accent-pale) 0 1.6px, transparent 2.4px),radial-gradient(circle 1.6px at 68% 22%, var(--apg-accent-pale) 0 1.6px, transparent 2.4px),radial-gradient(circle 1.6px at 40% 62%, var(--apg-accent-pale) 0 1.6px, transparent 2.4px),radial-gradient(circle 1.6px at 76% 58%, var(--apg-accent-pale) 0 1.6px, transparent 2.4px),radial-gradient(circle 1.6px at 30% 76%, var(--apg-accent-pale) 0 1.6px, transparent 2.4px),radial-gradient(circle 1.6px at 58% 44%, var(--apg-accent-pale) 0 1.6px, transparent 2.4px),radial-gradient(circle 1.6px at 15% 55%, var(--apg-accent-pale) 0 1.6px, transparent 2.4px),radial-gradient(circle 1.6px at 85% 40%, var(--apg-accent-pale) 0 1.6px, transparent 2.4px),radial-gradient(circle 1.6px at 50% 18%, var(--apg-accent-pale) 0 1.6px, transparent 2.4px),radial-gradient(circle 1.6px at 62% 80%, var(--apg-accent-pale) 0 1.6px, transparent 2.4px),radial-gradient(circle 1.6px at 35% 45%, var(--apg-accent-pale) 0 1.6px, transparent 2.4px),radial-gradient(circle 1.6px at 72% 68%, var(--apg-accent-pale) 0 1.6px, transparent 2.4px);
  background-repeat: no-repeat;
  filter: drop-shadow(0 0 4px var(--apg-accent));
  animation: apg-core-drift 14s ease-in-out infinite alternate;
  will-change: transform, opacity;
}

@keyframes apg-core-drift {
  0%   { transform: translate3d(0,0,0)      scale(1);    opacity: .45; }
  50%  { transform: translate3d(2%,-4%,0)   scale(1.06); opacity: .9;  }
  100% { transform: translate3d(-2%,-8%,0)  scale(1.02); opacity: .55; }
}

/* 15d · Site-wide dust — two fixed layers, one behind content and one in
   front, so page elements read as floating inside the portal.
   On <body>, so every page gets it without markup.
   Deliberately sparse in front: readability wins over sparkle. */

body::before,
body::after {
  content: "";
  position: fixed;
  inset: 0;
  pointer-events: none;
  background-repeat: no-repeat;
}

body::before {
  z-index: 1;
  background-image: radial-gradient(circle 1.6px at 5% 18%, var(--apg-accent-pale) 0 1.6px, transparent 2.4px),radial-gradient(circle 1.6px at 14% 62%, var(--apg-accent-pale) 0 1.6px, transparent 2.4px),radial-gradient(circle 1.6px at 23% 88%, var(--apg-accent-pale) 0 1.6px, transparent 2.4px),radial-gradient(circle 1.6px at 31% 27%, var(--apg-accent-pale) 0 1.6px, transparent 2.4px),radial-gradient(circle 1.6px at 44% 71%, var(--apg-accent-pale) 0 1.6px, transparent 2.4px),radial-gradient(circle 1.6px at 52% 12%, var(--apg-accent-pale) 0 1.6px, transparent 2.4px),radial-gradient(circle 1.6px at 61% 49%, var(--apg-accent-pale) 0 1.6px, transparent 2.4px),radial-gradient(circle 1.6px at 69% 82%, var(--apg-accent-pale) 0 1.6px, transparent 2.4px),radial-gradient(circle 1.6px at 77% 33%, var(--apg-accent-pale) 0 1.6px, transparent 2.4px),radial-gradient(circle 1.6px at 86% 66%, var(--apg-accent-pale) 0 1.6px, transparent 2.4px),radial-gradient(circle 1.6px at 93% 21%, var(--apg-accent-pale) 0 1.6px, transparent 2.4px),radial-gradient(circle 1.6px at 9% 44%, var(--apg-accent-pale) 0 1.6px, transparent 2.4px),radial-gradient(circle 1.6px at 38% 95%, var(--apg-accent-pale) 0 1.6px, transparent 2.4px),radial-gradient(circle 1.6px at 58% 90%, var(--apg-accent-pale) 0 1.6px, transparent 2.4px);
  filter: drop-shadow(0 0 5px var(--apg-accent));
  opacity: .75;
  animation: apg-dust-rise 34s linear infinite;
}

body::after {
  z-index: 60;
  background-image: radial-gradient(circle 1.6px at 12% 40%, var(--apg-accent-pale) 0 1.6px, transparent 2.4px),radial-gradient(circle 1.6px at 29% 74%, var(--apg-accent-pale) 0 1.6px, transparent 2.4px),radial-gradient(circle 1.6px at 47% 25%, var(--apg-accent-pale) 0 1.6px, transparent 2.4px),radial-gradient(circle 1.6px at 66% 60%, var(--apg-accent-pale) 0 1.6px, transparent 2.4px),radial-gradient(circle 1.6px at 81% 15%, var(--apg-accent-pale) 0 1.6px, transparent 2.4px),radial-gradient(circle 1.6px at 90% 85%, var(--apg-accent-pale) 0 1.6px, transparent 2.4px);
  filter: drop-shadow(0 0 6px var(--apg-accent));
  opacity: .45;
  animation: apg-dust-rise 52s linear infinite reverse;
}

@keyframes apg-dust-rise {
  0%   { transform: translate3d(0, 4vh, 0); }
  100% { transform: translate3d(1.5vw, -8vh, 0); }
}

/* 15e · Scroll entry — the suction.
   Uses the standalone `scale` property, NOT transform, so it composes with
   the jelly wobble (transform) and the breathing glow (filter) instead of
   overriding them. Scroll-driven, so scrolling back up reverses it. */

@keyframes apg-suction {
  0%   { scale: 1;   opacity: 1; }
  55%  { scale: 1.6; opacity: .95; }
  100% { scale: 8;   opacity: 0; }
}

.apg-portal {
  animation:
    apg-jelly 7s ease-in-out infinite,
    apg-breathe 4.5s ease-in-out infinite,
    apg-suction linear both;
  animation-timeline: auto, auto, scroll(root block);
  animation-range: normal, normal, 0 100svh;
}

@keyframes apg-gate-recede {
  0%   { opacity: 1; translate: 0 0; }
  100% { opacity: 0; translate: 0 -6vh; }
}

.apg-gate-inner {
  animation: apg-gate-recede linear both;
  animation-timeline: scroll(root block);
  animation-range: 0 62svh;
}

/* 15f · Reduced motion + mobile */

@media (prefers-reduced-motion: reduce) {
  body::before, body::after,
  .apg-portal::before, .apg-portal::after { animation: none !important; }
  .apg-portal { animation: apg-suction linear both; animation-timeline: scroll(root block); animation-range: 0 100svh; }
}

@media (max-width: 767px) {
  body::after { opacity: .3; }
  body::before { opacity: .6; }
}


/* =========================================================================
   16 · GATE FIT + CUE CONTRAST
   The v2 portal is large enough that the subtitle was clipping on shorter
   viewports, and the cue was losing contrast against the lit ring.
   ========================================================================= */

.apg-gate {
  padding: 5vh 1.25rem 6vh;
  gap: clamp(.4rem, 1.6vh, 1.1rem);
  justify-content: center;
}

/* Leave headroom for the type block at every viewport height */
.apg-portal { width: clamp(230px, min(52vw, 50svh), 780px); }

@media (max-width: 767px)  { .apg-portal { width: min(82vw, 40svh); } }
@media (max-height: 620px) { .apg-portal { width: min(38vw, 58svh); } }
@media (min-width: 1600px) { .apg-portal { width: clamp(230px, min(44vw, 56svh), 780px); } }

/* Cue: read it against the ring's lit interior, not the void */
.apg-portal::after {
  color: #FFFFFF;
  font-weight: 600;
  text-shadow:
    0 0 3px  rgba(10,11,14,.95),
    0 1px 10px rgba(10,11,14,.9),
    0 0 24px rgba(10,11,14,.85),
    0 0 42px rgba(10,11,14,.7);
}

.apg-gate-inner h1 { font-size: clamp(1.35rem, 3.1vw, 2.3rem); }
.apg-gate-sub { font-size: clamp(.86rem, 1.4vw, 1.02rem); }


/* =========================================================================
   17 · GATE v3 — locked portal stage
   The gate is now a real stage, not a scroll section:
     · dark void + dust only (book background does NOT show yet)
     · logo floats inside the ring, filled with the theme gradient
     · "Scroll to enter" sits BELOW the portal in Cardo
     · first scroll in either direction fires the blow-out; page is locked
       until it finishes, so the portal never scrolls away mid-animation
     · scrolling up past the top of the site brings the portal back
   Driven by html classes set in page 4557's inline script:
   .apg-js .apg-locked .apg-entering .apg-entered .apg-exiting
   Cubic slide page transitions are UNTOUCHED.
   ========================================================================= */

/* Cardo is already shipped by WooCommerce — reuse it, no new request */
/* 8 Sep 2026 — REPOINTED IN PLACE, and the one deliberate exception to the
   append-only rule. A @font-face cannot be un-declared from a later block.
   This rule carries no unicode-range, so it covers U+0-10FFFF, and Chrome
   went on fetching the 143 KB WooCommerce file even with a later full-range
   face shadowing it — measured twice on staging. Editing the src is the only
   fix. Same typeface: a self-hosted latin subset at 15,516 B, verified
   against the WooCommerce file across 84 characters with zero advance-width
   mismatches, so nothing reflows. Block 81a adds the latin-ext subset.
   Reverting means putting the plugin path back here, not deleting 81a. */
@font-face {
  font-family: "APG Cardo";
  src: url("/wp-content/uploads/fonts/apg-cardo-400-latin.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
}

/* 17a · Scroll lock */
html.apg-locked, html.apg-locked body { overflow: hidden !important; height: 100%; }

/* 17b · The stage. Only becomes an overlay once JS is present, so without JS
   the page degrades to a normal scrolling document. */
.apg-gate {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: clamp(1rem, 4vh, 2.6rem);
  min-height: 100svh;
  padding: 4vh 1.25rem;
  background: radial-gradient(circle at 50% 46%,
    color-mix(in srgb, var(--apg-accent-bg) 55%, #000) 0%,
    #05060A 62%, #000 100%);
  overflow: hidden;
}

html.apg-js .apg-gate {
  position: fixed;
  inset: 0;
  z-index: 9000;
  transform: none;
  width: 100vw;
  margin: 0;
}

html.apg-entered .apg-gate { display: none; }

/* 17c · Void dust — the only thing in the void besides the portal */
.apg-voiddust {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background-image: radial-gradient(circle 1.6px at 6% 18%, var(--apg-accent-pale) 0 1.6px, transparent 2.4px),radial-gradient(circle 1.6px at 14% 62%, var(--apg-accent-pale) 0 1.6px, transparent 2.4px),radial-gradient(circle 1.6px at 23% 88%, var(--apg-accent-pale) 0 1.6px, transparent 2.4px),radial-gradient(circle 1.6px at 31% 27%, var(--apg-accent-pale) 0 1.6px, transparent 2.4px),radial-gradient(circle 1.6px at 44% 71%, var(--apg-accent-pale) 0 1.6px, transparent 2.4px),radial-gradient(circle 1.6px at 52% 12%, var(--apg-accent-pale) 0 1.6px, transparent 2.4px),radial-gradient(circle 1.6px at 61% 49%, var(--apg-accent-pale) 0 1.6px, transparent 2.4px),radial-gradient(circle 1.6px at 69% 82%, var(--apg-accent-pale) 0 1.6px, transparent 2.4px),radial-gradient(circle 1.6px at 77% 33%, var(--apg-accent-pale) 0 1.6px, transparent 2.4px),radial-gradient(circle 1.6px at 86% 66%, var(--apg-accent-pale) 0 1.6px, transparent 2.4px),radial-gradient(circle 1.6px at 93% 21%, var(--apg-accent-pale) 0 1.6px, transparent 2.4px),radial-gradient(circle 1.6px at 9% 44%, var(--apg-accent-pale) 0 1.6px, transparent 2.4px),radial-gradient(circle 1.6px at 38% 95%, var(--apg-accent-pale) 0 1.6px, transparent 2.4px),radial-gradient(circle 1.6px at 58% 90%, var(--apg-accent-pale) 0 1.6px, transparent 2.4px),radial-gradient(circle 1.6px at 20% 8%, var(--apg-accent-pale) 0 1.6px, transparent 2.4px),radial-gradient(circle 1.6px at 72% 7%, var(--apg-accent-pale) 0 1.6px, transparent 2.4px),radial-gradient(circle 1.6px at 48% 40%, var(--apg-accent-pale) 0 1.6px, transparent 2.4px),radial-gradient(circle 1.6px at 35% 58%, var(--apg-accent-pale) 0 1.6px, transparent 2.4px),radial-gradient(circle 1.6px at 83% 48%, var(--apg-accent-pale) 0 1.6px, transparent 2.4px),radial-gradient(circle 1.6px at 66% 30%, var(--apg-accent-pale) 0 1.6px, transparent 2.4px);
  background-repeat: no-repeat;
  filter: drop-shadow(0 0 5px var(--apg-accent));
  opacity: .85;
  animation: apg-dust-rise 30s linear infinite;
}

/* Book background stays hidden until the visitor is through the portal */
.apg-bg { opacity: 0; transition: opacity .9s ease .15s; }
html.apg-entered .apg-bg { opacity: 1; }
html.apg-js.apg-locked body::before,
html.apg-js.apg-locked body::after { opacity: 0; }

/* 17d · Portal + logo */
.apg-portal-wrap {
  position: relative;
  display: grid;
  place-items: center;
  flex: none;
}

.apg-portal {
  animation: apg-jelly 7s ease-in-out infinite, apg-breathe 4.5s ease-in-out infinite;
  animation-timeline: auto, auto;
  animation-range: normal, normal;
  scale: 1;
  width: clamp(260px, min(62vw, 66svh), 900px);
}
.apg-portal::after { content: none; }

@media (max-width: 767px)  { .apg-portal { width: min(88vw, 52svh); } }
@media (max-height: 620px) { .apg-portal { width: min(46vw, 74svh); } }

/* Logo masked with the portal art's own theme gradient — the one place the
   logo takes theme colour instead of its fixed magenta. */
.apg-portal-logo {
  position: absolute;
  width: 34%;
  aspect-ratio: 1;
  -webkit-mask-image: url("/wp-content/uploads/2024/07/Logo-website.png");
  mask-image: url("/wp-content/uploads/2024/07/Logo-website.png");
  -webkit-mask-size: contain; mask-size: contain;
  -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
  -webkit-mask-position: center; mask-position: center;
  background: linear-gradient(140deg,
    var(--apg-accent-pale) 0%,
    var(--apg-accent) 42%,
    var(--apg-accent-soft) 70%,
    var(--apg-accent-pale) 100%);
  background-size: 200% 200%;
  filter: drop-shadow(0 0 14px var(--apg-accent));
  animation: apg-logo-shimmer 9s ease-in-out infinite, apg-logo-float 7s ease-in-out infinite;
}

@keyframes apg-logo-shimmer {
  0%,100% { background-position: 0% 50%; }
  50%     { background-position: 100% 50%; }
}
@keyframes apg-logo-float {
  0%,100% { translate: 0 0; }
  50%     { translate: 0 -2%; }
}

/* 17e · "Scroll to enter" — below the portal, Cardo, theme coloured */
.apg-enter {
  position: static !important;
  width: auto; height: auto;
  overflow: visible;
  clip-path: none;
  margin: 0;
  font-family: "APG Cardo", Cardo, "Iowan Old Style", Georgia, serif;
  font-style: italic;
  font-size: clamp(1rem, 2.1vw, 1.45rem);
  letter-spacing: .14em;
  color: var(--apg-accent-pale);
  text-shadow: 0 0 14px var(--apg-accent), 0 2px 20px var(--apg-void);
  animation: apg-blink 2.6s ease-in-out infinite;
  pointer-events: none;
}

/* 17f · The blow-out and its reverse */
@keyframes apg-blowout {
  0%   { scale: 1;   opacity: 1; }
  22%  { scale: .9;  opacity: 1; }
  100% { scale: 10;  opacity: 0; }
}
@keyframes apg-blowin {
  0%   { scale: 10;  opacity: 0; }
  70%  { scale: 1.05; opacity: 1; }
  100% { scale: 1;   opacity: 1; }
}

html.apg-entering .apg-portal-wrap { animation: apg-blowout 1.15s cubic-bezier(.6,0,.3,1) both; }
html.apg-exiting  .apg-portal-wrap { animation: apg-blowin  .9s cubic-bezier(.4,0,.3,1) both; }

html.apg-entering .apg-enter,
html.apg-entering .apg-voiddust { animation: apg-fade-away .5s ease both; }
@keyframes apg-fade-away { to { opacity: 0; } }

html.apg-entering .apg-gate { animation: apg-gate-clear 1.15s ease both; }
@keyframes apg-gate-clear {
  0%,55% { opacity: 1; }
  100%   { opacity: 0; }
}

/* 17g · The hero, now the first thing inside the portal */
.apg-hero { text-align: center; padding-top: clamp(3.5rem, 12vh, 8rem); }
.apg-hero h1 {
  color: var(--apg-parchment);
  font-size: clamp(1.5rem, 3.4vw, 2.6rem);
  line-height: 1.15;
  margin: 0 0 .6rem;
  text-shadow: 0 2px 26px var(--apg-void);
}
.apg-hero .apg-gate-sub {
  color: rgba(244,247,250,.8);
  font-size: clamp(.9rem, 1.5vw, 1.08rem);
  max-width: 60ch;
  margin-inline: auto;
}

@media (prefers-reduced-motion: reduce) {
  .apg-voiddust, .apg-portal-logo, .apg-enter { animation: none !important; }
  html.apg-entering .apg-portal-wrap,
  html.apg-exiting  .apg-portal-wrap { animation-duration: .06s; }
}


/* =========================================================================
   18 · LOGO ALIGNMENT + GATE CENTRING
   Each portal PNG has its glowing ring off image-centre (~58% down, and the
   horizontal varies per artwork). Measured centroids below; the logo is
   pinned to the ring rather than to the image box.
   ========================================================================= */

:root                          { --apg-ring-x: 48.1%; --apg-ring-y: 57.7%; } /* b2 default */
body:has(#apg-t1:checked)      { --apg-ring-x: 52.8%; --apg-ring-y: 58.6%; }
body:has(#apg-t2:checked)      { --apg-ring-x: 48.1%; --apg-ring-y: 57.7%; }
body:has(#apg-t3:checked)      { --apg-ring-x: 51.6%; --apg-ring-y: 57.2%; }

.apg-portal-wrap { position: relative; }

.apg-portal-logo {
  position: absolute;
  left: var(--apg-ring-x);
  top:  var(--apg-ring-y);
  translate: -50% -50%;
  width: 26%;
  animation: apg-logo-shimmer 9s ease-in-out infinite;
}

/* During the blow-out the logo must ride the portal, not run its own
   animation — otherwise it just fades while the ring expands. */
html.apg-entering .apg-portal-logo,
html.apg-exiting  .apg-portal-logo { animation: none; }

/* Gate centring — the cue was inheriting left alignment */
.apg-gate { text-align: center; }
.apg-enter { width: 100%; text-align: center; }
.apg-gate-inner { text-align: center; }


/* 19 · CUE RESET — clear every leftover from the earlier in-ring version */
.apg-enter {
  position: static !important;
  inset: auto !important;
  width: auto !important;
  height: auto !important;
  max-width: 90vw;
  margin: 0 auto !important;
  padding: 0 !important;
  overflow: visible !important;
  clip-path: none !important;
  white-space: normal !important;
  display: block !important;
  text-align: center !important;
  align-self: center !important;
}


/* 20 · CUE PINNED — flex centring was being skewed by inherited layout;
   pin to the fixed gate instead so it is exactly under the portal. */
html.apg-js .apg-gate { position: fixed; }
html.apg-js .apg-enter {
  position: absolute !important;
  left: 50% !important;
  right: auto !important;
  bottom: 8vh !important;
  translate: -50% 0;
  margin: 0 !important;
  width: max-content !important;
  max-width: 90vw;
  text-align: center !important;
}


/* 21 · CUE CENTRING, FINAL — translate was resolving to -100%.
   Span the gate edge to edge and centre the text instead. */
html.apg-js .apg-enter {
  left: 0 !important;
  right: 0 !important;
  translate: none !important;
  transform: none !important;
  width: auto !important;
  max-width: none !important;
  text-align: center !important;
}


/* =========================================================================
   22 · LOGO SCALE · AMBIENT PORTAL LIGHT · GLOW DUST
   Replaces the drifting-particle dust with fading glow motes, sizes the
   logo to each portal's inner ring, and bleeds theme light into the void.
   ========================================================================= */

/* --- 22a · Ring geometry, measured per artwork.
       x/y are the ring centre and size is its INNER diameter, both as a
       percentage of the portal box (not the source image, which is letter-
       boxed by background-size: contain). --- */

:root                     { --apg-ring-x: 48.7%; --apg-ring-y: 58.2%; --apg-logo-size: 38.5%; }
body:has(#apg-t1:checked) { --apg-ring-x: 52.7%; --apg-ring-y: 58.6%; --apg-logo-size: 36.0%; }
body:has(#apg-t2:checked) { --apg-ring-x: 48.7%; --apg-ring-y: 58.2%; --apg-logo-size: 38.5%; }
body:has(#apg-t3:checked) { --apg-ring-x: 51.4%; --apg-ring-y: 57.8%; --apg-logo-size: 42.2%; }

.apg-portal-logo {
  width: var(--apg-logo-size);
  filter: drop-shadow(0 0 18px var(--apg-accent))
          drop-shadow(0 0 42px color-mix(in srgb, var(--apg-accent) 55%, transparent));
}

/* --- 22b · Ambient light. The void is no longer flat black: theme colour
       pools around the portal and falls off to true black at the edges. --- */

.apg-gate {
  background:
    radial-gradient(circle at 50% 46%,
      color-mix(in srgb, var(--apg-accent) 20%, transparent) 0%,
      color-mix(in srgb, var(--apg-accent) 11%, transparent) 16%,
      color-mix(in srgb, var(--apg-accent-bg) 55%, transparent) 34%,
      color-mix(in srgb, var(--apg-accent-bg) 22%, transparent) 52%,
      #05060A 74%,
      #000 100%),
    #000;
}

/* Same pooled light behind the rest of the site, under the book background */
html.apg-entered body {
  background:
    radial-gradient(circle at 50% 0%,
      color-mix(in srgb, var(--apg-accent) 9%, transparent) 0%,
      transparent 55%),
    var(--apg-void);
}

/* --- 22c · Glow motes. Bigger, softer, and they breathe in and out on the
       spot rather than drifting up and vanishing. Three layers on separate
       cycles so they twinkle independently. --- */

@keyframes apg-mote-breathe {
  0%, 100% { opacity: 0;   }
  18%      { opacity: .85; }
  46%      { opacity: .35; }
  70%      { opacity: .95; }
}

.apg-voiddust,
.apg-voiddust::before,
.apg-voiddust::after {
  position: absolute;
  inset: 0;
  content: "";
  pointer-events: none;
  background-repeat: no-repeat;
  filter: blur(.4px) drop-shadow(0 0 6px var(--apg-accent));
  will-change: opacity;
}

.apg-voiddust {
  background-image:
    radial-gradient(circle 9.9px at 45.4% 55.7%, var(--apg-accent-pale) 0 3.3px, color-mix(in srgb, var(--apg-accent) 55%, transparent) 5.6px, transparent 9.9px),
    radial-gradient(circle 8.1px at 46.7% 50.8%, var(--apg-accent-pale) 0 2.7px, color-mix(in srgb, var(--apg-accent) 55%, transparent) 4.6px, transparent 8.1px),
    radial-gradient(circle 8.4px at 19.7% 51.1%, var(--apg-accent-pale) 0 2.8px, color-mix(in srgb, var(--apg-accent) 55%, transparent) 4.8px, transparent 8.4px),
    radial-gradient(circle 6.9px at 78.1% 11.0%, var(--apg-accent-pale) 0 2.3px, color-mix(in srgb, var(--apg-accent) 55%, transparent) 3.9px, transparent 6.9px),
    radial-gradient(circle 8.7px at 10.7% 79.7%, var(--apg-accent-pale) 0 2.9px, color-mix(in srgb, var(--apg-accent) 55%, transparent) 4.9px, transparent 8.7px),
    radial-gradient(circle 9.9px at 6.0% 96.3%, var(--apg-accent-pale) 0 3.3px, color-mix(in srgb, var(--apg-accent) 55%, transparent) 5.6px, transparent 9.9px),
    radial-gradient(circle 6.3px at 64.8% 61.1%, var(--apg-accent-pale) 0 2.1px, color-mix(in srgb, var(--apg-accent) 55%, transparent) 3.6px, transparent 6.3px),
    radial-gradient(circle 5.7px at 3.4% 52.7%, var(--apg-accent-pale) 0 1.9px, color-mix(in srgb, var(--apg-accent) 55%, transparent) 3.2px, transparent 5.7px),
    radial-gradient(circle 5.4px at 20.3% 25.2%, var(--apg-accent-pale) 0 1.8px, color-mix(in srgb, var(--apg-accent) 55%, transparent) 3.1px, transparent 5.4px),
    radial-gradient(circle 9.3px at 46.5% 44.3%, var(--apg-accent-pale) 0 3.1px, color-mix(in srgb, var(--apg-accent) 55%, transparent) 5.3px, transparent 9.3px),
    radial-gradient(circle 7.8px at 51.8% 63.5%, var(--apg-accent-pale) 0 2.6px, color-mix(in srgb, var(--apg-accent) 55%, transparent) 4.4px, transparent 7.8px),
    radial-gradient(circle 6.6px at 65.6% 45.9%, var(--apg-accent-pale) 0 2.2px, color-mix(in srgb, var(--apg-accent) 55%, transparent) 3.7px, transparent 6.6px),
    radial-gradient(circle 9.3px at 97.8% 97.6%, var(--apg-accent-pale) 0 3.1px, color-mix(in srgb, var(--apg-accent) 55%, transparent) 5.3px, transparent 9.3px),
    radial-gradient(circle 6.6px at 69.9% 32.3%, var(--apg-accent-pale) 0 2.2px, color-mix(in srgb, var(--apg-accent) 55%, transparent) 3.7px, transparent 6.6px),
    radial-gradient(circle 9.0px at 29.7% 8.7%, var(--apg-accent-pale) 0 3.0px, color-mix(in srgb, var(--apg-accent) 55%, transparent) 5.1px, transparent 9.0px),
    radial-gradient(circle 7.2px at 40.4% 83.3%, var(--apg-accent-pale) 0 2.4px, color-mix(in srgb, var(--apg-accent) 55%, transparent) 4.1px, transparent 7.2px),
    radial-gradient(circle 5.4px at 94.0% 83.3%, var(--apg-accent-pale) 0 1.8px, color-mix(in srgb, var(--apg-accent) 55%, transparent) 3.1px, transparent 5.4px),
    radial-gradient(circle 7.8px at 22.1% 89.4%, var(--apg-accent-pale) 0 2.6px, color-mix(in srgb, var(--apg-accent) 55%, transparent) 4.4px, transparent 7.8px),
    radial-gradient(circle 5.7px at 96.1% 40.2%, var(--apg-accent-pale) 0 1.9px, color-mix(in srgb, var(--apg-accent) 55%, transparent) 3.2px, transparent 5.7px),
    radial-gradient(circle 6.6px at 62.4% 76.7%, var(--apg-accent-pale) 0 2.2px, color-mix(in srgb, var(--apg-accent) 55%, transparent) 3.7px, transparent 6.6px),
    radial-gradient(circle 9.9px at 10.4% 33.9%, var(--apg-accent-pale) 0 3.3px, color-mix(in srgb, var(--apg-accent) 55%, transparent) 5.6px, transparent 9.9px),
    radial-gradient(circle 6.6px at 74.8% 13.3%, var(--apg-accent-pale) 0 2.2px, color-mix(in srgb, var(--apg-accent) 55%, transparent) 3.7px, transparent 6.6px);
  animation: apg-mote-breathe 7s ease-in-out infinite;
}
.apg-voiddust::before {
  background-image:
    radial-gradient(circle 5.4px at 94.0% 15.5%, var(--apg-accent-pale) 0 1.8px, color-mix(in srgb, var(--apg-accent) 55%, transparent) 3.1px, transparent 5.4px),
    radial-gradient(circle 6.0px at 97.9% 19.7%, var(--apg-accent-pale) 0 2.0px, color-mix(in srgb, var(--apg-accent) 55%, transparent) 3.4px, transparent 6.0px),
    radial-gradient(circle 9.6px at 64.5% 35.2%, var(--apg-accent-pale) 0 3.2px, color-mix(in srgb, var(--apg-accent) 55%, transparent) 5.4px, transparent 9.6px),
    radial-gradient(circle 6.9px at 24.2% 94.1%, var(--apg-accent-pale) 0 2.3px, color-mix(in srgb, var(--apg-accent) 55%, transparent) 3.9px, transparent 6.9px),
    radial-gradient(circle 8.7px at 59.7% 91.5%, var(--apg-accent-pale) 0 2.9px, color-mix(in srgb, var(--apg-accent) 55%, transparent) 4.9px, transparent 8.7px),
    radial-gradient(circle 5.7px at 90.7% 70.0%, var(--apg-accent-pale) 0 1.9px, color-mix(in srgb, var(--apg-accent) 55%, transparent) 3.2px, transparent 5.7px),
    radial-gradient(circle 6.9px at 86.6% 58.6%, var(--apg-accent-pale) 0 2.3px, color-mix(in srgb, var(--apg-accent) 55%, transparent) 3.9px, transparent 6.9px),
    radial-gradient(circle 8.1px at 20.3% 83.6%, var(--apg-accent-pale) 0 2.7px, color-mix(in srgb, var(--apg-accent) 55%, transparent) 4.6px, transparent 8.1px),
    radial-gradient(circle 9.9px at 92.0% 76.3%, var(--apg-accent-pale) 0 3.3px, color-mix(in srgb, var(--apg-accent) 55%, transparent) 5.6px, transparent 9.9px),
    radial-gradient(circle 8.4px at 56.4% 69.0%, var(--apg-accent-pale) 0 2.8px, color-mix(in srgb, var(--apg-accent) 55%, transparent) 4.8px, transparent 8.4px),
    radial-gradient(circle 6.9px at 26.8% 93.8%, var(--apg-accent-pale) 0 2.3px, color-mix(in srgb, var(--apg-accent) 55%, transparent) 3.9px, transparent 6.9px),
    radial-gradient(circle 8.4px at 77.0% 93.6%, var(--apg-accent-pale) 0 2.8px, color-mix(in srgb, var(--apg-accent) 55%, transparent) 4.8px, transparent 8.4px),
    radial-gradient(circle 8.7px at 15.7% 81.2%, var(--apg-accent-pale) 0 2.9px, color-mix(in srgb, var(--apg-accent) 55%, transparent) 4.9px, transparent 8.7px),
    radial-gradient(circle 8.1px at 19.8% 6.7%, var(--apg-accent-pale) 0 2.7px, color-mix(in srgb, var(--apg-accent) 55%, transparent) 4.6px, transparent 8.1px),
    radial-gradient(circle 7.8px at 5.1% 90.8%, var(--apg-accent-pale) 0 2.6px, color-mix(in srgb, var(--apg-accent) 55%, transparent) 4.4px, transparent 7.8px),
    radial-gradient(circle 8.7px at 51.7% 41.3%, var(--apg-accent-pale) 0 2.9px, color-mix(in srgb, var(--apg-accent) 55%, transparent) 4.9px, transparent 8.7px),
    radial-gradient(circle 7.2px at 90.0% 31.5%, var(--apg-accent-pale) 0 2.4px, color-mix(in srgb, var(--apg-accent) 55%, transparent) 4.1px, transparent 7.2px),
    radial-gradient(circle 8.4px at 57.9% 30.5%, var(--apg-accent-pale) 0 2.8px, color-mix(in srgb, var(--apg-accent) 55%, transparent) 4.8px, transparent 8.4px),
    radial-gradient(circle 8.7px at 54.1% 43.6%, var(--apg-accent-pale) 0 2.9px, color-mix(in srgb, var(--apg-accent) 55%, transparent) 4.9px, transparent 8.7px),
    radial-gradient(circle 10.2px at 52.7% 10.5%, var(--apg-accent-pale) 0 3.4px, color-mix(in srgb, var(--apg-accent) 55%, transparent) 5.8px, transparent 10.2px);
  animation: apg-mote-breathe 9.5s ease-in-out infinite -3.2s;
}
.apg-voiddust::after {
  background-image:
    radial-gradient(circle 9.3px at 56.8% 62.7%, var(--apg-accent-pale) 0 3.1px, color-mix(in srgb, var(--apg-accent) 55%, transparent) 5.3px, transparent 9.3px),
    radial-gradient(circle 9.6px at 28.6% 64.6%, var(--apg-accent-pale) 0 3.2px, color-mix(in srgb, var(--apg-accent) 55%, transparent) 5.4px, transparent 9.6px),
    radial-gradient(circle 8.4px at 89.2% 19.9%, var(--apg-accent-pale) 0 2.8px, color-mix(in srgb, var(--apg-accent) 55%, transparent) 4.8px, transparent 8.4px),
    radial-gradient(circle 9.9px at 61.2% 50.3%, var(--apg-accent-pale) 0 3.3px, color-mix(in srgb, var(--apg-accent) 55%, transparent) 5.6px, transparent 9.9px),
    radial-gradient(circle 9.9px at 52.7% 44.7%, var(--apg-accent-pale) 0 3.3px, color-mix(in srgb, var(--apg-accent) 55%, transparent) 5.6px, transparent 9.9px),
    radial-gradient(circle 6.9px at 63.0% 30.9%, var(--apg-accent-pale) 0 2.3px, color-mix(in srgb, var(--apg-accent) 55%, transparent) 3.9px, transparent 6.9px),
    radial-gradient(circle 8.4px at 49.6% 7.0%, var(--apg-accent-pale) 0 2.8px, color-mix(in srgb, var(--apg-accent) 55%, transparent) 4.8px, transparent 8.4px),
    radial-gradient(circle 9.6px at 72.6% 42.9%, var(--apg-accent-pale) 0 3.2px, color-mix(in srgb, var(--apg-accent) 55%, transparent) 5.4px, transparent 9.6px),
    radial-gradient(circle 8.7px at 91.1% 55.3%, var(--apg-accent-pale) 0 2.9px, color-mix(in srgb, var(--apg-accent) 55%, transparent) 4.9px, transparent 8.7px),
    radial-gradient(circle 10.2px at 13.3% 7.4%, var(--apg-accent-pale) 0 3.4px, color-mix(in srgb, var(--apg-accent) 55%, transparent) 5.8px, transparent 10.2px),
    radial-gradient(circle 9.6px at 29.3% 22.5%, var(--apg-accent-pale) 0 3.2px, color-mix(in srgb, var(--apg-accent) 55%, transparent) 5.4px, transparent 9.6px),
    radial-gradient(circle 6.9px at 74.0% 13.8%, var(--apg-accent-pale) 0 2.3px, color-mix(in srgb, var(--apg-accent) 55%, transparent) 3.9px, transparent 6.9px),
    radial-gradient(circle 5.4px at 25.3% 28.2%, var(--apg-accent-pale) 0 1.8px, color-mix(in srgb, var(--apg-accent) 55%, transparent) 3.1px, transparent 5.4px),
    radial-gradient(circle 7.2px at 52.4% 31.2%, var(--apg-accent-pale) 0 2.4px, color-mix(in srgb, var(--apg-accent) 55%, transparent) 4.1px, transparent 7.2px),
    radial-gradient(circle 5.7px at 19.8% 12.6%, var(--apg-accent-pale) 0 1.9px, color-mix(in srgb, var(--apg-accent) 55%, transparent) 3.2px, transparent 5.7px),
    radial-gradient(circle 8.1px at 85.8% 73.7%, var(--apg-accent-pale) 0 2.7px, color-mix(in srgb, var(--apg-accent) 55%, transparent) 4.6px, transparent 8.1px),
    radial-gradient(circle 6.0px at 13.9% 16.3%, var(--apg-accent-pale) 0 2.0px, color-mix(in srgb, var(--apg-accent) 55%, transparent) 3.4px, transparent 6.0px),
    radial-gradient(circle 6.6px at 32.4% 7.5%, var(--apg-accent-pale) 0 2.2px, color-mix(in srgb, var(--apg-accent) 55%, transparent) 3.7px, transparent 6.6px);
  animation: apg-mote-breathe 12s ease-in-out infinite -6.7s;
}

/* Site-wide motes, behind and in front of content */
body::before, body::after {
  background-repeat: no-repeat;
  filter: blur(.4px) drop-shadow(0 0 7px var(--apg-accent));
}
body::before {
  background-image:
    radial-gradient(circle 9.6px at 41.2% 54.0%, var(--apg-accent-pale) 0 3.2px, color-mix(in srgb, var(--apg-accent) 55%, transparent) 5.4px, transparent 9.6px),
    radial-gradient(circle 5.4px at 19.0% 23.6%, var(--apg-accent-pale) 0 1.8px, color-mix(in srgb, var(--apg-accent) 55%, transparent) 3.1px, transparent 5.4px),
    radial-gradient(circle 6.3px at 13.2% 2.8%, var(--apg-accent-pale) 0 2.1px, color-mix(in srgb, var(--apg-accent) 55%, transparent) 3.6px, transparent 6.3px),
    radial-gradient(circle 8.7px at 51.3% 97.0%, var(--apg-accent-pale) 0 2.9px, color-mix(in srgb, var(--apg-accent) 55%, transparent) 4.9px, transparent 8.7px),
    radial-gradient(circle 8.7px at 38.3% 40.9%, var(--apg-accent-pale) 0 2.9px, color-mix(in srgb, var(--apg-accent) 55%, transparent) 4.9px, transparent 8.7px),
    radial-gradient(circle 9.3px at 33.3% 9.1%, var(--apg-accent-pale) 0 3.1px, color-mix(in srgb, var(--apg-accent) 55%, transparent) 5.3px, transparent 9.3px),
    radial-gradient(circle 5.7px at 61.3% 12.8%, var(--apg-accent-pale) 0 1.9px, color-mix(in srgb, var(--apg-accent) 55%, transparent) 3.2px, transparent 5.7px),
    radial-gradient(circle 6.0px at 11.1% 94.1%, var(--apg-accent-pale) 0 2.0px, color-mix(in srgb, var(--apg-accent) 55%, transparent) 3.4px, transparent 6.0px),
    radial-gradient(circle 5.7px at 62.0% 95.1%, var(--apg-accent-pale) 0 1.9px, color-mix(in srgb, var(--apg-accent) 55%, transparent) 3.2px, transparent 5.7px),
    radial-gradient(circle 9.9px at 39.0% 47.8%, var(--apg-accent-pale) 0 3.3px, color-mix(in srgb, var(--apg-accent) 55%, transparent) 5.6px, transparent 9.9px),
    radial-gradient(circle 6.6px at 20.1% 26.8%, var(--apg-accent-pale) 0 2.2px, color-mix(in srgb, var(--apg-accent) 55%, transparent) 3.7px, transparent 6.6px),
    radial-gradient(circle 9.3px at 6.7% 66.7%, var(--apg-accent-pale) 0 3.1px, color-mix(in srgb, var(--apg-accent) 55%, transparent) 5.3px, transparent 9.3px),
    radial-gradient(circle 8.7px at 43.9% 5.6%, var(--apg-accent-pale) 0 2.9px, color-mix(in srgb, var(--apg-accent) 55%, transparent) 4.9px, transparent 8.7px),
    radial-gradient(circle 6.9px at 72.8% 64.7%, var(--apg-accent-pale) 0 2.3px, color-mix(in srgb, var(--apg-accent) 55%, transparent) 3.9px, transparent 6.9px),
    radial-gradient(circle 8.1px at 81.7% 62.5%, var(--apg-accent-pale) 0 2.7px, color-mix(in srgb, var(--apg-accent) 55%, transparent) 4.6px, transparent 8.1px),
    radial-gradient(circle 7.5px at 53.9% 24.4%, var(--apg-accent-pale) 0 2.5px, color-mix(in srgb, var(--apg-accent) 55%, transparent) 4.2px, transparent 7.5px),
    radial-gradient(circle 7.2px at 78.2% 67.6%, var(--apg-accent-pale) 0 2.4px, color-mix(in srgb, var(--apg-accent) 55%, transparent) 4.1px, transparent 7.2px),
    radial-gradient(circle 5.7px at 80.7% 82.6%, var(--apg-accent-pale) 0 1.9px, color-mix(in srgb, var(--apg-accent) 55%, transparent) 3.2px, transparent 5.7px),
    radial-gradient(circle 5.4px at 17.1% 23.4%, var(--apg-accent-pale) 0 1.8px, color-mix(in srgb, var(--apg-accent) 55%, transparent) 3.1px, transparent 5.4px),
    radial-gradient(circle 8.1px at 74.3% 64.1%, var(--apg-accent-pale) 0 2.7px, color-mix(in srgb, var(--apg-accent) 55%, transparent) 4.6px, transparent 8.1px);
  opacity: 1;
  animation: apg-mote-breathe 10s ease-in-out infinite;
}
body::after {
  background-image:
    radial-gradient(circle 4.8px at 10.7% 93.3%, var(--apg-accent-pale) 0 1.6px, color-mix(in srgb, var(--apg-accent) 55%, transparent) 2.7px, transparent 4.8px),
    radial-gradient(circle 6.9px at 73.0% 82.8%, var(--apg-accent-pale) 0 2.3px, color-mix(in srgb, var(--apg-accent) 55%, transparent) 3.9px, transparent 6.9px),
    radial-gradient(circle 6.9px at 31.0% 35.8%, var(--apg-accent-pale) 0 2.3px, color-mix(in srgb, var(--apg-accent) 55%, transparent) 3.9px, transparent 6.9px),
    radial-gradient(circle 6.0px at 94.7% 65.6%, var(--apg-accent-pale) 0 2.0px, color-mix(in srgb, var(--apg-accent) 55%, transparent) 3.4px, transparent 6.0px),
    radial-gradient(circle 6.9px at 39.2% 41.0%, var(--apg-accent-pale) 0 2.3px, color-mix(in srgb, var(--apg-accent) 55%, transparent) 3.9px, transparent 6.9px),
    radial-gradient(circle 6.6px at 43.2% 53.3%, var(--apg-accent-pale) 0 2.2px, color-mix(in srgb, var(--apg-accent) 55%, transparent) 3.7px, transparent 6.6px),
    radial-gradient(circle 5.1px at 62.7% 5.9%, var(--apg-accent-pale) 0 1.7px, color-mix(in srgb, var(--apg-accent) 55%, transparent) 2.9px, transparent 5.1px),
    radial-gradient(circle 7.2px at 69.8% 56.8%, var(--apg-accent-pale) 0 2.4px, color-mix(in srgb, var(--apg-accent) 55%, transparent) 4.1px, transparent 7.2px),
    radial-gradient(circle 6.0px at 7.0% 24.2%, var(--apg-accent-pale) 0 2.0px, color-mix(in srgb, var(--apg-accent) 55%, transparent) 3.4px, transparent 6.0px),
    radial-gradient(circle 6.9px at 73.7% 20.7%, var(--apg-accent-pale) 0 2.3px, color-mix(in srgb, var(--apg-accent) 55%, transparent) 3.9px, transparent 6.9px);
  opacity: 1;
  animation: apg-mote-breathe 14s ease-in-out infinite -5s;
}

@media (max-width: 767px) {
  .apg-voiddust::after { display: none; }
  body::after { display: none; }
}

@media (prefers-reduced-motion: reduce) {
  .apg-voiddust, .apg-voiddust::before, .apg-voiddust::after,
  body::before, body::after { animation: none !important; opacity: .5 !important; }
}

/* =========================================================================
   23 · MOTE CORRECTION + LOGO HOLD
   Reverts the oversized particles from block 22 back to fine dust with a
   mild glow, gives each layer its own wandering path, and stops the logo
   scaling with the portal during the blow-out.
   ========================================================================= */

/* --- 23a · Fine motes again. Small, gently glowing, fading in and out. --- */

.apg-voiddust,
.apg-voiddust::before,
.apg-voiddust::after {
  filter: drop-shadow(0 0 3px var(--apg-accent));
}

.apg-voiddust        { background-image: radial-gradient(circle 2.74px at 88.4% 24.5%, var(--apg-accent-pale) 0 1.44px, transparent 2.74px),radial-gradient(circle 2.43px at 4.0% 57.7%, var(--apg-accent-pale) 0 1.28px, transparent 2.43px),radial-gradient(circle 2.62px at 89.7% 13.0%, var(--apg-accent-pale) 0 1.38px, transparent 2.62px),radial-gradient(circle 3.25px at 49.8% 62.1%, var(--apg-accent-pale) 0 1.71px, transparent 3.25px),radial-gradient(circle 2.39px at 67.6% 66.8%, var(--apg-accent-pale) 0 1.26px, transparent 2.39px),radial-gradient(circle 2.17px at 4.4% 40.2%, var(--apg-accent-pale) 0 1.14px, transparent 2.17px),radial-gradient(circle 3.25px at 91.8% 54.0%, var(--apg-accent-pale) 0 1.71px, transparent 3.25px),radial-gradient(circle 2.43px at 94.1% 41.7%, var(--apg-accent-pale) 0 1.28px, transparent 2.43px),radial-gradient(circle 2.17px at 65.0% 62.0%, var(--apg-accent-pale) 0 1.14px, transparent 2.17px),radial-gradient(circle 3.12px at 26.0% 58.9%, var(--apg-accent-pale) 0 1.64px, transparent 3.12px),radial-gradient(circle 3.25px at 56.2% 34.5%, var(--apg-accent-pale) 0 1.71px, transparent 3.25px),radial-gradient(circle 2.85px at 49.2% 43.5%, var(--apg-accent-pale) 0 1.50px, transparent 2.85px),radial-gradient(circle 3.42px at 86.0% 82.2%, var(--apg-accent-pale) 0 1.80px, transparent 3.42px),radial-gradient(circle 2.77px at 2.9% 30.8%, var(--apg-accent-pale) 0 1.46px, transparent 2.77px),radial-gradient(circle 2.09px at 17.5% 15.8%, var(--apg-accent-pale) 0 1.10px, transparent 2.09px),radial-gradient(circle 2.87px at 79.9% 63.9%, var(--apg-accent-pale) 0 1.51px, transparent 2.87px),radial-gradient(circle 2.56px at 81.5% 46.1%, var(--apg-accent-pale) 0 1.35px, transparent 2.56px),radial-gradient(circle 2.58px at 69.2% 11.9%, var(--apg-accent-pale) 0 1.36px, transparent 2.58px),radial-gradient(circle 2.41px at 8.9% 12.1%, var(--apg-accent-pale) 0 1.27px, transparent 2.41px),radial-gradient(circle 2.30px at 36.7% 21.4%, var(--apg-accent-pale) 0 1.21px, transparent 2.30px),radial-gradient(circle 2.74px at 14.6% 21.0%, var(--apg-accent-pale) 0 1.44px, transparent 2.74px),radial-gradient(circle 3.02px at 66.9% 50.1%, var(--apg-accent-pale) 0 1.59px, transparent 3.02px),radial-gradient(circle 2.64px at 88.7% 56.2%, var(--apg-accent-pale) 0 1.39px, transparent 2.64px),radial-gradient(circle 2.51px at 63.8% 50.1%, var(--apg-accent-pale) 0 1.32px, transparent 2.51px),radial-gradient(circle 2.30px at 52.9% 77.9%, var(--apg-accent-pale) 0 1.21px, transparent 2.30px),radial-gradient(circle 2.38px at 26.0% 68.3%, var(--apg-accent-pale) 0 1.25px, transparent 2.38px); }
.apg-voiddust::before{ background-image: radial-gradient(circle 3.23px at 78.9% 80.1%, var(--apg-accent-pale) 0 1.70px, transparent 3.23px),radial-gradient(circle 3.32px at 21.9% 65.3%, var(--apg-accent-pale) 0 1.75px, transparent 3.32px),radial-gradient(circle 2.36px at 67.7% 79.6%, var(--apg-accent-pale) 0 1.24px, transparent 2.36px),radial-gradient(circle 2.39px at 91.3% 61.7%, var(--apg-accent-pale) 0 1.26px, transparent 2.39px),radial-gradient(circle 2.85px at 36.4% 38.6%, var(--apg-accent-pale) 0 1.50px, transparent 2.85px),radial-gradient(circle 2.22px at 85.3% 5.0%, var(--apg-accent-pale) 0 1.17px, transparent 2.22px),radial-gradient(circle 2.93px at 86.8% 3.8%, var(--apg-accent-pale) 0 1.54px, transparent 2.93px),radial-gradient(circle 3.36px at 67.0% 28.7%, var(--apg-accent-pale) 0 1.77px, transparent 3.36px),radial-gradient(circle 2.58px at 13.9% 18.3%, var(--apg-accent-pale) 0 1.36px, transparent 2.58px),radial-gradient(circle 2.28px at 48.8% 29.6%, var(--apg-accent-pale) 0 1.20px, transparent 2.28px),radial-gradient(circle 2.20px at 85.4% 29.2%, var(--apg-accent-pale) 0 1.16px, transparent 2.20px),radial-gradient(circle 2.24px at 35.8% 5.2%, var(--apg-accent-pale) 0 1.18px, transparent 2.24px),radial-gradient(circle 2.45px at 39.5% 94.2%, var(--apg-accent-pale) 0 1.29px, transparent 2.45px),radial-gradient(circle 2.30px at 20.1% 69.0%, var(--apg-accent-pale) 0 1.21px, transparent 2.30px),radial-gradient(circle 3.23px at 13.1% 68.3%, var(--apg-accent-pale) 0 1.70px, transparent 3.23px),radial-gradient(circle 3.04px at 8.0% 4.7%, var(--apg-accent-pale) 0 1.60px, transparent 3.04px),radial-gradient(circle 2.22px at 4.2% 51.5%, var(--apg-accent-pale) 0 1.17px, transparent 2.22px),radial-gradient(circle 2.39px at 66.3% 78.7%, var(--apg-accent-pale) 0 1.26px, transparent 2.39px),radial-gradient(circle 2.74px at 21.1% 65.0%, var(--apg-accent-pale) 0 1.44px, transparent 2.74px),radial-gradient(circle 2.81px at 29.2% 48.4%, var(--apg-accent-pale) 0 1.48px, transparent 2.81px),radial-gradient(circle 2.55px at 28.9% 9.1%, var(--apg-accent-pale) 0 1.34px, transparent 2.55px),radial-gradient(circle 2.74px at 53.6% 77.1%, var(--apg-accent-pale) 0 1.44px, transparent 2.74px),radial-gradient(circle 2.94px at 43.7% 59.2%, var(--apg-accent-pale) 0 1.55px, transparent 2.94px),radial-gradient(circle 3.08px at 6.4% 12.2%, var(--apg-accent-pale) 0 1.62px, transparent 3.08px); }
.apg-voiddust::after { background-image: radial-gradient(circle 3.13px at 69.3% 39.7%, var(--apg-accent-pale) 0 1.65px, transparent 3.13px),radial-gradient(circle 2.83px at 54.8% 16.4%, var(--apg-accent-pale) 0 1.49px, transparent 2.83px),radial-gradient(circle 2.22px at 22.8% 63.4%, var(--apg-accent-pale) 0 1.17px, transparent 2.22px),radial-gradient(circle 2.11px at 71.1% 27.6%, var(--apg-accent-pale) 0 1.11px, transparent 2.11px),radial-gradient(circle 3.04px at 39.3% 66.6%, var(--apg-accent-pale) 0 1.60px, transparent 3.04px),radial-gradient(circle 2.74px at 65.0% 33.5%, var(--apg-accent-pale) 0 1.44px, transparent 2.74px),radial-gradient(circle 3.10px at 77.8% 20.4%, var(--apg-accent-pale) 0 1.63px, transparent 3.10px),radial-gradient(circle 2.96px at 8.8% 77.4%, var(--apg-accent-pale) 0 1.56px, transparent 2.96px),radial-gradient(circle 2.20px at 97.3% 44.2%, var(--apg-accent-pale) 0 1.16px, transparent 2.20px),radial-gradient(circle 3.02px at 15.5% 65.1%, var(--apg-accent-pale) 0 1.59px, transparent 3.02px),radial-gradient(circle 2.55px at 79.1% 87.2%, var(--apg-accent-pale) 0 1.34px, transparent 2.55px),radial-gradient(circle 2.60px at 30.4% 8.2%, var(--apg-accent-pale) 0 1.37px, transparent 2.60px),radial-gradient(circle 3.06px at 24.9% 75.6%, var(--apg-accent-pale) 0 1.61px, transparent 3.06px),radial-gradient(circle 2.89px at 95.3% 41.8%, var(--apg-accent-pale) 0 1.52px, transparent 2.89px),radial-gradient(circle 2.39px at 58.7% 63.1%, var(--apg-accent-pale) 0 1.26px, transparent 2.39px),radial-gradient(circle 2.09px at 89.9% 44.7%, var(--apg-accent-pale) 0 1.10px, transparent 2.09px),radial-gradient(circle 3.15px at 90.1% 27.0%, var(--apg-accent-pale) 0 1.66px, transparent 3.15px),radial-gradient(circle 2.81px at 12.7% 21.5%, var(--apg-accent-pale) 0 1.48px, transparent 2.81px),radial-gradient(circle 2.30px at 16.0% 63.4%, var(--apg-accent-pale) 0 1.21px, transparent 2.30px),radial-gradient(circle 3.02px at 51.3% 43.6%, var(--apg-accent-pale) 0 1.59px, transparent 3.02px),radial-gradient(circle 2.75px at 62.5% 78.5%, var(--apg-accent-pale) 0 1.45px, transparent 2.75px),radial-gradient(circle 2.70px at 40.1% 49.5%, var(--apg-accent-pale) 0 1.42px, transparent 2.70px); }

body::before, body::after { filter: drop-shadow(0 0 3px var(--apg-accent)); }
body::before { background-image: radial-gradient(circle 2.30px at 59.7% 95.4%, var(--apg-accent-pale) 0 1.21px, transparent 2.30px),radial-gradient(circle 2.72px at 61.8% 54.4%, var(--apg-accent-pale) 0 1.43px, transparent 2.72px),radial-gradient(circle 2.62px at 9.0% 58.0%, var(--apg-accent-pale) 0 1.38px, transparent 2.62px),radial-gradient(circle 2.30px at 72.1% 33.4%, var(--apg-accent-pale) 0 1.21px, transparent 2.30px),radial-gradient(circle 2.30px at 5.6% 85.4%, var(--apg-accent-pale) 0 1.21px, transparent 2.30px),radial-gradient(circle 3.04px at 60.8% 38.7%, var(--apg-accent-pale) 0 1.60px, transparent 3.04px),radial-gradient(circle 2.58px at 57.8% 76.2%, var(--apg-accent-pale) 0 1.36px, transparent 2.58px),radial-gradient(circle 2.39px at 96.5% 97.6%, var(--apg-accent-pale) 0 1.26px, transparent 2.39px),radial-gradient(circle 2.17px at 13.9% 59.8%, var(--apg-accent-pale) 0 1.14px, transparent 2.17px),radial-gradient(circle 2.75px at 45.5% 69.0%, var(--apg-accent-pale) 0 1.45px, transparent 2.75px),radial-gradient(circle 2.15px at 84.3% 62.7%, var(--apg-accent-pale) 0 1.13px, transparent 2.15px),radial-gradient(circle 2.41px at 58.9% 72.5%, var(--apg-accent-pale) 0 1.27px, transparent 2.41px),radial-gradient(circle 2.66px at 69.2% 39.6%, var(--apg-accent-pale) 0 1.40px, transparent 2.66px),radial-gradient(circle 2.56px at 19.6% 27.7%, var(--apg-accent-pale) 0 1.35px, transparent 2.56px),radial-gradient(circle 2.17px at 63.1% 75.3%, var(--apg-accent-pale) 0 1.14px, transparent 2.17px),radial-gradient(circle 2.15px at 40.3% 93.0%, var(--apg-accent-pale) 0 1.13px, transparent 2.15px),radial-gradient(circle 3.31px at 71.5% 68.3%, var(--apg-accent-pale) 0 1.74px, transparent 3.31px),radial-gradient(circle 2.43px at 47.9% 62.6%, var(--apg-accent-pale) 0 1.28px, transparent 2.43px),radial-gradient(circle 2.38px at 23.5% 66.2%, var(--apg-accent-pale) 0 1.25px, transparent 2.38px),radial-gradient(circle 2.83px at 90.6% 68.8%, var(--apg-accent-pale) 0 1.49px, transparent 2.83px),radial-gradient(circle 3.10px at 27.8% 4.4%, var(--apg-accent-pale) 0 1.63px, transparent 3.10px),radial-gradient(circle 2.11px at 25.7% 55.0%, var(--apg-accent-pale) 0 1.11px, transparent 2.11px),radial-gradient(circle 2.28px at 10.4% 9.6%, var(--apg-accent-pale) 0 1.20px, transparent 2.28px),radial-gradient(circle 2.96px at 6.3% 95.9%, var(--apg-accent-pale) 0 1.56px, transparent 2.96px); }
body::after  { background-image: radial-gradient(circle 2.81px at 50.1% 55.0%, var(--apg-accent-pale) 0 1.48px, transparent 2.81px),radial-gradient(circle 2.77px at 68.8% 18.0%, var(--apg-accent-pale) 0 1.46px, transparent 2.77px),radial-gradient(circle 2.20px at 83.4% 84.0%, var(--apg-accent-pale) 0 1.16px, transparent 2.20px),radial-gradient(circle 3.36px at 60.9% 49.2%, var(--apg-accent-pale) 0 1.77px, transparent 3.36px),radial-gradient(circle 2.96px at 95.9% 2.8%, var(--apg-accent-pale) 0 1.56px, transparent 2.96px),radial-gradient(circle 2.93px at 13.3% 72.9%, var(--apg-accent-pale) 0 1.54px, transparent 2.93px),radial-gradient(circle 2.43px at 65.9% 40.8%, var(--apg-accent-pale) 0 1.28px, transparent 2.43px),radial-gradient(circle 3.34px at 22.0% 22.7%, var(--apg-accent-pale) 0 1.76px, transparent 3.34px),radial-gradient(circle 2.32px at 51.8% 22.3%, var(--apg-accent-pale) 0 1.22px, transparent 2.32px),radial-gradient(circle 3.13px at 97.6% 38.4%, var(--apg-accent-pale) 0 1.65px, transparent 3.13px),radial-gradient(circle 2.60px at 44.8% 19.2%, var(--apg-accent-pale) 0 1.37px, transparent 2.60px),radial-gradient(circle 2.45px at 89.6% 22.2%, var(--apg-accent-pale) 0 1.29px, transparent 2.45px); }

/* --- 23b · Wandering drift. Each layer takes a different meandering path
       on its own cycle, so nothing moves in unison or in a straight line.
       Opacity (breathe) and translate (wander) are separate properties, so
       the two animations compose instead of overriding. --- */

@keyframes apg-wander-a {
  0%,100% { translate: 0 0; }
  20%     { translate:  1.1vw -1.4vh; }
  42%     { translate: -0.7vw -2.2vh; }
  63%     { translate: -1.6vw -0.5vh; }
  82%     { translate:  0.5vw  1.2vh; }
}
@keyframes apg-wander-b {
  0%,100% { translate: 0 0; }
  24%     { translate: -1.3vw  0.9vh; }
  47%     { translate:  0.9vw  1.8vh; }
  68%     { translate:  1.7vw -0.6vh; }
  86%     { translate: -0.4vw -1.5vh; }
}
@keyframes apg-wander-c {
  0%,100% { translate: 0 0; }
  17%     { translate:  0.8vw  1.6vh; }
  39%     { translate: -1.5vw  0.4vh; }
  58%     { translate: -0.9vw -1.9vh; }
  79%     { translate:  1.4vw -0.9vh; }
}

.apg-voiddust         { animation: apg-mote-breathe 7s   ease-in-out infinite,
                                   apg-wander-a    23s   ease-in-out infinite; }
.apg-voiddust::before { animation: apg-mote-breathe 9.5s ease-in-out infinite -3.2s,
                                   apg-wander-b    31s   ease-in-out infinite -8s; }
.apg-voiddust::after  { animation: apg-mote-breathe 12s  ease-in-out infinite -6.7s,
                                   apg-wander-c    27s   ease-in-out infinite -14s; }

body::before { animation: apg-mote-breathe 10s ease-in-out infinite,
                          apg-wander-b     37s ease-in-out infinite -11s; }
body::after  { animation: apg-mote-breathe 14s ease-in-out infinite -5s,
                          apg-wander-a     43s ease-in-out infinite -19s; }

/* --- 23c · Logo holds its size and fades.
       The wrap scales 1 -> 10 during the blow-out; these keyframes are its
       numeric inverse, so the logo stays visually the same size while it
       dissolves instead of rushing at the viewer with the ring. --- */

@keyframes apg-logo-hold {
  0%   { scale: 1;     opacity: 1;   }
  22%  { scale: 1.111; opacity: .92; }
  40%  { scale: .333;  opacity: .5;  }
  55%  { scale: .22;   opacity: 0;   }
  100% { scale: .1;    opacity: 0;   }
}
@keyframes apg-logo-return {
  0%   { scale: .1;   opacity: 0; }
  45%  { scale: .28;  opacity: 0; }
  70%  { scale: .952; opacity: .7; }
  100% { scale: 1;    opacity: 1; }
}

html.apg-entering .apg-portal-logo {
  animation: apg-logo-hold 1.15s cubic-bezier(.6,0,.3,1) both !important;
}
html.apg-exiting .apg-portal-logo {
  animation: apg-logo-return .9s cubic-bezier(.4,0,.3,1) both !important;
}

@media (prefers-reduced-motion: reduce) {
  .apg-voiddust, .apg-voiddust::before, .apg-voiddust::after,
  body::before, body::after { animation: none !important; opacity: .5 !important; }
}


/* ═══ 24 · Portal larger on desktop; logo reads in front of the ring ═══
   The logo already painted above the portal (later sibling, both z-index:auto).
   It only *looked* recessed because it is a mask filled with the same theme
   gradient as the ring, sitting in the ring's dark well — so this is a
   contrast fix, not a stacking fix. Logo width stays 26% of the portal box,
   so it grows in proportion with the portal automatically. */
@media (min-width: 1025px) {
  .apg-portal { width: clamp(260px, min(70vw, 74svh), 1020px); }
}
.apg-portal-logo {
  z-index: 2;
  filter:
    drop-shadow(0 0 2px rgba(0,0,0,.90))
    drop-shadow(0 3px 12px rgba(0,0,0,.65))
    drop-shadow(0 0 16px var(--apg-accent))
    brightness(1.22);
}

/* ============================================================
   18 · SITE HEADER & FOOTER  — added 12 Aug 2026
   Hello Elementor native header/footer, themed to the portal.
   Markup: #site-header .header-inner  /  #site-footer .footer-inner
   Deliberately NOT Elementor — keeps the payload off every page.
   ============================================================ */

/* ---- 18a · Header shell ---- */
#site-header {
  position: sticky;
  top: var(--wp-admin--admin-bar--height, 0px);
  z-index: 58;
  background: color-mix(in srgb, var(--apg-void) 86%, transparent);
  backdrop-filter: blur(9px);
  -webkit-backdrop-filter: blur(9px);
  border-bottom: 1px solid color-mix(in srgb, var(--apg-accent) 20%, transparent);
  padding: 0;
}
#site-header .header-inner {
  max-width: 1180px;
  margin-inline: auto;
  padding: 0.55rem 1.25rem;
  gap: 1.5rem;
}
#site-header .site-title a,
#site-header .site-title {
  color: var(--apg-logo);
  font-family: Cardo, Georgia, serif;
  font-size: 1.28rem;
  letter-spacing: .04em;
  text-decoration: none;
}
#site-header .custom-logo-link img { max-height: 46px; width: auto; }

/* ---- 18b · Header nav ---- */
#menu-primary { gap: .15rem; }
#menu-primary a {
  color: var(--apg-parchment);
  font-size: 1rem;
  letter-spacing: .02em;
  text-decoration: none;
  padding: .62rem .78rem;      /* keeps tap target >= 44px tall */
  border-radius: 6px;
  transition: color .18s ease, background-color .18s ease;
}
#menu-primary a:hover,
#menu-primary a:focus-visible { color: var(--apg-accent-pale); background: color-mix(in srgb, var(--apg-accent) 14%, transparent); }
#menu-primary .current-menu-item > a,
#menu-primary .current_page_item > a { color: var(--apg-accent); }

#menu-primary .sub-menu {
  background: color-mix(in srgb, var(--apg-void) 96%, transparent);
  border: 1px solid color-mix(in srgb, var(--apg-accent) 24%, transparent);
  border-radius: 10px;
  padding: .35rem;
  min-width: 218px;
  box-shadow: 0 14px 34px rgba(0,0,0,.55);
}
#menu-primary .sub-menu a { padding: .55rem .7rem; font-size: .96rem; }

#site-header .site-navigation-toggle,
#site-header .site-navigation-toggle-holder .site-navigation-toggle {
  background: transparent;
  color: var(--apg-accent);
  border: 1px solid color-mix(in srgb, var(--apg-accent) 34%, transparent);
  border-radius: 8px;
  min-width: 48px; min-height: 48px;      /* mobile tap target */
}
#site-header .site-navigation-dropdown ul,
#site-header .site-navigation-dropdown {
  background: color-mix(in srgb, var(--apg-void) 97%, transparent);
}
#site-header .site-navigation-dropdown a { color: var(--apg-parchment); padding: .8rem 1.1rem; font-size: 1rem; }

/* ---- 18c · Header must not fight the portal gate ----
   While the gate is locked the header is hidden; it fades in on entry.
   Uses visibility (not display) so the nav stays in the DOM for crawlers. */
html.apg-js.apg-locked #site-header,
html.apg-js.apg-entering #site-header {
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
}
html.apg-js #site-header { transition: opacity .55s ease .15s, visibility .55s ease .15s; }

/* ---- 18d · Footer shell ---- */
#site-footer {
  background: linear-gradient(180deg,
    color-mix(in srgb, var(--apg-void) 82%, transparent) 0%,
    var(--apg-void) 55%);
  border-top: 1px solid color-mix(in srgb, var(--apg-accent) 24%, transparent);
  padding: 3.1rem 1.25rem 1.6rem;
  position: relative;
  z-index: 2;
}
#site-footer .footer-inner {
  max-width: 1180px;
  margin-inline: auto;
  display: block;
}
#site-footer .site-branding { justify-content: center; margin-bottom: 1.8rem; }
#site-footer .custom-logo-link img { max-height: 62px; width: auto; opacity: .95; }

/* ---- 18e · The four columns ---- */
#site-footer .site-navigation { display: block; width: 100%; }
#menu-footer {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 2rem 1.75rem;
  list-style: none;
  margin: 0;
  padding: 0;
  align-items: start;
}
#menu-footer > li.apg-fcol { display: block; margin: 0; }

/* column heading — a menu item needs a URL, so it is an anchor to '#'
   that we neutralise here rather than render as a dead link */
#menu-footer > li.apg-fcol > a {
  display: block;
  pointer-events: none;
  cursor: default;
  color: var(--apg-accent);
  font-family: Cardo, Georgia, serif;
  font-size: 1.02rem;
  font-weight: 700;
  letter-spacing: .13em;
  text-transform: uppercase;
  text-decoration: none;
  padding: 0 0 .7rem;
  border-bottom: 1px solid color-mix(in srgb, var(--apg-accent) 26%, transparent);
  margin-bottom: .55rem;
}

#menu-footer .sub-menu {
  position: static;
  display: block;
  opacity: 1;
  visibility: visible;
  transform: none;
  background: none;
  border: 0;
  box-shadow: none;
  list-style: none;
  margin: 0;
  padding: 0;
  min-width: 0;
}
#menu-footer .sub-menu li { margin: 0; }
#menu-footer .sub-menu a {
  display: block;
  color: color-mix(in srgb, var(--apg-parchment) 88%, transparent);
  font-size: 1rem;            /* >= 16px: stops iOS auto-zoom */
  line-height: 1.35;
  text-decoration: none;
  padding: .5rem 0;           /* generous vertical tap target */
  transition: color .16s ease, padding-left .16s ease;
}
#menu-footer .sub-menu a:hover,
#menu-footer .sub-menu a:focus-visible {
  color: var(--apg-accent-pale);
  padding-left: .3rem;
}

/* ---- 18f · Copyright strip ---- */
#site-footer .copyright {
  max-width: 1180px;
  margin: 2.6rem auto 0;
  padding-top: 1.4rem;
  border-top: 1px solid color-mix(in srgb, var(--apg-accent) 16%, transparent);
  text-align: center;
}
#site-footer .copyright p {
  color: color-mix(in srgb, var(--apg-parchment) 62%, transparent);
  font-size: .93rem;
  line-height: 1.7;
  margin: 0;
}

/* ---- 18g · Responsive ---- */
@media (max-width: 900px) {
  #menu-footer { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 2.1rem 1.4rem; }
}
@media (max-width: 540px) {
  #menu-footer { grid-template-columns: 1fr; gap: 1.9rem; }
  #site-footer { padding-top: 2.4rem; }
  #site-header .header-inner { padding: .45rem .9rem; }
}

/* ---- 18h · Reduced motion ---- */
@media (prefers-reduced-motion: reduce) {
  #menu-footer .sub-menu a,
  #menu-primary a,
  html.apg-js #site-header { transition: none; }
}


/* ---- 18i · Strip Hello Elementor's default footer-menu chrome ----
   The theme puts a border-bottom on every footer <li> and a dropdown
   caret on parent items. Both are wrong in a column layout. */
#site-footer #menu-footer li,
#site-footer #menu-footer .sub-menu li { border: 0 !important; }
#site-footer #menu-footer li::after,
#site-footer #menu-footer li::before,
#site-footer #menu-footer a::after,
#site-footer #menu-footer .sub-arrow,
#site-footer #menu-footer .menu-item-has-children > a::after { display: none !important; content: none !important; }
#site-footer #menu-footer > li.apg-fcol > a { border-bottom: 1px solid color-mix(in srgb, var(--apg-accent) 26%, transparent) !important; }


/* ============================================================
   19 · TRILOGY ROTATOR — added 12 Aug 2026
   Blender turntable (VP9 + alpha). One book holds face-on for
   ~3.3s; hover snaps to the nearest hold and labels it, so the
   click target never moves under the cursor.
   Holds: t=0.000 Legacy Academy / 3.375 Astoria / 6.583 Immortal
   ============================================================ */

/* ---- 19a · Rotator stage ---- */
.apg-rotator {
  position: relative;
  width: min(420px, 78vw);
  margin: 2.2rem auto 1.7rem;
  aspect-ratio: 864 / 1080;
}
.apg-rotator-js { cursor: pointer; }
.apg-rotator-vid {
  width: 100%; height: 100%;
  display: block;
  object-fit: contain;
  filter: drop-shadow(0 18px 38px rgba(0,0,0,.60));
  transition: filter .35s ease, transform .35s ease;
}
.apg-rotator.is-held .apg-rotator-vid {
  transform: scale(1.02);
  filter:
    drop-shadow(0 0 26px color-mix(in srgb, var(--apg-accent) 42%, transparent))
    drop-shadow(0 18px 38px rgba(0,0,0,.60));
}

/* ---- 19b · Hit area + label ----
   tabindex=-1 in the markup: the three real links below are the
   keyboard path, so this overlay never becomes a duplicate stop. */
.apg-rotator-hit {
  position: absolute; inset: 0;
  display: flex; align-items: flex-end; justify-content: center;
  text-decoration: none;
}
.apg-rotator-label {
  opacity: 0;
  transform: translateY(9px);
  transition: opacity .26s ease, transform .26s ease;
  background: color-mix(in srgb, var(--apg-void) 88%, transparent);
  border: 1px solid color-mix(in srgb, var(--apg-accent) 46%, transparent);
  color: var(--apg-accent-pale);
  font-family: Cardo, Georgia, serif;
  font-size: 1.02rem;
  letter-spacing: .045em;
  padding: .5rem 1.05rem;
  border-radius: 999px;
  margin-bottom: -.5rem;
  white-space: nowrap;
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  pointer-events: none;
}
.apg-rotator-label:not(:empty)::after { content: ' \2192'; }
.apg-rotator.is-held .apg-rotator-label { opacity: 1; transform: translateY(0); }

/* ---- 19c · The three real links (the crawlable content) ---- */
.apg-rotator-books {
  list-style: none;
  margin: 0 auto 1.3rem;
  padding: 0;
  max-width: 780px;
  display: grid;
  grid-template-columns: repeat(3, minmax(0,1fr));
  gap: 1rem;
}
.apg-rotator-books li { margin: 0; }
.apg-rotator-books a {
  display: block;
  height: 100%;
  text-decoration: none;
  padding: .9rem .95rem;
  border-radius: 10px;
  border: 1px solid color-mix(in srgb, var(--apg-accent) 18%, transparent);
  background: color-mix(in srgb, var(--apg-void) 52%, transparent);
  transition: border-color .2s ease, background-color .2s ease, transform .2s ease;
}
.apg-rotator-books a:hover,
.apg-rotator-books a:focus-visible {
  border-color: color-mix(in srgb, var(--apg-accent) 55%, transparent);
  background: color-mix(in srgb, var(--apg-void) 76%, transparent);
  transform: translateY(-2px);
}
.apg-rotator-books strong {
  display: block;
  color: var(--apg-accent);
  font-family: inherit;
  font-size: 1.07rem;
  font-weight: 700;
  letter-spacing: normal;
  margin-bottom: .32rem;
}
.apg-rotator-books span {
  display: block;
  color: color-mix(in srgb, var(--apg-parchment) 78%, transparent);
  font-size: .94rem;
  line-height: 1.45;
}
.apg-rotator-all { text-align: center; margin: 0; }
.apg-rotator-all a {
  color: var(--apg-accent-pale);
  font-size: 1rem;
  text-underline-offset: .22em;
}

/* ---- 19d · Fallback: no JS, or codec refused (Safari VP9 alpha) ----
   The poster attribute carries the first hold frame, so something
   correct is always on screen and the links below still work. */
.apg-rotator.is-fallback .apg-rotator-vid { visibility: hidden; }
.apg-rotator.is-fallback {
  background: url("/wp-content/uploads/2026/08/apg-trilogy-poster.webp") center / contain no-repeat;
}

/* ---- 19e · Responsive ---- */
@media (max-width: 720px) {
  .apg-rotator-books { grid-template-columns: 1fr; gap: .7rem; max-width: 420px; }
  .apg-rotator { width: min(320px, 74vw); margin-top: 1.6rem; }
}

/* ---- 19f · Reduced motion ---- */
@media (prefers-reduced-motion: reduce) {
  .apg-rotator-vid,
  .apg-rotator-label,
  .apg-rotator-books a { transition: none; }
  .apg-rotator.is-held .apg-rotator-vid { transform: none; }
}


/* ============================================================
   20 · CAMP EPIPHANY LAUNCH CARD — added 12 Aug 2026
   Announcement + ARC funnel. Cover is a placeholder until the
   final art lands; swap .apg-ce-cover for a real <img> then.
   ARC window: 15 Aug - 30 Sept. Release: 15 Oct 2026.
   ============================================================ */

/* ---- 20a · Launch card shell ---- */
.apg-ce {
  border: 1px solid color-mix(in srgb, var(--apg-accent) 30%, transparent);
  border-radius: 16px;
  background:
    radial-gradient(120% 90% at 50% 0%,
      color-mix(in srgb, var(--apg-accent) 11%, transparent) 0%,
      transparent 62%),
    color-mix(in srgb, var(--apg-void) 62%, transparent);
  padding: 2.2rem 1.6rem 2.4rem;
}

/* ---- 20b · New badge ---- */
.apg-ce-badge {
  display: inline-block;
  margin: 0 auto .9rem;
  padding: .34rem 1rem;
  border-radius: 999px;
  background: color-mix(in srgb, var(--apg-accent) 88%, var(--apg-void));
  color: var(--apg-void);
  font-size: .8rem;
  font-weight: 700;
  letter-spacing: .14em;
  text-transform: uppercase;
}

/* ---- 20c · Cover placeholder (temporary) ---- */
.apg-ce-cover {
  width: min(210px, 52vw);
  aspect-ratio: 2 / 3;
  margin: 1.3rem auto 1.5rem;
  display: grid;
  place-items: center;
  border: 2px dashed color-mix(in srgb, var(--apg-accent) 40%, transparent);
  border-radius: 8px;
  background: color-mix(in srgb, var(--apg-void) 55%, transparent);
  color: color-mix(in srgb, var(--apg-accent-pale) 70%, transparent);
  font-family: Cardo, Georgia, serif;
  font-size: .95rem;
  line-height: 1.5;
  text-align: center;
  letter-spacing: .04em;
}

/* ---- 20d · ARC note ---- */
.apg-ce-arc {
  max-width: 620px;
  margin-inline: auto;
  font-size: .95rem;
  line-height: 1.6;
  color: color-mix(in srgb, var(--apg-parchment) 72%, transparent);
  border-top: 1px solid color-mix(in srgb, var(--apg-accent) 16%, transparent);
  padding-top: 1.1rem;
  margin-top: 1.4rem;
}

@media (max-width: 540px) {
  .apg-ce { padding: 1.7rem 1.1rem 1.9rem; border-radius: 12px; }
}


/* ---- 20e · Button corrections (site-wide) ----
   Core's button styles inherit the global link underline, and
   is-style-outline was still painting the filled background.
   Fixes the launch CTAs and the bestseller CTAs together. */
.wp-block-button__link,
.apg-cta .wp-block-button__link {
  text-decoration: none;
  font-weight: 600;
  letter-spacing: .02em;
  padding: .78rem 1.5rem;
  border-radius: 999px;
  min-height: 48px;              /* tap target */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: background-color .2s ease, color .2s ease, border-color .2s ease, transform .15s ease;
}
.wp-block-button__link:hover,
.wp-block-button__link:focus-visible { transform: translateY(-1px); text-decoration: none; }

/* filled */
.wp-block-button:not(.is-style-outline) > .wp-block-button__link {
  background-color: var(--apg-accent);
  color: var(--apg-void);
  border: 1px solid var(--apg-accent);
}
.wp-block-button:not(.is-style-outline) > .wp-block-button__link:hover {
  background-color: var(--apg-accent-pale);
  border-color: var(--apg-accent-pale);
}

/* outline — was rendering filled */
.wp-block-button.is-style-outline > .wp-block-button__link,
.wp-block-button.is-style-outline > .wp-block-button__link:not(.has-background) {
  background-color: transparent;
  background-image: none;
  color: var(--apg-accent);
  border: 1px solid color-mix(in srgb, var(--apg-accent) 58%, transparent);
}
.wp-block-button.is-style-outline > .wp-block-button__link:hover {
  background-color: color-mix(in srgb, var(--apg-accent) 15%, transparent);
  color: var(--apg-accent-pale);
  border-color: var(--apg-accent);
}

.apg-cta { gap: .8rem; justify-content: center; flex-wrap: wrap; }

/* ---- 20f · Badge sits centred ----
   its core/html wrapper inherits text-align:start, so centre it directly */
.apg-ce-badge {
  display: block;
  width: fit-content;
  margin-inline: auto;
}

@media (prefers-reduced-motion: reduce) {
  .wp-block-button__link { transition: none; }
  .wp-block-button__link:hover { transform: none; }
}


/* ==========================================================================
   24 · INTERIOR PORTAL TREATMENT
   The gate and .apg-bg are front-page only, so every other page inherited
   Hello Elementor's white body and rendered gold dust on white.
   Fix: void + book art go on <body> itself, the same way the dust already
   does. Scoped off .home so the gate's dark void is untouched.
   body::before / body::after ARE the dust layers -- never reuse them here.
   ========================================================================== */

body:not(.home) {
  background-color: var(--apg-void);
  background-image:
    radial-gradient(circle at 50% 38%, transparent 0%,
      color-mix(in srgb, var(--apg-void) 90%, transparent) 88%),
    var(--apg-bg-img);
  background-repeat: no-repeat, no-repeat;
  background-position: center center, center center;
  background-size: cover, cover;
  background-attachment: fixed, fixed;
  color: var(--apg-parchment);
}

/* Rear dust layer is body::before at z-index 1 -- lift real content over it. */
/* Header keeps whatever position block 18a gave it (sticky) -- only the
   stacking context is set here. Forcing position:relative on #site-header
   silently killed the sticky header; do not put it back. */
body:not(.home) #site-header { z-index: 20; }
body:not(.home) .site-main,
body:not(.home) #site-footer { position: relative; z-index: 2; }

/* Readability scrim, same recipe as the homepage .apg-section scrim.
   z-index -1 keeps it inside .site-main's stacking context, behind its
   content but in front of the body art -- no child rules needed. */
body:not(.home) .site-main {
  padding-block: clamp(2.25rem, 5.5vw, 4rem);
}
/* Scrim moved to html::after in block 33. It used to live on .site-main,
   but .site-main is now the view-transition capture target, so the scrim
   travelled with the sliding content and dragged a dark band across the art. */

/* Type ------------------------------------------------------------------- */
body:not(.home) .site-main,
body:not(.home) .site-main :is(p, li, dd, dt, td, th, blockquote, figcaption, label, span) {
  color: var(--apg-parchment);
}
body:not(.home) .site-main :is(h1, h2) { color: var(--apg-accent); }
body:not(.home) .site-main :is(h3, h4, h5, h6) { color: var(--apg-accent-pale); }
body:not(.home) .site-main hr { border-color: color-mix(in srgb, var(--apg-accent) 28%, transparent); }

/* Preset colour classes from the block editor were picked against white.
   Neutralise the dark ones; leave anything with a background alone. */
body:not(.home) .site-main :is(.has-tertiary-color, .has-secondary-color, .has-foreground-color,
  .has-black-color, .has-dark-gray-color, .has-medium-gray-color):not(.has-background) {
  color: var(--apg-parchment);
}

body:not(.home) .site-main a:not(.wp-block-button__link):not(.button):not(.apg-back) {
  color: var(--apg-accent-pale);
  text-decoration-color: color-mix(in srgb, var(--apg-accent) 55%, transparent);
  text-underline-offset: .18em;
}
body:not(.home) .site-main a:not(.wp-block-button__link):not(.button):not(.apg-back):hover,
body:not(.home) .site-main a:not(.wp-block-button__link):not(.button):not(.apg-back):focus-visible {
  color: var(--apg-accent);
}

/* Fields were white-on-white against the old white body. */
body:not(.home) .site-main :is(input:not([type="checkbox"]):not([type="radio"]):not([type="submit"]):not([type="button"]), textarea, select) {
  background: color-mix(in srgb, var(--apg-void) 70%, transparent);
  color: var(--apg-parchment);
  border: 1px solid color-mix(in srgb, var(--apg-accent) 38%, transparent);
  border-radius: 8px;
}
body:not(.home) .site-main :is(input, textarea, select)::placeholder {
  color: color-mix(in srgb, var(--apg-parchment) 55%, transparent);
}
body:not(.home) .site-main :is(input, textarea, select):focus-visible {
  outline: 2px solid var(--apg-accent);
  outline-offset: 2px;
}

/* Images that were cut out against white need a little separation now. */
body:not(.home) .site-main .wp-block-image img {
  filter: drop-shadow(0 14px 30px rgba(0, 0, 0, .55));
}


/* ==========================================================================
   25 · INTERIOR BACK AFFORDANCE
   Markup is printed by the apg-portal mu-plugin as the first thing inside
   the content, on singular pages only. Never on the front page.
   ========================================================================== */

.apg-back {
  display: inline-flex; align-items: center; gap: .55rem;
  margin: 0 0 clamp(1.1rem, 3vw, 1.9rem);
  padding: .46rem 1.05rem .46rem .85rem;
  font-family: Cardo, Georgia, "Times New Roman", serif;
  font-style: italic;
  font-size: clamp(.88rem, 1.5vw, 1rem);
  letter-spacing: .06em;
  line-height: 1;
  color: var(--apg-accent-pale) !important;
  text-decoration: none !important;
  border: 1px solid color-mix(in srgb, var(--apg-accent) 42%, transparent);
  border-radius: 999px;
  background: color-mix(in srgb, var(--apg-void) 55%, transparent);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  transition: color .28s ease, border-color .28s ease,
              background .28s ease, box-shadow .28s ease;
}
.apg-back::before {
  content: "\2190";
  font-style: normal;
  transition: transform .28s ease;
}
.apg-back:hover,
.apg-back:focus-visible {
  color: var(--apg-accent) !important;
  border-color: color-mix(in srgb, var(--apg-accent) 78%, transparent);
  background: color-mix(in srgb, var(--apg-void) 72%, transparent);
  box-shadow: 0 0 18px color-mix(in srgb, var(--apg-accent) 28%, transparent);
}
.apg-back:hover::before,
.apg-back:focus-visible::before { transform: translateX(-3px); }
.apg-back:focus-visible { outline: 2px solid var(--apg-accent); outline-offset: 3px; }


/* ==========================================================================
   26 · CUBIC SLIDE -- DIRECTION
   Block 9 already had valid @view-transition + old/new keyframes. It was
   never firing for a different reason: WordPress sends
   "Cache-Control: ... no-store, private" to logged-in users and on a
   private "Coming soon" site, and Chrome disables cross-document view
   transitions on no-store responses. Nothing to fix in CSS -- it will run
   for logged-out visitors on public production.
   What WAS missing is direction. Types are set by the mu-plugin script from
   pageswap / pagereveal; block 9 stays the forward default.
   ========================================================================== */

@keyframes apg-slide-out-right { to   { transform: translateX(28%)   scale(.96); opacity: 0; } }
@keyframes apg-slide-in-left   { from { transform: translateX(-100%) scale(.96); opacity: 0; } }

html:active-view-transition-type(apg-back)::view-transition-old(root) {
  animation-name: apg-slide-out-right;
}
html:active-view-transition-type(apg-back)::view-transition-new(root) {
  animation-name: apg-slide-in-left;
}

@media (prefers-reduced-motion: reduce) {
  html:active-view-transition-type(apg-back)::view-transition-old(root),
  html:active-view-transition-type(apg-back)::view-transition-new(root) {
    animation: none !important;
  }
}


/* ==========================================================================
   27 · MOXXI SLOT + AUTHOR SECTION
   .apg-moxxi-slot holds the Book 1 cover at exactly the trilogy rotator's
   footprint -- min(420px, 78vw) at 4:5 -- so when the Moxxi turntable is
   ready it drops straight in with no layout shift. Both are 4:5 already
   (rotator 864x1080, cover 2400x3000).
   ========================================================================== */

.apg-moxxi-slot {
  width: min(420px, 78vw);
  aspect-ratio: 864 / 1080;
  margin: 2.2rem auto 1.7rem;
  position: relative;
  display: block;
}
.apg-moxxi-slot img {
  width: 100%; height: 100%;
  object-fit: contain;
  display: block;
  filter: drop-shadow(0 18px 38px rgba(0, 0, 0, .60));
  transition: filter .35s ease, transform .35s ease;
}
.apg-moxxi-slot a { display: block; width: 100%; height: 100%; }
.apg-moxxi-slot:hover img,
.apg-moxxi-slot a:focus-visible img {
  transform: translateY(-4px);
  filter: drop-shadow(0 22px 44px rgba(0, 0, 0, .68))
          drop-shadow(0 0 22px color-mix(in srgb, var(--apg-accent) 34%, transparent));
}
.apg-moxxi-slot a:focus-visible { outline: 2px solid var(--apg-accent); outline-offset: 6px; border-radius: 6px; }
/* core/image adds its own margins on the figure */
.apg-moxxi-slot.wp-block-image { margin: 2.2rem auto 1.7rem !important; }


/* Author section -------------------------------------------------------- */

.apg-author {
  display: grid;
  grid-template-columns: minmax(180px, 260px) minmax(0, 1fr);
  gap: clamp(1.5rem, 4vw, 2.75rem);
  align-items: start;
  margin: 0 auto clamp(1.75rem, 4vw, 2.5rem);
}
.apg-author-photo {
  margin: 0;
  position: relative;
  border-radius: 14px;
  overflow: hidden;
  border: 1px solid color-mix(in srgb, var(--apg-accent) 34%, transparent);
  box-shadow: 0 18px 38px rgba(0, 0, 0, .55),
              0 0 26px color-mix(in srgb, var(--apg-accent) 16%, transparent);
}
.apg-author-photo img {
  display: block; width: 100%; height: auto;
  filter: none !important;
}
.apg-author-bio > p:first-child { margin-top: 0; }
.apg-author-bio p { line-height: 1.72; }

.apg-socials {
  list-style: none;
  display: flex; flex-wrap: wrap; gap: .6rem;
  margin: 1.4rem 0 0; padding: 0;
}
.apg-socials li { margin: 0; }
.apg-socials a {
  display: inline-flex; align-items: center;
  padding: .44rem 1rem;
  font-size: .92rem; letter-spacing: .05em;
  border-radius: 999px;
  text-decoration: none !important;
  color: var(--apg-accent-pale) !important;
  border: 1px solid color-mix(in srgb, var(--apg-accent) 34%, transparent);
  background: color-mix(in srgb, var(--apg-void) 52%, transparent);
  transition: color .28s ease, border-color .28s ease, box-shadow .28s ease;
}
.apg-socials a:hover,
.apg-socials a:focus-visible {
  color: var(--apg-accent) !important;
  border-color: color-mix(in srgb, var(--apg-accent) 72%, transparent);
  box-shadow: 0 0 16px color-mix(in srgb, var(--apg-accent) 26%, transparent);
}

.apg-substack {
  margin: clamp(1.6rem, 4vw, 2.4rem) auto 0;
  max-width: 640px;
  padding: clamp(1.15rem, 3vw, 1.6rem);
  border-radius: 14px;
  border: 1px solid color-mix(in srgb, var(--apg-accent) 30%, transparent);
  background: color-mix(in srgb, var(--apg-void) 66%, transparent);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
  text-align: center;
}
.apg-substack h3 { margin: 0 0 .45rem; }
.apg-substack p { margin: 0 0 1.05rem; opacity: .92; }
.apg-substack-cta {
  display: inline-block;
  padding: .62rem 1.6rem;
  border-radius: 999px;
  font-weight: 600; letter-spacing: .05em;
  text-decoration: none !important;
  color: var(--apg-void) !important;
  background: linear-gradient(135deg, var(--apg-accent), var(--apg-accent-soft));
  border: 1px solid color-mix(in srgb, var(--apg-accent) 80%, transparent);
  transition: box-shadow .28s ease, transform .28s ease;
}
.apg-substack-cta:hover,
.apg-substack-cta:focus-visible {
  transform: translateY(-2px);
  box-shadow: 0 0 24px color-mix(in srgb, var(--apg-accent) 44%, transparent);
}

@media (max-width: 640px) {
  .apg-author { grid-template-columns: 1fr; justify-items: center; text-align: left; }
  .apg-author-photo { max-width: 240px; }
  .apg-socials { justify-content: center; }
}


/* ==========================================================================
   28 · INTERIOR TREATMENT -- ELEMENTOR OVERRIDE  [RETIRED 13 Aug 2026]
   Kept as a record, deliberately inert.
   Every Elementor page used to carry a per-page white background from its
   Elementor page settings:
       body.elementor-page-393:not(.elementor-motion-effects-element-type-background)
   That is (0,3,1) and silently outranked block 24's body:not(.home) at
   (0,1,1), so the portal treatment only ever landed on the four plain
   Gutenberg pages. This block forced it back with !important.
   Elementor was removed from the render path on 13 Aug 2026 -- all pages are
   core blocks now and nothing competes for the body background -- so the
   !important is gone and block 24 stands on its own. If a white body ever
   returns on an interior page, something has reintroduced a per-page
   background rule; fix that at the source rather than restoring this.
   ========================================================================== */

/* Elementor sections are gone, but leave this in place: it costs nothing and
   keeps any stray .elementor-* wrapper from punching a panel through the art. */
body:not(.home) .elementor-section,
body:not(.home) .elementor-container,
body:not(.home) .elementor-widget-wrap,
body:not(.home) .e-con,
body:not(.home) .e-con-inner {
  background-color: transparent;
}


/* ==========================================================================
   29 · WOOCOMMERCE NOTICES
   Woo's default notice is a white panel with dark text -- fine on the old
   white body, a bright slab on the portal. Only the notice family is themed
   here; the rest of the storefront waits until real products exist.
   ========================================================================== */

body:not(.home) :is(.woocommerce-info, .woocommerce-message, .woocommerce-error,
                    .woocommerce-noreviews, .wc-block-components-notice-banner) {
  background: color-mix(in srgb, var(--apg-void) 72%, transparent);
  color: var(--apg-parchment);
  border-top: 3px solid var(--apg-accent);
  border-radius: 8px;
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
}
body:not(.home) :is(.woocommerce-info, .woocommerce-message, .woocommerce-error) a {
  color: var(--apg-accent-pale);
}
body:not(.home) :is(.woocommerce-info, .woocommerce-message)::before {
  color: var(--apg-accent);
}
body:not(.home) .woocommerce-breadcrumb,
body:not(.home) .woocommerce-breadcrumb a {
  color: var(--apg-accent-pale);
}


/* ==========================================================================
   30 · BOOK PAGES + BOOKS INDEX
   These were Elementor. Rebuilt as core blocks in the homepage's section
   language so the site stops looking like two websites. Covers use the
   woocommerce_single size deliberately -- see the note in block 31.
   ========================================================================== */

.apg-book { max-width: 760px; margin-inline: auto; }
.apg-book-tagline {
  font-family: Cardo, Georgia, "Times New Roman", serif;
  font-style: italic;
  font-size: clamp(1.05rem, 2.2vw, 1.35rem);
  color: var(--apg-accent-pale);
  margin: 0 auto 1.2rem;
  max-width: 46ch;
}
.apg-book-cover {
  width: min(340px, 68vw);
  margin: 1.8rem auto 2rem !important;
}
.apg-book-cover img {
  width: 100%; height: auto; display: block;
  filter: drop-shadow(0 18px 38px rgba(0, 0, 0, .62));
}
.apg-book-hook {
  font-size: clamp(1rem, 1.9vw, 1.12rem);
  line-height: 1.65;
  color: var(--apg-parchment);
  border-left: 2px solid color-mix(in srgb, var(--apg-accent) 55%, transparent);
  padding-left: 1.1rem;
  text-align: left;
  margin-bottom: 1.8rem;
}
.apg-book-blurb { text-align: left; }
.apg-book-blurb p { line-height: 1.78; margin-bottom: 1.05rem; }

/* Books index ----------------------------------------------------------- */
.apg-books-grid {
  list-style: none; padding: 0;
  margin: 2rem auto 0;
  max-width: 1040px;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: clamp(1.2rem, 3vw, 2rem);
}
.apg-books-grid li { margin: 0; }
.apg-books-grid a {
  display: flex; flex-direction: column; height: 100%;
  padding: 1.1rem 1.1rem 1.3rem;
  border-radius: 12px;
  text-decoration: none !important;
  color: var(--apg-parchment) !important;
  border: 1px solid color-mix(in srgb, var(--apg-accent) 26%, transparent);
  background: color-mix(in srgb, var(--apg-void) 58%, transparent);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
  transition: border-color .3s ease, box-shadow .3s ease, transform .3s ease;
}
.apg-books-grid a:hover,
.apg-books-grid a:focus-visible {
  transform: translateY(-4px);
  border-color: color-mix(in srgb, var(--apg-accent) 68%, transparent);
  box-shadow: 0 14px 30px rgba(0, 0, 0, .5),
              0 0 20px color-mix(in srgb, var(--apg-accent) 22%, transparent);
}
.apg-books-grid img {
  width: 100%; height: auto; display: block;
  border-radius: 6px; margin-bottom: .9rem;
  filter: drop-shadow(0 10px 22px rgba(0, 0, 0, .5));
}
.apg-books-grid strong {
  display: block;
  font-size: 1.06rem;
  color: var(--apg-accent);
  margin-bottom: .3rem;
}
.apg-books-grid span {
  font-size: .93rem; line-height: 1.55;
  color: color-mix(in srgb, var(--apg-parchment) 84%, transparent);
}
.apg-books-grid .apg-book-order {
  display: block; margin-top: .7rem;
  font-size: .78rem; letter-spacing: .12em; text-transform: uppercase;
  color: color-mix(in srgb, var(--apg-accent) 78%, transparent);
}


/* ==========================================================================
   31 · THEME SWITCHING, SITE-WIDE
   Block 2 keys off the radio inputs, and those only exist in block 0 of the
   homepage -- so every interior page fell back to the :root default and the
   site was permanently B2 amber no matter what the toggle said.
   The toggle now writes data-apg-theme onto <html> and persists it, so the
   choice survives navigation. Block 2 still wins on the homepage because
   body:has(#apg-t1:checked) carries ID specificity (1,1,1); the two are kept
   in sync by the runtime script, and block 2 remains the no-JS fallback.
   ========================================================================== */

html[data-apg-theme="b1"] {
  --apg-accent: var(--apg-b1-core); --apg-accent-soft: var(--apg-b1);
  --apg-accent-pale: var(--apg-b1-pale); --apg-accent-bg: var(--apg-b1-bg);
  --apg-portal-img: url("/wp-content/uploads/2026/08/apg-portal-b1.webp");
  --apg-bg-img:     url("/wp-content/uploads/2026/08/apg-bg-b1.webp");
}
html[data-apg-theme="b2"] {
  --apg-accent: var(--apg-b2-core); --apg-accent-soft: var(--apg-b2);
  --apg-accent-pale: var(--apg-b2-pale); --apg-accent-bg: var(--apg-b2-bg);
  --apg-portal-img: url("/wp-content/uploads/2026/08/apg-portal-b2.webp");
  --apg-bg-img:     url("/wp-content/uploads/2026/08/apg-bg-b2.webp");
}
html[data-apg-theme="b3"] {
  --apg-accent: var(--apg-b3-core); --apg-accent-soft: var(--apg-b3);
  --apg-accent-pale: var(--apg-b3-pale); --apg-accent-bg: var(--apg-b3-bg);
  --apg-portal-img: url("/wp-content/uploads/2026/08/apg-portal-b3.webp");
  --apg-bg-img:     url("/wp-content/uploads/2026/08/apg-bg-b3.webp");
}


/* ==========================================================================
   32 · HEADER-ANCHORED BACK BUTTON + ARC MENU CTA
   The back link used to sit inside the content, which meant it slid away with
   every page transition. It belongs to the frame, not the page, so it is now
   fixed into the header band and printed at wp_body_open by the mu-plugin.
   Deliberately NOT given a view-transition-name -- unnamed elements are not
   captured, so it renders live from the incoming document and appears to
   never move. Same reasoning for the header, footer, background and dust.
   Supersedes the inline pill styling in block 25.
   ========================================================================== */

.apg-back {
  position: fixed;
  top: calc(var(--wp-admin--admin-bar--height, 0px) + 8px);
  left: clamp(.6rem, 2vw, 1.5rem);
  z-index: 30;
  margin: 0;
  padding: .42rem 1rem .42rem .8rem;
  line-height: 1;
}
.apg-back[hidden] { display: none; }

/* Mobile: shrink to a single chevron on the left edge. */
@media (max-width: 900px) {
  .apg-back {
    left: 2px;
    padding: .5rem .55rem;
    gap: 0;
    border-radius: 0 999px 999px 0;
  }
  .apg-back .apg-back-text {
    position: absolute; width: 1px; height: 1px;
    margin: -1px; padding: 0; overflow: hidden;
    clip-path: inset(50%); white-space: nowrap;
  }
  .apg-back::before { content: "\2039"; font-size: 1.5rem; line-height: .8; }
}

/* ARC call to action in the primary menu ---------------------------------
   Toggle it from Settings > Reading > "Show the ARC request button".
   Turning it off removes the markup entirely -- it is not just hidden. */
#site-header .menu-item.apg-arc-cta > a {
  display: inline-flex; align-items: center;
  padding: .42rem 1.05rem;
  border-radius: 999px;
  color: var(--apg-void) !important;
  background: linear-gradient(135deg, var(--apg-accent), var(--apg-accent-soft));
  border: 1px solid color-mix(in srgb, var(--apg-accent) 80%, transparent);
  font-weight: 600;
  transition: box-shadow .28s ease, transform .28s ease;
}
#site-header .menu-item.apg-arc-cta > a:hover,
#site-header .menu-item.apg-arc-cta > a:focus-visible {
  transform: translateY(-1px);
  box-shadow: 0 0 20px color-mix(in srgb, var(--apg-accent) 45%, transparent);
}


/* ==========================================================================
   33 · PORTAL NAVIGATION -- ONLY THE CONTENT MOVES
   The whole point of the effect: the portal frame (header, background art,
   dust, back button) is the room you are standing in, and navigating just
   turns your head. So the root is explicitly un-named, which stops the
   browser snapshotting the entire page, and only .site-main is captured.
   Everything else renders live from the incoming document and never moves.
   Block 9's ::view-transition-*(root) rules are inert now by design.
   Forward = right to left. Back = left to right, set as a transition type by
   the mu-plugin from pageswap / pagereveal.
   STILL GATED BY no-store: WordPress sends Cache-Control: no-store to
   logged-in users and on a private site, and Chrome refuses cross-document
   view transitions on no-store. Log out on public production to see it.
   ========================================================================== */

:root { view-transition-name: none; }
.site-main { view-transition-name: apg-content; }

::view-transition-old(apg-content) {
  animation: apg-slide-out-left .58s cubic-bezier(.65, 0, .35, 1) both;
}
::view-transition-new(apg-content) {
  animation: apg-slide-in-right .58s cubic-bezier(.65, 0, .35, 1) both;
}
html:active-view-transition-type(apg-back)::view-transition-old(apg-content) {
  animation-name: apg-slide-out-right;
}
html:active-view-transition-type(apg-back)::view-transition-new(apg-content) {
  animation-name: apg-slide-in-left;
}

/* Scrim relocated here from .site-main::before. Fixed to the viewport and
   attached to html, so it is outside the captured content and holds still. */
html:has(body:not(.home))::after {
  content: ""; position: fixed; inset: 0; z-index: -1; pointer-events: none;
  background: color-mix(in srgb, var(--apg-void) 62%, transparent);
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
}

@media (prefers-reduced-motion: reduce) {
  ::view-transition-old(apg-content),
  ::view-transition-new(apg-content) { animation: none !important; }
}


/* ==========================================================================
   34 · BOOK PAGE TWO-COLUMN + INDEX ALIGNMENT
   Single column meant a 340px cover, then a long scroll of dead space, then
   the buy buttons somewhere below the fold. Cover left, everything else
   right, and the cover sticks while the blurb scrolls past it.
   ========================================================================== */

@media (min-width: 900px) {
  .apg-book {
    display: grid;
    grid-template-columns: minmax(250px, 330px) minmax(0, 1fr);
    column-gap: clamp(1.75rem, 3.5vw, 3.25rem);
    align-items: start;
    max-width: 1040px;
    text-align: left;
  }
  /* Everything lands in the right column; the cover is pulled into the left
     and spans the lot. span 20 rather than -1 -- these rows are implicit, and
     -1 resolves to the end of the explicit grid, which is row 1. */
  .apg-book > * { grid-column: 2; }
  .apg-book-cover {
    grid-column: 1;
    grid-row: 1 / span 20;
    width: 100%;
    margin: .35rem 0 0 !important;
    position: sticky;
    top: calc(var(--wp-admin--admin-bar--height, 0px) + 76px);
  }
  /* WP's constrained layout gives every child margin-inline:auto, and the
     portal headings shrink-wrap, so the h1 sat centred in its column while
     the body copy sat left. Pin the whole right column to the left edge. */
  .apg-book > *:not(.apg-book-cover) {
    margin-left: 0 !important;
    margin-right: 0 !important;
    width: auto;
    max-width: none;
    text-align: left;
  }
  .apg-book > h1 { margin-top: 0; }
  .apg-book-tagline { margin-left: 0; margin-right: 0; max-width: none; }
  .apg-book .wp-block-buttons { justify-content: flex-start; }
  .apg-book > .has-text-align-center { text-align: left !important; }
}

/* Index cards: covers ship at different ratios (1197x1759 for the trilogy,
   728x965 for Immortal Eyes), so the titles sat at different heights. Give
   every cover the same box and letterbox inside it. */
.apg-books-grid img {
  aspect-ratio: 3 / 4;
  object-fit: contain;
  object-position: center bottom;
}
.apg-books-grid a { justify-content: flex-start; }
.apg-books-grid .apg-book-order { margin-top: auto; padding-top: .7rem; }


/* Camp Epiphany has no cover yet, so its card had no image box and the whole
   card collapsed out of line with The Backrooms. Same box, same ratio. */
.apg-books-grid .apg-cover-tbd {
  display: flex; align-items: center; justify-content: center;
  width: 100%; aspect-ratio: 3 / 4;
  margin-bottom: .9rem;
  border-radius: 6px;
  border: 1px dashed color-mix(in srgb, var(--apg-accent) 34%, transparent);
  background: color-mix(in srgb, var(--apg-void) 52%, transparent);
  color: color-mix(in srgb, var(--apg-accent) 78%, transparent);
  font-size: .82rem; letter-spacing: .14em; text-transform: uppercase;
  text-align: center; line-height: 1.7;
}

/* ===== 35. Blog index cards ===== */
.apg-blog-list .apg-blog-cards{display:grid;grid-template-columns:repeat(auto-fill,minmax(clamp(240px,28vw,320px),1fr));gap:2rem;list-style:none;margin:2.5rem 0 0;padding:0}
.apg-blog-list .apg-blog-cards>li{display:flex;flex-direction:column;background:color-mix(in srgb,var(--apg-void) 68%,transparent);backdrop-filter:blur(3px);border:1px solid color-mix(in srgb,var(--apg-accent) 20%,transparent);border-radius:10px;overflow:hidden;transition:transform .25s ease,border-color .25s ease}
.apg-blog-list .apg-blog-cards>li:hover{transform:translateY(-4px);border-color:color-mix(in srgb,var(--apg-accent) 55%,transparent)}
.apg-blog-thumb{margin:0;line-height:0}
.apg-blog-thumb img{display:block;width:100%;height:100%;object-fit:cover}
.apg-blog-title{margin:1.05rem 1.15rem .3rem;font-size:1.18rem;line-height:1.28}
.apg-blog-title a{text-decoration:none;color:var(--apg-parchment)}
.apg-blog-title a:hover{color:var(--apg-accent-pale);text-decoration:underline}
.apg-blog-date{margin:0 1.15rem;font-size:.76rem;letter-spacing:.06em;text-transform:uppercase;opacity:.62}
.apg-blog-excerpt{margin:.6rem 1.15rem 1.3rem;font-size:.94rem;line-height:1.58;opacity:.88}
.apg-blog-excerpt .wp-block-post-excerpt__more-link{display:inline-block;margin-top:.55rem;font-style:italic;color:var(--apg-accent-soft)}
.apg-blog-pagination{margin-top:2.75rem}
.apg-blog-pagination .page-numbers{padding:.2rem .55rem;border-radius:4px}
.apg-blog-pagination .page-numbers.current{background:color-mix(in srgb,var(--apg-accent) 25%,transparent)}
@media(max-width:600px){.apg-blog-list .apg-blog-cards{grid-template-columns:1fr;gap:1.5rem}}


/* ===== 36. Slight background lift =====
   The art files are dark by nature (b3 measures mean luma 21.7/255, max 44), so the
   pattern barely registered. Lifted the cheap way: one extra flat layer blended with
   screen to raise the blacks, and a slightly softer vignette on interior pages.
   No filters, no masks, no animation, no new elements — same number of composited
   layers as before, so this costs nothing at scroll time. Deliberately modest.
   The homepage paints the art through .apg-bg (opacity 1 once the gate is passed);
   interior pages paint it on body under a vignette. Both need lifting separately. */
.apg-bg{background-image:linear-gradient(rgba(255,255,255,.10),rgba(255,255,255,.10)),var(--apg-bg-img);background-size:cover,cover;background-position:center,center;background-blend-mode:screen,normal}
body:not(.home){background-image:radial-gradient(circle at 50% 38%,transparent 0%,color-mix(in srgb,var(--apg-void) 82%,transparent) 92%),linear-gradient(rgba(255,255,255,.10),rgba(255,255,255,.10)),var(--apg-bg-img);background-size:cover,cover,cover;background-position:center,center,center;background-attachment:fixed,fixed,fixed;background-blend-mode:normal,screen,normal}


/* ===== 37. Group mockup loops =====
   Square 1:1 loops showing all three editions of a title. They replace the single
   cover on book pages, the catalogue grid and product pages. Poster-first and only
   fetched near the viewport (apg-groupmock.php), so they cost nothing until seen. */
.apg-groupmock-wrap{margin:0;line-height:0;max-width:100%}
.apg-groupmock{display:block;width:100%;height:auto;aspect-ratio:1/1;border-radius:8px;background:transparent}
.apg-groupmock.is-fallback{display:none}
.apg-book-cover.apg-groupmock-wrap{max-width:min(520px,100%);margin-inline:auto}
.apg-books-grid .apg-grid-mock{width:100%}
.apg-books-grid .apg-grid-mock .apg-groupmock{aspect-ratio:1/1;object-fit:contain}
.apg-groupmock-product{max-width:min(560px,100%);margin:0 auto 1.5rem}
@media(max-width:781px){.apg-book-cover.apg-groupmock-wrap{max-width:100%}}



/* ---------------------------------------------------------------------------
   Book pages - group mockup sizing.
   Appended last so it wins over the earlier .apg-book / .apg-groupmock rules.
   Was: .apg-book is a 330px + 52px gap + 658px grid capped at 1040px, while
   .apg-section reserved 410px of side padding at a 1920px viewport - so the
   mockup rendered 330px square no matter how much room was available, and the
   page used barely half the screen. Scoped to body.parent-pageid-393 (the four
   book pages) so no other .apg-section is affected.
--------------------------------------------------------------------------- */

body.parent-pageid-393 .apg-section {
  padding-left: clamp(20px, 5vw, 110px);
  padding-right: clamp(20px, 5vw, 110px);
}

body.parent-pageid-393 .apg-book {
  max-width: 1340px;
  grid-template-columns: minmax(0, 620px) minmax(0, 1fr);
}

body.parent-pageid-393 .apg-book-cover.apg-groupmock-wrap {
  width: 100%;
  max-width: 100%;
}

@media (max-width: 1180px) {
  body.parent-pageid-393 .apg-book {
    grid-template-columns: minmax(0, 46%) minmax(0, 1fr);
  }
}

@media (max-width: 900px) {
  body.parent-pageid-393 .apg-book {
    grid-template-columns: minmax(0, 1fr);
  }
  body.parent-pageid-393 .apg-book-cover.apg-groupmock-wrap {
    width: 100%;
    max-width: 620px;
    margin-inline: auto;
  }
}


/* ===== 38. Drop the group mockup letterbox =====
   The loops are yuv420p with no alpha, so the black card is baked into the frames
   and shows as a rectangle against the void. screen blending makes pure black
   vanish exactly — screen(backdrop, 0) leaves the backdrop untouched — and the
   corners measure (1,1,1), so the card disappears. Dark parts of the covers lift
   only very slightly because the page behind them is near-black too.
   Replace with an alpha re-export (yuva420p) when there is one, then delete this. */
.apg-groupmock{mix-blend-mode:screen;border-radius:0;background:transparent}
.apg-groupmock-wrap{background:transparent;border:0}


/* ===== 39. WooCommerce controls: readability and theme fit =====
   Woo ships #7F54B3 purple for primary buttons and #E9E6ED grey for secondary ones,
   plus white tabs — all of which are invisible or off-brand on the void. Primary
   actions become accent-filled pills matching the site buttons, secondary actions
   become outlines so three solid accent buttons never crowd one row. */
.woocommerce a.button,.woocommerce button.button,.woocommerce input.button,
.woocommerce .single_add_to_cart_button,.woocommerce .checkout-button,
.woocommerce a.added_to_cart,.woocommerce #respond input#submit,
.wc-block-components-button{background:var(--apg-accent)!important;background-image:none!important;color:var(--apg-void)!important;border:0!important;border-radius:999px!important;font-weight:600;padding:.62rem 1.4rem;text-decoration:none;transition:filter .2s ease,transform .2s ease}
.woocommerce a.button:hover,.woocommerce button.button:hover,.woocommerce input.button:hover,
.woocommerce .single_add_to_cart_button:hover,.woocommerce .checkout-button:hover,
.woocommerce a.added_to_cart:hover{filter:brightness(1.12);transform:translateY(-1px)}
/* Secondary: apply coupon and update cart sit next to the cart table, not in the buy path. */
.woocommerce button[name="apply_coupon"],.woocommerce button[name="update_cart"],
.woocommerce input[name="apply_coupon"],.woocommerce input[name="update_cart"]{background:transparent!important;color:var(--apg-accent-pale)!important;border:1px solid color-mix(in srgb,var(--apg-accent) 48%,transparent)!important;border-radius:999px!important;font-weight:600}
.woocommerce button[name="apply_coupon"]:hover,.woocommerce button[name="update_cart"]:hover{background:color-mix(in srgb,var(--apg-accent) 16%,transparent)!important;filter:none;transform:none}
.woocommerce button:disabled,.woocommerce button.button:disabled,
.woocommerce button[name="update_cart"]:disabled{background:transparent!important;color:color-mix(in srgb,var(--apg-parchment) 42%,transparent)!important;border-color:color-mix(in srgb,var(--apg-parchment) 20%,transparent)!important;opacity:1!important;cursor:not-allowed}
/* Coupon field was 80px, so the placeholder read "Coupon c". */
.woocommerce .coupon{display:flex;gap:.6rem;align-items:center;flex-wrap:wrap}
.woocommerce #coupon_code.input-text{width:auto!important;min-width:13rem;flex:1 1 13rem;padding:.55rem .9rem;border-radius:8px}
/* Tabs: Woo paints them white with decorative corner curls. */
.woocommerce-tabs ul.tabs::before,.woocommerce-tabs ul.tabs li::before,
.woocommerce-tabs ul.tabs li::after{display:none!important}
.woocommerce-tabs ul.tabs{border-bottom:1px solid color-mix(in srgb,var(--apg-accent) 28%,transparent);padding:0;margin:0 0 1.4rem;display:flex;gap:.4rem;flex-wrap:wrap}
.woocommerce-tabs ul.tabs li{background:color-mix(in srgb,var(--apg-void) 78%,transparent)!important;border:1px solid color-mix(in srgb,var(--apg-accent) 22%,transparent)!important;border-bottom:0!important;border-radius:8px 8px 0 0!important;margin:0!important;box-shadow:none!important}
.woocommerce-tabs ul.tabs li a{color:color-mix(in srgb,var(--apg-parchment) 76%,transparent)!important;font-weight:600;text-decoration:none}
.woocommerce-tabs ul.tabs li.active{background:color-mix(in srgb,var(--apg-accent) 15%,var(--apg-void))!important;border-color:color-mix(in srgb,var(--apg-accent) 50%,transparent)!important}
.woocommerce-tabs ul.tabs li.active a{color:var(--apg-accent-pale)!important}
/* Shipment row: the carrier, destination and address editor all repeat at checkout. */
.woocommerce-shipping-totals .woocommerce-shipping-methods{margin:0;padding:0;list-style:none}
.woocommerce-shipping-totals .woocommerce-shipping-methods label{font-size:0!important}
.woocommerce-shipping-totals .woocommerce-shipping-methods label .woocommerce-Price-amount{font-size:1rem!important;font-weight:600;color:var(--apg-parchment)}
.woocommerce-shipping-destination,.woocommerce-shipping-calculator,
.woocommerce-shipping-totals .shipping-calculator-button{display:none!important}


/* ===== 40. Camp Epiphany placeholder cover ===== */
/* Attachment 4758 is the front crop of the KDP teaser wrap and is only
   440px wide, while the shared .apg-book-cover slot is 620px, so it was
   being upscaled 1.4x. Cap it at native width until the final cover
   mockup lands, then delete this block. Cheap by design: no filters,
   no masks, nothing animated. */
.apg-book .apg-ce-teaser { max-width: 440px; margin-inline: auto; }
.apg-book .apg-ce-teaser img { width: 100%; height: auto; }
.apg-book .apg-ce-teaser figcaption {
  text-align: center;
  font-size: .78rem;
  letter-spacing: .04em;
  text-transform: uppercase;
  opacity: .55;
  margin-top: .6rem;
}


/* ===== 41. Blog index — horizontal alternating rows ===== */
/* Replaces the vertical card grid from block 35 on the blog index only.
   Each post is one full-width row: image one side, text the other,
   alternating down the page. Heights are held uniform by giving the
   image a fixed 4:3 cell and line-clamping the text. */
.apg-blog-cards { grid-template-columns: 1fr; gap: 1.75rem; }

.apg-blog-cards > li {
  display: grid;
  grid-template-columns: minmax(0, 42%) minmax(0, 1fr);
  grid-template-rows: auto auto auto 1fr;
  column-gap: 0;
  overflow: hidden;
  min-height: 16rem;
}

.apg-blog-cards > li > .wp-block-post-featured-image {
  grid-column: 1; grid-row: 1 / -1;
  margin: 0; height: 100%; min-height: 100%;
}
.apg-blog-cards > li > .wp-block-post-featured-image a,
.apg-blog-cards > li > .wp-block-post-featured-image img {
  display: block; width: 100%; height: 100%;
  object-fit: cover; border-radius: 0;
}

.apg-blog-cards > li > .wp-block-post-title {
  grid-column: 2; grid-row: 1;
  margin: 1.35rem 1.5rem 0;
  font-size: clamp(1.15rem, 1.9vw, 1.5rem); line-height: 1.2;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.apg-blog-cards > li > .wp-block-post-date { grid-column: 2; grid-row: 2; margin: .5rem 1.5rem 0; }
.apg-blog-cards > li > .apg-blog-sub {
  grid-column: 2; grid-row: 3;
  margin: .55rem 1.5rem 0;
  font-size: .92rem; line-height: 1.35; opacity: .72;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.apg-blog-cards > li > .wp-block-post-excerpt { grid-column: 2; grid-row: 4; margin: .55rem 1.5rem 1.35rem; align-self: start; }
.apg-blog-cards > li .wp-block-post-excerpt__excerpt {
  display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden;
}

.apg-blog-cards > li:nth-child(even) { grid-template-columns: minmax(0, 1fr) minmax(0, 42%); }
.apg-blog-cards > li:nth-child(even) > .wp-block-post-featured-image { grid-column: 2; }
.apg-blog-cards > li:nth-child(even) > .wp-block-post-title,
.apg-blog-cards > li:nth-child(even) > .wp-block-post-date,
.apg-blog-cards > li:nth-child(even) > .apg-blog-sub,
.apg-blog-cards > li:nth-child(even) > .wp-block-post-excerpt { grid-column: 1; }

@media (max-width: 782px) {
  .apg-blog-cards > li,
  .apg-blog-cards > li:nth-child(even) {
    grid-template-columns: 1fr;
    grid-template-rows: auto auto auto auto;
    min-height: 0;
  }
  .apg-blog-cards > li > *,
  .apg-blog-cards > li:nth-child(even) > * { grid-column: 1; }
  .apg-blog-cards > li > .wp-block-post-featured-image,
  .apg-blog-cards > li:nth-child(even) > .wp-block-post-featured-image {
    grid-row: 1; height: auto; min-height: 0; aspect-ratio: 16 / 9;
  }
  .apg-blog-cards > li > .wp-block-post-title { grid-row: 2; margin-top: 1.1rem; }
  .apg-blog-cards > li > .wp-block-post-date { grid-row: 3; }
  .apg-blog-cards > li > .apg-blog-sub { grid-row: 4; }
  .apg-blog-cards > li > .wp-block-post-excerpt { grid-row: 5; }
  .apg-blog-cards > li, .apg-blog-cards > li:nth-child(even) { grid-template-rows: auto auto auto auto 1fr; }
}


/* ===== 42. Blog index — specificity patch for block 41 ===== */
/* Block 35 sets the column track on `.apg-blog-list .apg-blog-cards`, which
   outranks the single-class selector used in block 41, so the outer grid stayed
   two-up. Same declarations, matched specificity. */
.apg-blog-list .apg-blog-cards { grid-template-columns: 1fr; gap: 1.75rem; }
.apg-blog-list .apg-blog-cards > li { grid-template-columns: minmax(0, 42%) minmax(0, 1fr); }
.apg-blog-list .apg-blog-cards > li:nth-child(even) { grid-template-columns: minmax(0, 1fr) minmax(0, 42%); }
@media (max-width: 782px) {
  .apg-blog-list .apg-blog-cards > li,
  .apg-blog-list .apg-blog-cards > li:nth-child(even) { grid-template-columns: 1fr; }
}


/* ===== 43. Blog index — horizontal rows, full rule set ===== */
/* Supersedes blocks 41 and 42. Those used single-class selectors and lost to
   block 35's `.apg-blog-list .apg-blog-cards` rules — notably display:flex on
   the card, which left the grid placement computed but inert. Everything below
   is the same design at matching specificity. If blocks 41 and 42 are ever
   removed, this one stands alone. */
.apg-blog-list .apg-blog-cards > li {
  display: grid;
  grid-template-columns: minmax(0, 42%) minmax(0, 1fr);
  grid-template-rows: auto auto auto 1fr;
  column-gap: 0;
  overflow: hidden;
  min-height: 16rem;
}
.apg-blog-list .apg-blog-cards > li > .wp-block-post-featured-image {
  grid-column: 1; grid-row: 1 / -1;
  margin: 0; height: 100%; min-height: 100%; aspect-ratio: auto;
}
.apg-blog-list .apg-blog-cards > li > .wp-block-post-featured-image a {
  display: block; height: 100%;
}
.apg-blog-list .apg-blog-cards > li > .wp-block-post-featured-image img {
  display: block; width: 100%; height: 100%;
  aspect-ratio: auto; object-fit: cover; border-radius: 0;
}
.apg-blog-list .apg-blog-cards > li > .wp-block-post-title {
  grid-column: 2; grid-row: 1;
  margin: 1.35rem 1.5rem 0;
  font-size: clamp(1.15rem, 1.9vw, 1.5rem); line-height: 1.2;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.apg-blog-list .apg-blog-cards > li > .wp-block-post-date { grid-column: 2; grid-row: 2; margin: .5rem 1.5rem 0; }
.apg-blog-list .apg-blog-cards > li > .apg-blog-sub {
  grid-column: 2; grid-row: 3;
  margin: .5rem 1.5rem 0;
  font-size: .92rem; line-height: 1.35; opacity: .72;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.apg-blog-list .apg-blog-cards > li > .wp-block-post-excerpt {
  grid-column: 2; grid-row: 4; margin: .5rem 1.5rem 1.35rem; align-self: start;
}
.apg-blog-list .apg-blog-cards > li .wp-block-post-excerpt__excerpt {
  display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; margin-bottom: .5rem;
}

.apg-blog-list .apg-blog-cards > li:nth-child(even) > .wp-block-post-featured-image { grid-column: 2; }
.apg-blog-list .apg-blog-cards > li:nth-child(even) > .wp-block-post-title,
.apg-blog-list .apg-blog-cards > li:nth-child(even) > .wp-block-post-date,
.apg-blog-list .apg-blog-cards > li:nth-child(even) > .apg-blog-sub,
.apg-blog-list .apg-blog-cards > li:nth-child(even) > .wp-block-post-excerpt { grid-column: 1; }

@media (max-width: 782px) {
  .apg-blog-list .apg-blog-cards > li,
  .apg-blog-list .apg-blog-cards > li:nth-child(even) {
    grid-template-columns: 1fr;
    grid-template-rows: auto auto auto auto 1fr;
    min-height: 0;
  }
  .apg-blog-list .apg-blog-cards > li > *,
  .apg-blog-list .apg-blog-cards > li:nth-child(even) > * { grid-column: 1; }
  .apg-blog-list .apg-blog-cards > li > .wp-block-post-featured-image,
  .apg-blog-list .apg-blog-cards > li:nth-child(even) > .wp-block-post-featured-image {
    grid-row: 1; height: auto; min-height: 0; aspect-ratio: 16 / 9;
  }
  .apg-blog-list .apg-blog-cards > li > .wp-block-post-title { grid-row: 2; margin-top: 1.1rem; }
  .apg-blog-list .apg-blog-cards > li > .wp-block-post-date { grid-row: 3; }
  .apg-blog-list .apg-blog-cards > li > .apg-blog-sub { grid-row: 4; }
  .apg-blog-list .apg-blog-cards > li > .wp-block-post-excerpt { grid-row: 5; }
}


/* ===== 44. Blog index - row height evening ===== */
/* Tunes block 43. Most rows settle at 264px, two with a wrapping subtitle
   at 293px. Raising the floor to 18rem brings the short ones up to meet them
   rather than clipping the tall ones, which would have cut the Keep reading
   link. */
.apg-blog-list .apg-blog-cards > li { min-height: 18rem; }
@media (max-width: 782px) { .apg-blog-list .apg-blog-cards > li { min-height: 0; } }


/* ===== 45. Group mock loops - alpha, so no screen blend ===== */
/* Block 38 stripped a baked-in black card with mix-blend-mode:screen, which
   also destroyed the books contact shadows. The loops were re-exported with
   real VP9 alpha on 14 Aug and the posters are transparent too (corners read
   alpha 0-18), so the blend is no longer needed and now actively washes the
   footage out. Block 38 can be deleted outright next time the CSS is tidied. */
.apg-groupmock-wrap video.apg-groupmock, .apg-groupmock-wrap .apg-groupmock { mix-blend-mode: normal; }


/* ===== 46. Single post reading layout ===== */
/* Posts were 1140px wide at 16px/24px with the featured image at full size and
   no title at all. The title, date and reading time now come from the
   apg-post-header mu-plugin; everything below is the reading treatment. */
.single-post .page-content {
  max-width: 44rem;
  margin-inline: auto;
}

/* --- header from apg-post-header.php --- */
.single-post .apg-post-head { margin: 0 0 2.25rem; }
.single-post .apg-post-kicker {
  font-size: .72rem; letter-spacing: .18em; text-transform: uppercase;
  color: var(--apg-accent); opacity: .8; margin: 0 0 .6rem;
}
.single-post .apg-post-title {
  font-size: clamp(2rem, 4.4vw, 3rem); line-height: 1.1;
  margin: 0 0 .7rem; text-wrap: balance;
}
.single-post .apg-post-meta {
  display: flex; align-items: center; gap: .6rem; flex-wrap: wrap;
  font-size: .8rem; letter-spacing: .06em; text-transform: uppercase;
  opacity: .55; margin: 0;
}
.single-post .apg-post-dot {
  width: 3px; height: 3px; border-radius: 50%;
  background: currentColor; display: inline-block;
}

/* --- standfirst: every post opens with an h3 subtitle --- */
.single-post .page-content > h3:first-of-type:not(.jp-relatedposts-headline) {
  font-size: clamp(1.15rem, 2vw, 1.4rem);
  line-height: 1.45; font-weight: 400; font-style: italic;
  color: var(--apg-accent-pale, var(--apg-parchment));
  opacity: .85; margin: 0 0 1.75rem; padding: 0 0 1.5rem;
  border-bottom: 1px solid color-mix(in srgb, var(--apg-accent) 28%, transparent);
}

/* --- body copy --- */
.single-post .page-content p {
  font-size: 1.075rem; line-height: 1.78; margin: 0 0 1.4em;
  text-wrap: pretty;
}
.single-post .page-content > h2,
.single-post .page-content > h3:not(:first-of-type):not(.jp-relatedposts-headline),
.single-post .page-content > h4 {
  margin: 2.4rem 0 .9rem; line-height: 1.25;
}
.single-post .page-content a:not(.wp-block-button__link) {
  text-underline-offset: .18em;
  text-decoration-color: color-mix(in srgb, var(--apg-accent) 55%, transparent);
}

/* --- images: cap the lead shot, let the rest breathe --- */
.single-post .page-content img {
  display: block; height: auto; max-width: 100%;
  border-radius: 12px; margin-inline: auto;
}
.single-post .page-content p:has(> img) { margin-block: 2rem; }
.single-post .page-content p > img {
  max-height: 62vh; width: auto;
  box-shadow: 0 18px 44px rgba(0,0,0,.55);
}
@media (min-width: 1100px) {
  .single-post .page-content p:first-of-type > img,
  .single-post .page-content > p:nth-of-type(-n+2) > img {
    max-width: calc(100% + 7rem); margin-inline: -3.5rem;
  }
}

/* --- tags --- */
.single-post .post-tags {
  margin: 3rem 0 0; padding-top: 1.5rem;
  border-top: 1px solid color-mix(in srgb, var(--apg-accent) 22%, transparent);
}
.single-post .post-tags a {
  display: inline-block; margin: 0 .4rem .5rem 0; padding: .3rem .8rem;
  font-size: .78rem; letter-spacing: .04em; text-decoration: none;
  border-radius: 999px;
  border: 1px solid color-mix(in srgb, var(--apg-accent) 35%, transparent);
  opacity: .85;
}
.single-post .post-tags a:hover {
  opacity: 1; border-color: var(--apg-accent);
  background: color-mix(in srgb, var(--apg-accent) 12%, transparent);
}

/* --- Jetpack related posts --- */
.single-post .jp-relatedposts { margin-top: 3rem; }
.single-post .jp-relatedposts-headline {
  font-size: .75rem !important; letter-spacing: .18em; text-transform: uppercase;
  color: var(--apg-accent); opacity: .8; margin-bottom: 1rem;
}
.single-post .jp-relatedposts-headline em:before { display: none; }
.single-post .jp-relatedposts-items { display: grid; gap: 1.25rem; }
@media (min-width: 720px) {
  .single-post .jp-relatedposts-items { grid-template-columns: repeat(3, 1fr); }
}

@media (max-width: 782px) {
  .single-post .page-content { max-width: 100%; }
  .single-post .page-content p > img { max-height: none; }
}


/* ===== 47. Blog cards: whole-card link, and the post lead image ===== */
/* Stretched link rather than wrapping the card in an anchor: wrapping would nest
   the Keep reading link inside another link (invalid) and give screen readers one
   giant link whose text is the entire card. The real anchor stays on the title and
   an overlay expands its hit area. Trade-off: card text is no longer selectable. */
.apg-blog-list .apg-blog-cards > li { position: relative; transition: border-color .18s ease, transform .18s ease; }
.apg-blog-list .apg-blog-cards > li .apg-card-link::after {
  content: ""; position: absolute; inset: 0; z-index: 2;
}
.apg-blog-list .apg-blog-cards > li:hover { border-color: var(--apg-accent); transform: translateY(-2px); }
.apg-blog-list .apg-blog-cards > li:has(.apg-card-link:focus-visible) {
  outline: 2px solid var(--apg-accent); outline-offset: 3px;
}
.apg-blog-list .apg-blog-cards > li .apg-card-link:focus-visible { outline: none; }
/* the image link and Keep reading both point at the same place; let the overlay win */
.apg-blog-list .apg-blog-cards > li .wp-block-post-featured-image a,
.apg-blog-list .apg-blog-cards > li .wp-block-post-excerpt__more-link { pointer-events: none; }
.apg-blog-list .apg-blog-cards > li:hover .wp-block-post-excerpt__more-link { color: var(--apg-accent); }

/* --- single post: lead image beside the opening text, not above it --- */
/* Post content always begins with an empty paragraph, then the image paragraph,
   then the h3 standfirst. Guarded with :has so it only fires when that second
   paragraph really does hold an image. */
@media (min-width: 900px) {
  .single-post .page-content > p:nth-of-type(2):has(> img) {
    float: left; width: 45%; max-width: 45%;
    margin: .35rem 2rem .75rem 0;
  }
  .single-post .page-content > p:nth-of-type(2):has(> img) > img {
    width: 100%; max-width: 100%; max-height: none;
    margin-inline: 0;
  }
  /* give the standfirst its own block so its rule does not run under the image */
  .single-post .page-content > h3:first-of-type:not(.jp-relatedposts-headline) {
    overflow: hidden; margin-top: 0;
  }
  .single-post .post-tags, .single-post .jp-relatedposts { clear: both; }
}


/* ===== 48. Post lead image - corrects the selector in block 47 ===== */
/* Block 47 targeted p:nth-of-type(2), on the assumption that content opens with an
   empty paragraph. Once apg-post-header injects its <header>, the image paragraph is
   the FIRST p. Guarded with :has so it only fires when that paragraph holds an image. */
@media (min-width: 900px) {
  .single-post .page-content > p:first-of-type:has(> img) {
    float: left; width: 45%; max-width: 45%;
    margin: .35rem 2rem .75rem 0;
  }
  .single-post .page-content > p:first-of-type:has(> img) > img {
    width: 100%; max-width: 100%; max-height: none;
    margin-inline: 0;
  }
}


/* ===== 49. Emoji must stay inline ===== */
/* Some emoji in older posts are stored as bare <img src="s.w.org/.../emoji/...">
   WITHOUT the .emoji class, so core emoji CSS (which is !important) never reaches
   them. Block 46 then treated them as content images: display:block, max-width:100%,
   and a 150px SVG stretched to the full 586px measure. Match on the source as well
   as the class so both forms are covered. Unscoped on purpose - this should never
   happen anywhere on the site. */
img.emoji,
img.wp-smiley,
img[src*="s.w.org/images/core/emoji"],
img[src*="/core/emoji/"] {
  display: inline !important;
  width: 1.15em !important;
  height: 1.15em !important;
  max-width: 1.15em !important;
  max-height: 1.15em !important;
  margin: 0 .07em !important;
  padding: 0 !important;
  border: 0 !important;
  border-radius: 0 !important;
  box-shadow: none !important;
  background: none !important;
  vertical-align: -0.18em;
}

/* the lead-image float must not fire on a paragraph whose only image is an emoji */
@media (min-width: 900px) {
  .single-post .page-content > p:first-of-type:has(> img.emoji):not(:has(> img:not(.emoji):not([src*="/core/emoji/"]))),
  .single-post .page-content > p:first-of-type:has(> img[src*="/core/emoji/"]):not(:has(> img:not(.emoji):not([src*="/core/emoji/"]))) {
    float: none; width: auto; max-width: none; margin: 0 0 1.4em;
  }
}


/* ===== 50. Shop catalogue, one buy button, product copy ===== */

/* --- 50a. Three across, on a grid --------------------------------------
   Woo ships li.product as a 22.05% float, and floats stagger the moment two
   cards differ in height — which is what looked broken once the square group
   shots landed beside the 4:5 covers. Grid removes the staircase.
   Three columns, not four: at a 1140px container that is a 357px card, wide
   enough that no book title wraps and the art can be read at a glance. */
.woocommerce ul.products,
.woocommerce-page ul.products {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 52px 3%;
  margin: 0 0 2em;
}
.woocommerce ul.products::before,
.woocommerce ul.products::after,
.woocommerce-page ul.products::before,
.woocommerce-page ul.products::after { content: none; display: none; }

.woocommerce ul.products.columns-1, .woocommerce-page ul.products.columns-1 { grid-template-columns: 1fr; }
.woocommerce ul.products.columns-2, .woocommerce-page ul.products.columns-2 { grid-template-columns: repeat(2, 1fr); }

.woocommerce ul.products li.product,
.woocommerce-page ul.products li.product {
  float: none !important;
  clear: none !important;
  width: auto !important;
  margin: 0 !important;
  display: flex;
  flex-direction: column;
  text-align: left;
}

/* --- 50b. One image plinth per card -------------------------------------
   Fixed slot so every card shares one image box and every title lands on the
   same baseline. object-position is LEFT bottom, not centre bottom, so the
   art's own left edge sits on the column rail the type is set from.
   That only works because the catalogue images are cropped to their visible
   art — the original files carry 6-17% transparent margin a side, which is
   what made the books look inset while the text hung out to the left of them.
   See the media note: card images are the -card- crops, not the solo shots
   or the raw group posters. */
.woocommerce ul.products li.product a img,
.woocommerce-page ul.products li.product a img {
  width: 100%;
  height: 360px;
  object-fit: contain;
  object-position: 0% 100%;
  margin: 0 0 2px;
  box-shadow: none;
}

/* --- 50c. Type scale ----------------------------------------------------
   Before this the button was the largest, boldest thing on the card: 16px
   600 in a filled amber pill, against a 16px 500 title and a 13.7px 400
   price in a muddy olive. The name and the price now lead and the button
   supports. Left, matching every other surface on the site — and the art is
   centred inside its own canvas by a different amount per file, so type was
   never going to line up with it anyway. */
.woocommerce ul.products li.product .woocommerce-loop-product__title {
  font-size: 1.31rem;
  line-height: 1.25;
  font-weight: 600;
  color: var(--apg-accent, #FFA200);
  margin: 0 0 .4rem;
  padding: 0;  /* the theme sets .5em vertical padding, which collapses to 0 on the font-size:0 cards below and would knock the two title shapes out of step */
  min-height: 50px;
  text-align: left;
}
.woocommerce ul.products li.product .price,
.woocommerce ul.products li.product .price .amount,
.woocommerce ul.products li.product .price bdi {
  display: block;
  font-size: 1.06rem;
  font-weight: 600;
  color: var(--apg-parchment, #F4F7FA);
  text-align: left;
}
.woocommerce ul.products li.product .price { margin: 0 0 .95rem; }
.woocommerce ul.products li.product .price .amount,
.woocommerce ul.products li.product .price bdi { display: inline; margin: 0; }

.woocommerce ul.products li.product .button,
.woocommerce ul.products li.product .added_to_cart {
  margin-top: auto;
  align-self: flex-start;
  font-size: .9rem;
  padding: .72em 1.5em;
}

/* --- 50d. One buy button at a time --------------------------------------
   apg-shop-runtime sets link.hidden on the Amazon button whenever a physical
   format is selected, but Woo's .button rule (display:inline-block) outranks
   the UA sheet's low-specificity [hidden]{display:none}, so Add to cart and
   Buy the ebook on Amazon were both on screen. */
.apg-ebook-buy[hidden],
a.apg-ebook-buy[hidden] { display: none !important; }

/* --- 50e. Series kicker -------------------------------------------------
   Every card is a two-line block: series above, book name below. The series
   line is generated, so no product is renamed — the real name stays in the
   cart, the order emails, the admin order screen and the product page H1,
   and it stays in the link's accessible name. Sizes are in rem so they
   survive the font-size:0 below. */
.woocommerce ul.products li.product .woocommerce-loop-product__title::before {
  display: block;
  font-size: .78rem;
  line-height: 1.4;
  font-weight: 600;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--apg-parchment, #F4F7FA);
  opacity: .55;
  margin: 0 0 .3rem;
}
.woocommerce ul.products li.product.post-4723 .woocommerce-loop-product__title::before,
.woocommerce ul.products li.product.post-4730 .woocommerce-loop-product__title::before,
.woocommerce ul.products li.product.post-4737 .woocommerce-loop-product__title::before { content: "Magdalena Basil Trilogy"; }

/* Moxxi, Camp Epiphany and the set already carry the series inside the
   product name. Splitting a text node is not possible in CSS, so the name is
   collapsed to zero and both lines are drawn — same result, same markup,
   and the name is untouched everywhere it actually matters. */
.woocommerce ul.products li.product.post-4744 .woocommerce-loop-product__title,
.woocommerce ul.products li.product.post-4805 .woocommerce-loop-product__title,
.woocommerce ul.products li.product.post-4747 .woocommerce-loop-product__title { font-size: 0; line-height: 0; }

.woocommerce ul.products li.product.post-4744 .woocommerce-loop-product__title::after,
.woocommerce ul.products li.product.post-4805 .woocommerce-loop-product__title::after,
.woocommerce ul.products li.product.post-4747 .woocommerce-loop-product__title::after {
  display: block;
  font-size: 1.31rem;
  line-height: 1.25;
  font-weight: 600;
  color: var(--apg-accent, #FFA200);
}
.woocommerce ul.products li.product.post-4744 .woocommerce-loop-product__title::before,
.woocommerce ul.products li.product.post-4805 .woocommerce-loop-product__title::before { content: "Pursuits of Moxxi"; }
.woocommerce ul.products li.product.post-4747 .woocommerce-loop-product__title::before { content: "Magdalena Basil Trilogy"; }
.woocommerce ul.products li.product.post-4744 .woocommerce-loop-product__title::after { content: "The Backrooms"; }
.woocommerce ul.products li.product.post-4805 .woocommerce-loop-product__title::after { content: "Camp Epiphany"; }
.woocommerce ul.products li.product.post-4747 .woocommerce-loop-product__title::after { content: "Complete Set"; }

@media (max-width: 900px) {
  .woocommerce ul.products,
  .woocommerce-page ul.products { grid-template-columns: repeat(2, 1fr); gap: 40px 3%; }
  .woocommerce ul.products.columns-1, .woocommerce-page ul.products.columns-1 { grid-template-columns: 1fr; }
  .woocommerce ul.products li.product a img,
  .woocommerce-page ul.products li.product a img { height: 260px; }
}
@media (max-width: 560px) {
  .woocommerce ul.products,
  .woocommerce-page ul.products { grid-template-columns: 1fr; gap: 36px 0; }
  .woocommerce ul.products li.product a img,
  .woocommerce-page ul.products li.product a img { height: 300px; }
}

/* --- 50f. Product copy: hook, summary, highlights ----------------------- */
.apg-prod-hook {
  margin: 0 0 1.5em;
  padding: .1em 0 .1em 18px;
  border-left: 3px solid var(--apg-accent, #FFA200);
  font-size: 1.05rem;
  line-height: 1.65;
}
.apg-prod-summary { max-width: 704px; }  /* the site measure, same as block 46 */
.apg-prod-summary p { line-height: 1.72; margin: 0 0 1.15em; }

.apg-prod-highlights {
  list-style: none;
  margin: 2.2em 0 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 18px;
}
.apg-prod-highlights li {
  margin: 0;
  padding: 20px 22px 22px;
  border-radius: 14px;
  border: 1px solid rgba(255,255,255,.14);
  border: 1px solid color-mix(in srgb, var(--apg-accent, #FFA200) 32%, transparent);
  background: rgba(255,255,255,.04);
  background: color-mix(in srgb, var(--apg-accent, #FFA200) 7%, transparent);
}
.apg-prod-highlights li::before {
  content: "\2605";
  display: block;
  color: var(--apg-accent, #FFA200);
  font-size: 1.15rem;
  line-height: 1;
  margin: 0 0 .6em;
}
.apg-hl-label {
  display: block;
  font-weight: 700;
  letter-spacing: .01em;
  color: var(--apg-accent, #FFA200);
  margin: 0 0 .4em;
}
.apg-hl-body { display: block; font-size: .95rem; line-height: 1.6; opacity: .92; }

.apg-prod-ship {
  max-width: 960px;
  margin: 2.2em 0 0;
  padding-top: 1.2em;
  border-top: 1px solid rgba(255,255,255,.12);
  font-size: .92rem;
  opacity: .78;
}

.apg-set-books { max-width: 960px; margin: 1.8em 0 0; display: grid; gap: 20px; }
.apg-set-book { margin: 0; padding-left: 18px; border-left: 2px solid rgba(255,255,255,.18); }
.apg-set-book h3 {
  margin: 0 0 .35em;
  font-size: 1.02rem;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--apg-accent, #FFA200);
}
.apg-set-book p { margin: 0; line-height: 1.65; }


/* ===== 51. Homepage book covers ===== */

/* The Camp Epiphany figure carried a tinted panel and a rounded corner from the
   days when it held a flat, edge-to-edge crop of the wrap. The cover is now a
   transparent paperback mockup, so the panel showed up as a black box.

   The gaps above and below the book were never CSS — they were 12% transparent
   padding baked into the file, ~48px at this size. Hero crop 4929 cuts the dead
   space off the top and trims the faintest of the render's own shadow off the
   bottom, which is why no drop-shadow filter is applied here: the file has one. */
.apg-ce-cover {
  background: none;
  border-radius: 0;
  box-shadow: none;
  margin: 18px auto 0;
}
.apg-ce-cover img {
  border-radius: 0 !important;
}
.apg-ce .apg-lede {
  margin-top: 18px;
}


/* ===== 52. Blog index cards - mobile stack ===== */
/* Block 43 collapses the row below 782px by resetting its children with
   `> li > *` - two classes. The desktop column assignments it has to beat are
   `> li > .wp-block-post-title` and friends - three classes - so the reset lost and
   the two-column row survived onto phones. The image and the text each held a
   column, one of which sized to 0, so a card showed either a squashed image or no
   image at all depending on whether that img had decoded when the row was laid out.
   Fix: mirror the full ancestor chain, one selector per child. */
@media (max-width: 782px) {
  .apg-blog-list .apg-blog-cards > li,
  .apg-blog-list .apg-blog-cards > li:nth-child(2n) {
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: auto auto auto auto 1fr;
    min-height: 0;
  }
  .apg-blog-list .apg-blog-cards > li > .wp-block-post-featured-image,
  .apg-blog-list .apg-blog-cards > li:nth-child(2n) > .wp-block-post-featured-image,
  .apg-blog-list .apg-blog-cards > li > .wp-block-post-title,
  .apg-blog-list .apg-blog-cards > li:nth-child(2n) > .wp-block-post-title,
  .apg-blog-list .apg-blog-cards > li > .wp-block-post-date,
  .apg-blog-list .apg-blog-cards > li:nth-child(2n) > .wp-block-post-date,
  .apg-blog-list .apg-blog-cards > li > .apg-blog-sub,
  .apg-blog-list .apg-blog-cards > li:nth-child(2n) > .apg-blog-sub,
  .apg-blog-list .apg-blog-cards > li > .wp-block-post-excerpt,
  .apg-blog-list .apg-blog-cards > li:nth-child(2n) > .wp-block-post-excerpt {
    grid-column: 1 / -1;
  }
  .apg-blog-list .apg-blog-cards > li > .wp-block-post-featured-image,
  .apg-blog-list .apg-blog-cards > li:nth-child(2n) > .wp-block-post-featured-image {
    grid-row: 1;
    width: 100%;
    height: auto;
    min-height: 0;
    aspect-ratio: 16 / 9;
    margin: 0;
  }
  .apg-blog-list .apg-blog-cards > li > .wp-block-post-featured-image a,
  .apg-blog-list .apg-blog-cards > li:nth-child(2n) > .wp-block-post-featured-image a {
    display: block;
    width: 100%;
    height: 100%;
  }
  .apg-blog-list .apg-blog-cards > li > .wp-block-post-featured-image img,
  .apg-blog-list .apg-blog-cards > li:nth-child(2n) > .wp-block-post-featured-image img {
    width: 100%;
    height: 100%;
    aspect-ratio: auto;
    object-fit: cover;
  }
  .apg-blog-list .apg-blog-cards > li > .wp-block-post-title,
  .apg-blog-list .apg-blog-cards > li:nth-child(2n) > .wp-block-post-title { grid-row: 2; margin-top: 1.1rem; }
  .apg-blog-list .apg-blog-cards > li > .wp-block-post-date,
  .apg-blog-list .apg-blog-cards > li:nth-child(2n) > .wp-block-post-date { grid-row: 3; }
  .apg-blog-list .apg-blog-cards > li > .apg-blog-sub,
  .apg-blog-list .apg-blog-cards > li:nth-child(2n) > .apg-blog-sub { grid-row: 4; }
  .apg-blog-list .apg-blog-cards > li > .wp-block-post-excerpt,
  .apg-blog-list .apg-blog-cards > li:nth-child(2n) > .wp-block-post-excerpt { grid-row: 5; }
}


/* ===== 53. Mobile header - two-column layout, and drop the back button ===== */
/* Below 782px the header is two columns. LEFT: 56px logo with the ARC pill beside it,
   the pill narrow enough that "Request an ARC" breaks to two lines. RIGHT: all eight
   menu items in a 4 x 2 grid. Was 143px - logo on its own row, then the nav wrapping to
   two 44px rows, six items then three and a pill. It read as overflow, not layout.

   *** DO NOT SET position: relative ON THIS HEADER. ***
   It is position: sticky WITH top: 46px (the admin-bar offset). Under sticky that top is
   a scroll anchor; under relative it becomes a 46px downward SHOVE, which opens a band of
   dead space above the bar and drops the whole header onto the page content. Sticky is
   already a positioned ancestor, so the absolutely positioned ARC pill below needs
   nothing extra. This was shipped broken once - do not reintroduce it.

   Four more things that will bite:

   1. The ARC pill is an <li> inside #menu-primary. CSS cannot reparent a node, so it
      cannot be moved beside the logo by reordering - it is absolute, with a reserved
      width on .site-branding standing in for the room it no longer takes in flow.
   2. THAT RESERVED WIDTH MUST EQUAL THE PILL'S RENDERED WIDTH. Written content-box first,
      it came out 18px short and the pill overlapped "Home" by 9px. Border-box now, same
      number in both places - change one, change the other.
   3. `.site-navigation ul#menu-primary > li > a` (4 classes, id, 3 elements) OUT-SPECIFIES
      `li.apg-arc-cta > a`. The pill silently inherited white-space: nowrap and a one-line
      height and spilled its second line outside its own background. It looked exactly like
      "the text is too big"; it was specificity. The ARC overrides run through the same
      ancestor chain, and the label sits at full size.
   4. .site-branding is flex-direction: column, so justify-content is its VERTICAL axis,
      and the theme caps the logo at max-height: 46px. */
@media (max-width: 782px) {
  .apg-back { display: none !important; }

  header#site-header.site-header {
    flex-wrap: nowrap;
    align-items: center;
    column-gap: .55rem;
    padding: .45rem .6rem;
  }

  /* --- left column: logo, ARC pill beside it --- */
  header#site-header.site-header .site-branding { flex: 0 0 auto; }
  header#site-header.site-header .site-branding .custom-logo-link { display: block; line-height: 0; }
  header#site-header.site-header .site-branding .custom-logo {
    display: block;
    width: 56px;
    height: 56px;
    max-height: none;   /* theme caps this at 46px */
    max-width: none;
  }
  /* 56 logo + 10 gap + 78 pill = 144. Keep in step with the pill width below. */
  header#site-header.site-header:has(.apg-arc-cta) .site-branding {
    width: 144px;
    justify-content: center;   /* column flex - this is the vertical one */
    align-self: center;
  }
  header#site-header.site-header .site-navigation ul#menu-primary > li.apg-arc-cta {
    position: absolute;
    left: calc(.6rem + 66px);
    top: 50%;
    transform: translateY(-50%);
    margin: 0;
    padding: 0;
    display: block;
  }
  header#site-header.site-header .site-navigation ul#menu-primary > li.apg-arc-cta > a {
    display: block;
    box-sizing: border-box;
    width: 78px;
    height: auto;
    min-height: 0;
    max-height: none;
    white-space: normal;
    text-align: center;
    font-size: 12.5px;
    line-height: 1.15;
    padding: 6px 8px;
  }

  /* --- right column: 4 x 2 grid --- */
  header#site-header.site-header .site-navigation { flex: 1 1 auto; min-width: 0; }
  header#site-header.site-header .site-navigation ul#menu-primary {
    display: grid;
    grid-template-columns: repeat(4, auto);
    justify-content: end;
    justify-items: end;
    align-items: center;
    gap: 4px 6px;
  }
  header#site-header.site-header .site-navigation ul#menu-primary > li { padding-right: 0; }
  header#site-header.site-header .site-navigation ul#menu-primary > li > a {
    font-size: .8rem;
    padding: .26rem .24rem;
    line-height: 1.3;
    white-space: nowrap;
  }
  header#site-header.site-header #menu-primary > li::after { content: none; }

  /* --- after 30 September: ARC off at Settings -> Reading --- */
  /* The li stops rendering, :not(:has()) takes over, the reserved 144px is released and
     spent on the menu instead: 60px logo squared off at the left edge and vertically
     centred, and the eight items get a bigger face and real breathing room. Shop takes
     over the CTA slot IN PLACE - the existing menu item is promoted, nothing is
     duplicated into the header. Delete the #menu-item-4721 rule to undo just that. */
  header#site-header.site-header:not(:has(.apg-arc-cta)) .site-branding { width: auto; }
  header#site-header.site-header:not(:has(.apg-arc-cta)) .site-branding .custom-logo { width: 60px; height: 60px; }
  header#site-header.site-header:not(:has(.apg-arc-cta)) .site-navigation ul#menu-primary { gap: 6px 14px; }
  header#site-header.site-header:not(:has(.apg-arc-cta)) .site-navigation ul#menu-primary > li > a {
    font-size: .9rem;
    padding: .32rem .45rem;
  }
  header#site-header.site-header:not(:has(.apg-arc-cta)) .site-navigation ul#menu-primary > li#menu-item-4721 > a {
    background: var(--apg-accent);
    color: var(--apg-void);
    border-radius: 999px;
    font-weight: 600;
    padding: .32rem .8rem;
  }
}


/* ===== 54. Shop catalogue - centred cards ===== */
/* Replaces block 50b's horizontal left rail on the author's call - the left-aligned art
   read badly on a phone, where the card is 358px wide and the art hugged one edge.
   The FLOOR LINE IS KEPT: object-position stays at 100% vertically, which is what stands
   every book on the same baseline. Only the horizontal axis moves to centre.

   Specificity, twice over:
   - Block 50b targets the art through `li.product a img` - three classes and FOUR
     elements. A rule written `li.product img` is one element short and loses silently
     while looking perfectly correct in the stylesheet.
   - The title and price carry their OWN text-align, so centring the card or the link does
     nothing to them. They have to be named. The series kicker (::before) and the second
     name line (::after) do inherit, so they follow the title for free. */
.woocommerce ul.products li.product a img,
.woocommerce-page ul.products li.product a img {
  object-position: 50% 100%;
}

.woocommerce ul.products li.product a.woocommerce-LoopProduct-link,
.woocommerce ul.products li.product .woocommerce-loop-product__title,
.woocommerce ul.products li.product .price,
.woocommerce ul.products li.product .price * {
  text-align: center;
}

.woocommerce ul.products li.product a.button {
  margin-inline: auto;
  align-self: center;
}


/* ===== 55. Shop toolbar - sorting hidden ===== */
/* THE TOGGLE: delete the one rule marked HIDE to bring sorting back. Everything under
   SHOWN STYLE is inert while it is hidden and takes effect the moment it is not.

   WHY IT IS HIDDEN: the sort does not merely look wrong, IT SORTS ON THE WRONG NUMBER.
   Every product is variable, so WooCommerce sorts by the MINIMUM variation price in
   _price - and the minimum variation is the EBOOK:

     Legacy Academy    _price 0.99    displayed 14.99
     Astorias Secret   _price 0.99    displayed 14.99
     Immortal Eyes     _price 0.99    displayed 14.99
     Moxxi Backrooms   _price 2.99    displayed  9.99
     Trilogy set       _price 44.97   displayed 44.97

   The displayed price is the PAPERBACK price, produced by the single-paperback-price
   filter in apg-shop-runtime.php. So the number on the card and the number being sorted
   are different numbers, and price-low-to-high puts the three 14.99 titles above the 9.99
   one. Not a stylesheet bug - the ebook variations carry their Amazon Kindle prices while
   the site deliberately never sells or shows an ebook.

   TO FIX IT PROPERLY rather than hide it, one of:
     a) a woocommerce_get_catalog_ordering_args filter sorting on a paperback-price meta
        key - PHP, so a companion mu-plugin over SFTP;
     b) drop the ebook variations and let Buy-on-Amazon be a plain link, which changes the
        cart and the runtime swap;
   Neither is worth doing for five products. Revisit when the catalogue grows. */

/* HIDE - this is the toggle */
.woocommerce .woocommerce-ordering { display: none; }

/* SHOWN STYLE - inert until the rule above is removed. */
/* The native control was a fixed 232px box with the UA arrow pinned to its far right, so
   a short selection left a wide gap between the label and the arrow. This drops the UA
   appearance, draws its own chevron a fixed 0.9rem from the right edge, and lets the box
   size to its widest option instead of a hardcoded width. */
.woocommerce .woocommerce-ordering select {
  appearance: none;
  -webkit-appearance: none;
  width: auto;
  max-width: 100%;
  padding: .5rem 2.1rem .5rem .9rem;
  background-image: linear-gradient(45deg, transparent 50%, currentColor 50%), linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-position: calc(100% - 1.15rem) 55%, calc(100% - .9rem) 55%;
  background-size: .3rem .3rem, .3rem .3rem;
  background-repeat: no-repeat;
}
.woocommerce .woocommerce-result-count { margin-top: .55rem; }


/* ===== 56. Desktop logo - overhang badge ===== */
/* The logo used to be sized BY the bar - 46px because the bar is 53px, capped by the theme
   at max-height 46px, and it could not grow without making the bar taller on every page. At
   46px the mark reads as a smudge: measured, it is only 15.5% ink, a thin magenta ring on
   near-black. Its file has just 5% transparent margin, so the crop trick that fixed the book
   mockups does not apply - the art already fills its canvas.

   So the logo sits ON the bar rather than in it: 96px in a dark disc that overhangs below.
   The bar stays 47px, so this costs nothing vertically, and at 96px the wordmark inside the
   mark is legible.

   THE BACK BUTTON KEEPS ITS CORNER. .apg-back is fixed at x=24 and ~96px wide, while the
   1140px container starts at (100vw - 1140px) / 2 - only 70px at a 1280 viewport, so a badge
   on the container edge lands underneath it. The left offset is the clearance calculation:
   push right by however much the gutter falls short of 136px, and by zero once it is wide
   enough. Measured 9px clear at 1280, 103px at 1600. Change 136 if Back changes width.

   THE DISC IS A BACKGROUND ON THE LINK, NOT A ::before. It was a ::before with z-index 1
   under an img at z-index 2, and the disc painted OVER the logo at some widths - an empty
   ring with nothing in it, while the img reported complete with a 1200x1200 natural size and
   every srcset candidate returning 200. A parent background cannot paint over its own child,
   so this version has no stacking question to get wrong. Do not refactor it back.

   overflow: visible on the header is load-bearing - without it the badge is clipped to the
   bar and the effect disappears. */
@media (min-width: 783px) {
  header#site-header { overflow: visible; }
  header#site-header .site-branding {
    width: 96px;
    min-height: 46px;   /* holds the bar height once the logo leaves the flow */
    overflow: visible;
    position: relative;
  }
  header#site-header .custom-logo-link {
    position: absolute;
    top: 2px;
    left: max(0px, calc(136px - (100vw - 1140px) / 2));   /* clears .apg-back */
    display: block;
    line-height: 0;
    border-radius: 50%;
    background: #0A0B0E;
    box-shadow: 0 0 0 8px #0A0B0E, 0 0 0 9px rgba(255, 162, 0, .35), 0 8px 26px rgba(0, 0, 0, .65);
  }
  header#site-header .custom-logo {
    display: block;
    width: 96px;
    height: 96px;
    max-height: none;   /* theme caps this at 46px */
    max-width: none;
    border-radius: 50%;
  }
}


/* ===== 57. Books index — section panel removed, POM row parity ===== */

/* 17 Aug, author's call: the void panel was hiding the portal artwork, so it is
   removed outright with no per-theme patch. Contrast measured against all three
   background layers with parchment text before writing — b3 worst pixel 11.57:1,
   b2 7.01:1, b1 99th percentile 14.11:1. b1's night sky carries lightning in
   0.127% of its pixels where contrast falls below AAA. Known, and accepted.
   Card anchors keep their own void 58% backing; that is what blurb text sits on. */
.apg-section { background: transparent; }

/* auto-fit COLLAPSES empty tracks, so the two-item Pursuits of Moxxi row threw its
   third track away and gave each book a 504px slot against the trilogy's 325px —
   which is why the two POM covers rendered 3x the size of a trilogy book.
   auto-fill keeps the empty track. Verified identical slot widths on BOTH rows at
   393 / 600 / 782 / 900 / 1100 and at desktop. Do not revert this to auto-fit:
   the entire row-parity fix is this one word. */
.apg-books-grid { grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); }

/* Group mocks are aspect-ratio 1/1; the POM stills were 3/4 and stood 622px against
   288px. A square box puts every cover on one baseline. Specificity: the rule this
   beats is .apg-books-grid img, 1 class 1 element — this is 1 class 3 elements. */
.apg-books-grid li a img { aspect-ratio: 1 / 1; }


/* ===== 58. Logo overhang badge — ring follows the active theme ===== */

/* Block 56 baked the badge's accent ring in as rgba(255,162,0,.35), which is b2 amber.
   That is also the value --apg-accent falls back to when no theme is set, so it looked
   correct until the site switched to b1 or b3 and the ring stayed amber regardless.
   Only the middle layer changes: layer 1 is the void disc, layer 2 is the 1px accent
   ring, layer 3 is the drop shadow. All three are restated because box-shadow cannot
   be overridden one layer at a time.
   Verified in-DOM before writing — b1 #0598FC, b2 #FFA200, b3 #00FF86, and with no
   data-apg-theme it still resolves to amber exactly as before, so the un-themed state
   cannot regress. Selector matches block 56's exactly and wins on source order. */
@media (min-width: 783px) {
  header#site-header .custom-logo-link {
    box-shadow:
      var(--apg-void, #0A0B0E) 0 0 0 8px,
      color-mix(in srgb, var(--apg-accent, #FFA200) 35%, transparent) 0 0 0 9px,
      rgba(0, 0, 0, .65) 0 8px 26px;
  }
}


/* ===== 59. Legacy character quiz — Forminator on theme ===== */

/* Forminator generates a per-module stylesheet of 44 rules with !important on EVERY
   declaration, at .forminator-ui.forminator-quiz-489 … (3 to 6 classes, 0 elements).
   Every rule below therefore carries !important and uses the prefix
   body form.forminator-ui.forminator-quiz.forminator-quiz
   — .forminator-quiz is deliberately DOUBLED. That is not a typo: a repeated class is
   valid and counts twice toward specificity, which is what beats Forminator's longest
   selectors (its start-button rule is (0,5,1); a single-class prefix only reached
   (0,4,2) and lost on class count). Using the generic .forminator-quiz rather than
   .forminator-quiz-489 means this survives the quiz being rebuilt as a new module.

   The module's own styles hardcode three different blues — #85C1E9 on the title and
   Start button, #81A3C1 on the question legend, rgba(33,99,177,.68) on Next — plus the
   GodofWar and Nunito faces, neither used anywhere else on the rebuilt site. Everything
   here reads var(--apg-accent) instead, so the quiz follows b1/b2/b3. Verified live:
   b1 #0598FC, b2 #FFA200, b3 #00FF86.

   ⚠️ Forminator sets transition:.2s on its buttons. In the automated browser those
   transitions never advance, so getComputedStyle returns the PRE-transition value and a
   working override reads as a total failure. Inject
   *{transition:none!important;animation:none!important} before measuring anything here. */

/* The quiz's own title and description duplicate the page H1 and lede — and its H3
   rendered at 48px against the page H1's 40px, in a different face. Page heading stands alone. */
body form.forminator-ui.forminator-quiz.forminator-quiz .forminator-quiz--title,
body form.forminator-ui.forminator-quiz.forminator-quiz .forminator-quiz--description { display: none !important; }

/* Question */
body form.forminator-ui.forminator-quiz.forminator-quiz .forminator-question .forminator-legend { color: var(--apg-parchment, #F4F7FA) !important; font-family: inherit !important; font-size: 1.5rem !important; font-weight: 600 !important; line-height: 1.35 !important; }
body form.forminator-ui.forminator-quiz.forminator-quiz .forminator-question::after { background-color: color-mix(in srgb, var(--apg-accent) 18%, transparent) !important; }
body form.forminator-ui.forminator-quiz.forminator-quiz .forminator-page-indicator { color: color-mix(in srgb, var(--apg-parchment, #F4F7FA) 62%, transparent) !important; font-family: inherit !important; font-size: .82rem !important; letter-spacing: .08em !important; text-transform: uppercase !important; }

/* Answer tiles — same language as the book and shop cards: void 58%, 12px, accent hairline */
body form.forminator-ui.forminator-quiz.forminator-quiz .forminator-answer .forminator-answer--design { background-color: color-mix(in srgb, var(--apg-void) 58%, transparent) !important; border-color: color-mix(in srgb, var(--apg-accent) 24%, transparent) !important; border-radius: 12px !important; }
body form.forminator-ui.forminator-quiz.forminator-quiz .forminator-answer .forminator-answer--design:hover,
body form.forminator-ui.forminator-quiz.forminator-quiz .forminator-answer .forminator-answer--design:focus { background-color: color-mix(in srgb, var(--apg-void) 70%, transparent) !important; border-color: color-mix(in srgb, var(--apg-accent) 55%, transparent) !important; }
body form.forminator-ui.forminator-quiz.forminator-quiz .forminator-answer input:checked + .forminator-answer--design { background-color: color-mix(in srgb, var(--apg-accent) 14%, transparent) !important; border-color: var(--apg-accent) !important; }
body form.forminator-ui.forminator-quiz.forminator-quiz .forminator-answer .forminator-answer--name { color: var(--apg-parchment, #F4F7FA) !important; font-family: inherit !important; font-size: 1rem !important; font-weight: 500 !important; }
body form.forminator-ui.forminator-quiz.forminator-quiz .forminator-answer input:checked + .forminator-answer--design .forminator-answer--name { color: var(--apg-accent) !important; font-weight: 600 !important; }
body form.forminator-ui.forminator-quiz.forminator-quiz .forminator-answer .forminator-answer--status { border-color: color-mix(in srgb, var(--apg-accent) 45%, transparent) !important; }
body form.forminator-ui.forminator-quiz.forminator-quiz .forminator-answer input:checked + .forminator-answer--design .forminator-answer--status { background-color: var(--apg-accent) !important; border-color: var(--apg-accent) !important; }

/* Buttons — matches the Woo button token exactly: accent fill, void text, 999px, .9rem/600 */
body form.forminator-ui.forminator-quiz.forminator-quiz button.forminator-button.forminator-quiz-start,
body form.forminator-ui.forminator-quiz.forminator-quiz .forminator-pagination-footer button.forminator-button,
body form.forminator-ui.forminator-quiz.forminator-quiz button.forminator-button-submit { background-color: var(--apg-accent) !important; color: var(--apg-void, #0A0B0E) !important; font-family: inherit !important; font-size: .9rem !important; font-weight: 600 !important; border-radius: 999px !important; padding: .72em 1.5em !important; }
body form.forminator-ui.forminator-quiz.forminator-quiz button.forminator-button.forminator-quiz-start:hover,
body form.forminator-ui.forminator-quiz.forminator-quiz .forminator-pagination-footer button.forminator-button:hover,
body form.forminator-ui.forminator-quiz.forminator-quiz button.forminator-button-submit:hover { background-color: var(--apg-accent-pale, #D3E9FF) !important; color: var(--apg-void, #0A0B0E) !important; }
body form.forminator-ui.forminator-quiz.forminator-quiz .forminator-pagination-footer button.forminator-button.forminator-disabled { background-color: color-mix(in srgb, var(--apg-void) 45%, transparent) !important; color: color-mix(in srgb, var(--apg-parchment, #F4F7FA) 40%, transparent) !important; }

/* Result */
body form.forminator-ui.forminator-quiz.forminator-quiz .forminator-quiz--result { border-top-color: color-mix(in srgb, var(--apg-accent) 18%, transparent) !important; }
body form.forminator-ui.forminator-quiz.forminator-quiz .forminator-quiz--result .forminator-result { background-color: color-mix(in srgb, var(--apg-void) 58%, transparent) !important; border-color: color-mix(in srgb, var(--apg-accent) 24%, transparent) !important; border-radius: 12px !important; }
body form.forminator-ui.forminator-quiz.forminator-quiz .forminator-quiz--result .forminator-result--title { color: var(--apg-accent) !important; font-family: inherit !important; font-size: 2rem !important; font-weight: 600 !important; }
body form.forminator-ui.forminator-quiz.forminator-quiz .forminator-quiz--result .forminator-result .forminator-result--content .forminator-result--description,
body form.forminator-ui.forminator-quiz.forminator-quiz .forminator-quiz--result .forminator-result .forminator-result--content .forminator-result--description p { color: var(--apg-parchment, #F4F7FA) !important; font-family: inherit !important; font-size: 1.02rem !important; }
body form.forminator-ui.forminator-quiz.forminator-quiz button.forminator-button.forminator-result--retake,
body form.forminator-ui.forminator-quiz.forminator-quiz .forminator-result--view-answers { color: var(--apg-accent) !important; font-family: inherit !important; }
body form.forminator-ui.forminator-quiz.forminator-quiz .forminator-quiz--result .forminator-quiz--social { border-top-color: color-mix(in srgb, var(--apg-accent) 18%, transparent) !important; }


/* ===== 60. Legacy character quiz — the question clears the sticky header ===== */
/* Forminator's scroll_to_top_form() animates the page so the FORM's border-box
   top lands at viewport y=0 — y=35 when #wpadminbar is present, its only
   offset. It knows nothing about #site-header, which is position:sticky and
   owns the top 47px (71px below 782px) — exactly where the question legend
   sits. So every Start / Next / Previous parked the question behind the header
   and left only the answers on screen. Ten questions out of ten, measured:
   legend top 40, header bottom 79.

   The padding goes on the STEP WRAPPER, not on the form. Padding inside the
   form does not move its offset().top, so Forminator's scroll target is
   unchanged; and .forminator-pagination generates no box on the intro screen,
   so the pre-Start layout stays identical to the pixel. Padding the form
   itself does not — it clamps the intro's content height to zero and adds 33px
   to the page.

   Clearance = max(headerH, adminBarH + headerH - 35) + air:
     desktop   max(47, 32+47-35) = 47  ->  80px, measured 41px clear
     <=782px   max(71, 46+71-35) = 82  -> 104px, measured 27px clear
   .forminator-quiz is doubled per block 59: every Forminator rule carries
   !important, so this has to out-specify them, not merely come after them. */

body form.forminator-ui.forminator-quiz.forminator-quiz .forminator-pagination {
	padding-top: 80px !important;
}

@media (max-width: 782px) {
	body form.forminator-ui.forminator-quiz.forminator-quiz .forminator-pagination {
		padding-top: 104px !important;
	}
}


/* ===== 61. Share pill on dark + related-posts cards ===== */
/* 61a. Jetpack share button — was a white pill with a near-white label */
.sharedaddy h3.sd-title { font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: .18em; color: var(--apg-accent-pale, #FAB462); }
.sharedaddy .sd-content ul li a.sd-button,
.sharedaddy .sd-content ul li a.sd-button:visited {
  background: transparent !important;
  border: 1px solid color-mix(in srgb, var(--apg-accent, #FFA200) 35%, transparent) !important;
  border-radius: 999px !important;
  box-shadow: none !important;
  color: var(--apg-accent-pale, #FAB462) !important;
  padding: .38em 1.15em .38em .95em !important;
  transition: border-color .2s, color .2s;
}
.sharedaddy .sd-content ul li a.sd-button span { color: inherit !important; }
.sharedaddy .sd-content ul li a.sd-button::before { color: currentColor !important; }
.sharedaddy .sd-content ul li a.sd-button:hover {
  border-color: var(--apg-accent, #FFA200) !important;
  color: var(--apg-parchment, #F4F7FA) !important;
}
/* 61b. Jetpack related posts — card grid; works with and without thumbnails
   (staging is private so Jetpack serves the minimal, image-free variant;
   the visual variant's img/anchor rules below are for post-launch) */
.jp-relatedposts { margin-top: 2.5rem !important; padding-top: 0 !important; border-top: 0 !important; }
.jp-relatedposts h3.jp-relatedposts-headline { margin-bottom: 1.1rem; }
.jp-relatedposts h3.jp-relatedposts-headline em { font-style: normal; }
.jp-relatedposts .jp-relatedposts-items { display: grid !important; grid-template-columns: repeat(3, 1fr) !important; gap: 1.1rem !important; }
.jp-relatedposts .jp-relatedposts-items .jp-relatedposts-post {
  float: none !important; width: auto !important; max-width: none !important;
  display: flex; flex-direction: column; gap: .3rem;
  margin: 0 !important; padding: 1rem 1.1rem 1.1rem;
  background: rgba(255,255,255,.035);
  border: 1px solid color-mix(in srgb, var(--apg-accent, #FFA200) 18%, transparent);
  border-radius: 12px;
  opacity: 1 !important;
}
.jp-relatedposts .jp-relatedposts-post > a.jp-relatedposts-post-a { order: -1; }
.jp-relatedposts .jp-relatedposts-post .jp-relatedposts-post-img,
.jp-relatedposts .jp-relatedposts-post a.jp-relatedposts-post-a img {
  width: 100% !important; height: auto !important; max-width: 100% !important;
  border-radius: 8px; margin: 0 0 .55rem !important; order: -1;
}
.jp-relatedposts .jp-relatedposts-post .jp-relatedposts-post-title {
  display: block; width: auto !important; float: none !important;
  font-size: 1.02rem !important; line-height: 1.4 !important; margin: 0 !important;
}
.jp-relatedposts .jp-relatedposts-post .jp-relatedposts-post-title a {
  color: var(--apg-accent-pale, #FAB462) !important; font-weight: 600 !important; text-decoration: none !important;
}
.jp-relatedposts .jp-relatedposts-post .jp-relatedposts-post-title a:hover { color: var(--apg-parchment, #F4F7FA) !important; }
.jp-relatedposts .jp-relatedposts-post .jp-relatedposts-post-date {
  display: block; width: auto !important; float: none !important;
  font-size: .8rem !important; color: color-mix(in srgb, var(--apg-parchment, #F4F7FA) 55%, transparent) !important;
  margin: 0 !important; opacity: 1 !important;
}
/* every post is in the Blog category, so the 'In "Blog"' context line is noise */
.jp-relatedposts .jp-relatedposts-post .jp-relatedposts-post-context { display: none !important; }
@media (max-width: 782px) {
  .jp-relatedposts .jp-relatedposts-items { grid-template-columns: 1fr !important; }
}

/* ===== 62. Accolade seal — Amazon #1, Pursuits of Moxxi: The Backrooms ===== */
/* An award seal pinned to the corner of the book art, not a text label.
   One inline <svg class="apg-seal"> per surface: homepage Moxxi block (4557),
   books index card (393), book page (3274). Delete this block and the seal loses
   its size and position; strip the three [data-apg-accolade] svgs to remove it
   from the markup properly. Colours follow the active theme via --apg-accent —
   do not freeze them as literals. */
.apg-book-cover,
.apg-moxxi-slot,
.apg-books-grid li { position: relative; }

.apg-seal {
  position: absolute;
  z-index: 3;
  height: auto;
  pointer-events: none;
  overflow: visible;
  filter: drop-shadow(0 4px 12px rgba(0, 0, 0, .55));
}
/* The artwork is the author's own Website Badge.psd, traced to vector so its
   colours can be driven by the theme. Three shape layers ship as inline SVG paths
   in the page markup; the lettering rides here as one data-URI, since it is always
   void and never needs to change. The art is already tilted — do NOT add rotation. */
.apg-seal { aspect-ratio: 1; }
.apg-seal svg { display: block; width: 100%; height: 100%; }
.apg-seal::after {
  content: "";
  position: absolute;
  inset: 0;
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 300 300'%3E%3Cpath fill='%230A0B0E' d='M129.4,220.9C129.7,219.1,117.6,207.3,115.8,207.6C113.8,208,114.6,207.4,119.5,204.5C122,203.1,124.7,201.4,125.4,200.7L126.8,199.5L128.5,201.3L130.2,203L128.6,202.4C127.2,201.8,126.7,201.9,124.7,203C121,205,121,205.2,124,208.3C127.1,211.4,126.9,211.4,129.6,209.7C131.3,208.6,131.6,208.2,131.4,207.2C131.1,206,131.3,206.1,133.3,208.1C135.9,210.6,135.9,210.6,134.1,209.9C132.9,209.4,132.3,209.6,130.3,210.8L127.9,212.2L130.9,215.1C133.4,217.5,134.3,218.1,135.3,217.9C137.1,217.5,136.9,217.7,133,220C128.8,222.4,129.1,222.3,129.4,220.9ZM145,211.2C146,210,133.4,197.4,131.4,197.6C129.5,197.9,129.5,197.9,133.8,195C136.6,193.1,137.4,192.7,137,193.5C136.1,195.2,148.7,207.8,150.7,207.3C152.6,206.8,152.3,207.1,148.5,209.7C144.8,212.1,144,212.5,145,211.2ZM86.3,210.4C86.4,210,84.4,207.7,81.6,204.8L76.7,199.9L74.8,200.8C73.3,201.6,73,201.9,73.1,202.9C73.4,204.1,73.4,204.1,71.9,202.7L70.5,201.3L73.1,200.4C74.5,199.9,77.7,198.5,80.1,197.4L84.5,195.2L85.9,196.7C86.6,197.6,86.9,198,86.5,197.6C85.3,196.6,82.1,197,80.5,198.3C80.1,198.7,90.2,208.5,91,208.5C91.8,208.5,89,210.1,87.1,210.7C86.3,211,86.1,210.9,86.3,210.4ZM98.3,204.9C98.8,204.1,89,194.5,87.5,194.5C85.6,194.4,95.8,189.5,97.8,189.5C100.5,189.5,102.7,191.7,102.4,194.1C102.1,195.8,102.2,196,103.2,196C103.8,196,105.4,196.3,106.7,196.8C109.7,197.7,112.3,197.7,112.6,196.9C112.8,196.5,111.8,193.1,110.4,189.3C109.1,185.4,108.1,182.2,108.2,182.1C108.3,182,112,183.4,116.4,185.2C120.8,187,125.1,188.5,125.8,188.5L127.2,188.5L125.5,189.7C121.4,192.5,121,192.7,121.3,191.9C121.4,191.4,120.9,190.9,119.7,190.3C117.9,189.5,117.9,189.5,115.7,190.8C113,192.3,112.9,194.8,115.3,195.6C116.3,195.9,109.7,199.6,107.6,199.8C106.3,200,104.8,199.7,102.5,198.8C100.8,198.1,98.9,197.5,98.3,197.5C97.3,197.5,97.3,197.5,98.4,196.5C99.8,195.1,99.8,194.7,98,193C96.5,191.4,94.9,191.2,93.1,192.2C92,192.8,92.2,192.9,96.9,197.6C99.6,200.2,102.2,202.5,102.8,202.6C103.5,202.7,103.1,203.1,101.1,204.1C98.3,205.6,97.8,205.8,98.3,204.9ZM150.5,200.6C142.1,196.3,142.7,187,151.7,181.8C155.8,179.4,156.2,179.4,158.2,181.5L160.1,183.4L157.8,182.6C154,181.4,149.2,184,148.2,187.8C147.3,191.5,151.7,197.3,156.5,198.7C163.2,200.7,168.5,195.1,165.5,189.2C164.1,186.6,164.2,186.5,166.5,188.8C169.5,191.7,169,194.7,164.8,198.4C160.4,202.2,155.2,203,150.5,200.6ZM74.8,191.6C75.2,190.1,62.4,177.8,60.8,178.1C59,178.5,59.3,178.3,63.9,176C67.2,174.4,67.9,174.2,67.4,175C66.4,176.6,79.1,189.2,81.2,188.8C82.2,188.6,81.5,189.2,79,190.5C74.7,192.8,74.5,192.8,74.8,191.6ZM115.1,189.9L116.6,188.8L114,187.7C112.6,187,111.3,186.5,111.2,186.5C111.1,186.5,111.4,187.5,111.9,188.8C112.8,191.3,113,191.4,115.1,189.9ZM128,187.2C128.6,186.5,128,185.7,123.2,180.9L117.6,175.4L126.2,175.8L134.8,176.1L131.5,172.8C129,170.2,128,169.5,127.1,169.7C125.6,169.9,125.6,169.9,128.7,167.6C130.2,166.4,131.3,165.8,131.2,166.3C130.9,166.9,132.5,168.7,136.3,172.6L141.8,178.1L133.3,177.9L124.9,177.6L128.3,181.1C130.2,183,132,184.4,132.4,184.3C133.6,183.8,133,184.4,130.5,186.2C127.8,188.1,127,188.5,128,187.2ZM86,186.7C86.6,186,85.8,185,79.6,178.9C75.7,175,72.6,171.8,72.6,171.7C72.7,171.7,76.2,172,80.5,172.5C84.8,173,89.9,173.5,92,173.7L95.8,174.1L91.5,169.8C87.7,165.9,87.1,165.5,85.6,165.6C83.5,165.8,83.5,165.9,86.6,164C88.1,163.1,89.7,162,90.3,161.7C91.3,161,91.4,161.1,91.2,162.1C91,163,92.2,164.5,97.7,170C101.4,173.6,104.4,176.7,104.3,176.8C104.2,177,97,176.4,85.2,175.2L81.8,174.9L86,179.2C90,183.3,90.4,183.6,91.9,183.3C94.1,183,94,183,89.5,185.5C85.5,187.7,85.1,187.9,86,186.7ZM176.9,186.4C177.2,185.4,165.5,173,164.2,173C163.3,173,160.5,176.4,160.5,177.4C160.5,177.9,160.8,178.6,161.1,179C161.5,179.4,160.9,179,159.9,178L158,176.3L159.9,174.8C160.9,174,164,171,166.9,168.2L172,163.2L173.6,164.9C174.8,166.2,175,166.5,174.2,166.1C172.5,165.2,172.2,165.3,170,167.3L167.9,169.2L174.3,175.6C179.6,180.9,181,182,181.9,181.8C182.9,181.7,182.7,182,180.7,184.1C177.6,187.2,176.5,187.9,176.9,186.4ZM188.3,174.6C188.7,172.4,176,159.9,174.4,161C173.8,161.3,174.7,160.1,176.4,158.1C179.7,154.4,180.4,153.8,179.5,155.5C178.7,156.9,179.2,157.5,185.9,164.2C191.4,169.6,192,170,193,169.5C194.6,168.6,194.1,169.5,190.9,173C188.4,175.7,188,175.9,188.3,174.6ZM41.2,173.8C41.9,172.9,41.6,171.8,38.8,163.4C37.1,158.2,35.8,154,35.9,154C36,154,41.5,156.4,48.1,159.3C60.3,164.6,61.5,165,63.4,163.4C64.3,162.6,64.4,162.6,58,154.2C54.6,149.8,52.1,146.2,52.4,146.1C52.6,146.1,56.9,147.3,62,148.9C67.5,150.6,71.3,151.6,71.4,151.3C71.5,151,70.8,147.4,70,143.3C69.2,139.2,68.6,135.7,68.7,135.6C68.8,135.5,72.4,138.1,76.7,141.5C86.3,148.9,86.1,148.7,87.7,148.3C90.3,147.6,90.3,146.8,86.6,136.4C84.7,131.1,83,126.3,82.8,125.9C82.4,125,82.6,125.1,85.1,126C102.2,132.8,105.8,134,107,133.5C108.2,132.9,108.2,132.9,107.3,133.9C105.2,136.2,100.4,139.6,100.6,138.7C100.7,138,100.1,137.4,98.5,136.5L96.2,135.3L93.1,137.7L90,140L91,142.1C91.6,143.7,92.1,144.2,92.9,144.2C94.7,144.1,94.2,144.6,88.9,148.2C82.2,152.9,80.6,153.9,81.3,153C81.7,152.6,81.8,152,81.6,151.7C81,150.9,72.9,144.4,72.8,144.6C72.4,144.9,75,156.2,75.6,156.8C76.4,157.9,75.7,157.7,67.4,155C63.2,153.7,59.7,152.6,59.7,152.7C59.2,153.2,66.1,160.8,67.2,161C68.4,161.2,67.6,161.8,61.6,164.9C54.1,168.8,53.9,168.9,54.3,167.8C54.5,167.3,53.8,166.7,52.1,165.8L49.6,164.5L46.3,166.1C44.4,166.9,43,167.9,43,168.3C43,169.7,44.5,171.9,45.5,172.1C46.3,172.2,45.7,172.6,43.6,173.6C40.7,174.8,40.6,174.9,41.2,173.8ZM143,173.5C141.7,172.2,141.7,171.3,143,172C146.4,173.8,151.5,169.8,148.7,167.5C148.1,167,147.1,166.9,143.5,167.4C137.4,168.2,135.5,167.3,135.5,163.7C135.5,161.9,136.1,160.9,138.6,159.1L140.9,157.5L142,158.7L143.1,160L141.4,159.7C139.4,159.4,137.3,161.2,137.6,163.1C137.8,164.4,138.4,164.5,144.9,163.8C148.3,163.4,148.6,163.4,149.7,164.5C152.4,167,151.5,170.6,147.6,173.1C144.9,174.8,144.3,174.8,143,173.5ZM116.1,168C117.1,166.4,104.5,153.8,102.3,154.2C101.2,154.4,101.9,153.7,105.3,151.2C107.7,149.4,110.4,147.2,111.3,146.4L112.8,145L114.5,146.9C115.5,147.9,115.9,148.5,115.5,148.1C114.3,147,112.4,147.4,109.9,149.4L107.5,151.2L110.4,154.1C113.7,157.5,113.5,157.4,115.8,155.4C117.2,154.2,117.5,153.5,117.4,152.6C117.1,151.6,117.4,151.7,119.3,153.6C121.7,155.9,121.9,156.3,120.5,155.5C119.4,154.9,118.3,155.2,116.1,156.9L114.4,158.2L117.3,161.1C119.9,163.7,120.4,164,121.7,163.8C122.9,163.5,122.5,164,119.5,166.2C115.4,169.3,115.2,169.4,116.1,168ZM46.1,164.7C46.9,164.3,47.4,163.7,47.2,163.5C46.3,162.6,39.9,160,40.1,160.6C40.2,161,40.8,162.4,41.3,163.8C42.3,166.7,42.3,166.7,46.1,164.7ZM152.2,162.4C145.4,158.7,145.4,151.7,152.1,146.7L155.1,144.5L156.5,145.6C157.9,146.6,158.7,148,157.5,147.2C155.2,145.8,151,147.9,150,150.8C149,153.8,151.4,157.9,155.3,159.9C160.3,162.5,165.1,156.8,161,153.3C160.1,152.6,159.2,152.1,158.8,152.3C158.5,152.5,159.2,151.7,160.2,150.5C161.3,149.3,162.3,148.1,162.5,148C162.6,147.9,162.5,148.1,162.4,148.6C162.2,149.1,162.6,149.9,163.6,150.8C168.6,155.6,158.5,165.9,152.2,162.4ZM192.1,162.2C183.5,157.9,183.8,145.9,192.6,139.9C199.5,135.2,208.9,142.9,207.3,151.8C205.8,159.8,198,165.1,192.1,162.2ZM202.8,157.8C207.5,153.1,201.3,142,194,142C189.8,142,187.5,147.2,189.5,151.8C192.3,158.4,199.1,161.5,202.8,157.8ZM130.5,156.3C131.4,155.2,130.8,154.4,124.1,147.8C119,142.8,117.9,142,116.9,142.2C114.5,142.9,124.9,134.2,127.4,133.5C131.2,132.3,134.3,135.2,133.9,139.5C133.8,140.9,133.8,141.9,134.1,141.7C134.3,141.6,136.5,141.9,139.1,142.3C142.7,143,144,143,145.2,142.6L146.7,142L145,143.7C141.6,147.3,139,147.7,133.7,145.7C131.9,145,130,144.6,129.7,144.7C128.8,145.1,128.8,144.3,129.8,143.5C131.6,142,129.6,137.5,126.7,136.8C125.5,136.5,122.9,137.5,122.2,138.6C122,138.9,124.5,141.7,128.1,145.3C133.6,150.9,134.4,151.5,135.7,151.4C137.1,151.2,136.9,151.4,134,154.1C131,156.7,129.1,158,130.5,156.3ZM169.3,151.2C169.8,150.1,159.9,140.2,158.8,140.7C157.5,141.1,157.8,140.7,160.6,137.6C162.1,136,163.7,134.1,164.3,133.4L165.3,132L166.8,133.5C168,134.9,168.1,135,167.1,134.5C165.8,133.9,165.1,134.2,163.3,136.3L162,137.8L164.3,140L166.5,142.2L167.8,141C168.8,140,169,139.4,168.8,138.3L168.5,136.8L170.3,138.5C172.1,140.2,172.1,140.3,170.8,140.4C170.2,140.5,169.1,141.1,168.5,141.8L167.3,143L169.5,145.3L171.7,147.5L173.1,146.2C174.7,144.6,175.3,142.1,174.5,140.6C173.5,138.6,174.7,139.5,175.9,141.5L177,143.4L173.2,147.7C169.3,152.1,168.8,152.6,169.3,151.2ZM212.4,145.9C213.5,144.5,212.7,143.4,205.7,136.5C202,132.9,199,129.8,199.1,129.8C199.2,129.7,203,129.1,207.6,128.4C212.3,127.7,216.2,127,216.4,126.8C216.8,126.3,208.9,118.5,208,118.5C207.6,118.5,206.9,118.8,206.5,119.1C205.7,119.9,205.8,119.7,208.5,115.1C210.9,111,210.9,111,210.6,112.9C210.5,114.1,211.3,115.1,217.5,121.2C221.3,125,224.4,128.2,224.4,128.3C224.3,128.4,220.4,129.1,215.6,129.8C210.9,130.6,207,131.4,207,131.5C207,131.7,208.9,133.7,211.3,136C215.1,139.9,215.6,140.2,216.5,139.6C217.7,138.8,214.6,144.1,212.9,145.8C212.2,146.3,212.1,146.4,212.4,145.9ZM147.5,141.1C148.4,139.3,136,126.8,133.8,127.2C132.5,127.5,132.8,127.1,136.6,123.1C138.9,120.7,141.2,118.1,141.8,117.4L142.8,116L144.5,117.8C145.8,119.1,146,119.5,145.2,119.3C143.1,118.5,142.3,118.8,140.2,121.3L138.4,123.6L141,126.3C144.2,129.5,144.5,129.5,146.3,127.4C147.3,126.1,147.5,125.5,147.3,124L147,122.3L149.2,124.5C151.3,126.6,152.4,128.4,150.8,127C149.7,126.1,148.2,126.8,146.7,128.8L145.3,130.6L148.1,133.4C151.2,136.5,151.3,136.5,153.7,133.1C155.1,131,155.4,129.7,154.8,127C154.4,125.3,154.4,125.3,156,127.6C156.8,129,157.5,130.3,157.5,130.6C157.5,131,155.1,133.7,152.2,136.8C149.3,139.8,147.2,141.7,147.5,141.1ZM178.8,140C179,138.9,178.2,137.9,173.8,133.5C170.9,130.7,168.5,128.3,168.6,128.2C168.7,128.2,171.3,127.8,174.5,127.5C177.7,127.2,180.9,126.8,181.7,126.7L183.1,126.4L179.9,123.2C177.8,121,176.5,120,175.9,120.2C174.7,120.5,174.7,120.4,176.8,117.5C178.8,114.7,179,114.5,178.6,116.1C178.4,116.9,179.5,118.2,183.9,122.6C187,125.6,189.2,128,188.8,128C187.5,128,175.3,129.6,175.1,129.7C174.7,130.2,181.2,135.7,182.1,135.7C183.1,135.7,182.8,136.4,180.3,139.2C178.8,141,178.6,141.1,178.8,140ZM92.7,136.2L94.6,134.8L91,133.1C89,132.2,87.3,131.6,87.2,131.7C86.7,132.1,89.7,138.4,90.2,138.1C90.5,137.9,91.6,137,92.7,136.2ZM108.8,131.4C108.7,131,107.4,126.3,106,120.8C104.6,115.3,103.4,110.7,103.3,110.6C102.8,110.2,97,115.7,96.7,116.9C96.5,117.5,96.7,118.4,97,118.8C98.3,120.3,97.1,119.9,95.5,118.2L93.8,116.5L95.5,115.1C96.5,114.4,99.4,111.7,102,109.2L106.8,104.5L109.7,115.6C111.3,121.7,112.7,126.8,112.8,127C112.9,127.1,114.4,125.9,116.1,124.2L119.1,121.2L118.9,118.4C118.8,116.8,118.7,115.5,118.8,115.5C118.8,115.5,119.6,116.6,120.5,118C121.9,120.2,122,120.7,121.4,121.2C117.1,125.4,109.7,132,109.4,132C109.1,132,108.9,131.7,108.8,131.4ZM160.8,126.4C161.3,124.3,149.6,112.3,147.4,112.6C146.5,112.8,146.2,112.6,146.4,112.3C146.7,112,148.4,109.8,150.2,107.5C152.1,105.2,153.9,102.7,154.1,102.1C154.8,100.6,155,100.7,157,102.7L158.7,104.5L157.1,103.9C155.3,103.2,154.8,103.5,152.7,106.5L151.3,108.6L154.3,111.6L157.3,114.6L158.7,112.5C159.8,111,160.1,110.2,159.8,109C159.4,107.1,159.4,107.1,162,109.8C164.1,111.9,164.1,112,162.9,111.7C161.9,111.5,161.3,111.7,160.1,113.1C158,115.3,158,115.3,161.2,118.5L164,121.2L165.1,119.8C167.5,116.7,168.2,113.8,167,111.4C166.7,110.9,166.5,110.3,166.6,110.2C167,109.8,170.1,115.1,169.8,115.7C169.1,117.4,160.5,127.6,160.8,126.4ZM191.3,123.5C191.7,122,182.2,112.3,180.8,112.8C179.3,113.4,185.8,104.1,187.7,103C193,99.7,199.4,106.8,197.5,113.9C197.2,115,195.8,117.7,194.4,119.8C191.5,124.4,191,125,191.3,123.5ZM195,117C196.8,113.5,196,110.5,192.5,107.3C189.1,104.3,186.8,104.5,184.6,108.1L183.8,109.5L188.5,114.2C191.1,116.9,193.4,119,193.6,119C193.8,119,194.4,118.1,195,117ZM120.5,112C112.8,108,111.8,99.1,118.1,92.2C120.4,89.6,124.4,87.5,126.8,87.5C130.3,87.5,134.8,90.9,136.6,94.7C140.9,104.4,129.4,116.7,120.5,112ZM132.3,107.9C135.6,104.8,135.2,100.4,131,95.8C125.9,90.3,120.4,90,117.7,95C116.2,98.1,117.1,101.5,120.6,105.3C124.8,109.8,129.1,110.8,132.3,107.9ZM201.7,107.2C202.1,106,192,95.6,191,96.2C190.7,96.5,191.3,95,192.5,92.9C193.6,90.9,194.8,88.6,195.1,87.9C195.7,86.2,196,86.2,197.4,87.7L198.6,88.9L197.3,88.6C196.2,88.4,195.9,88.7,194.9,90.7L193.8,93L196,95.3L198.2,97.5L199.2,96C199.8,95,200,93.9,199.9,93L199.7,91.5L201.3,93.1C203.2,94.9,203.3,95.1,202.2,94.9C201.7,94.8,200.8,95.4,200.1,96.5L199,98.2L201.1,100.3C203.6,102.9,203.6,102.9,204.7,100.7C205.6,98.7,205.8,95.9,205,94.5C204.1,92.8,204.9,93.4,206.3,95.5L207.6,97.5L205.2,101.9C202.4,107,201.2,108.9,201.7,107.2ZM179.9,99.5C180,98.8,179.3,97.7,177.1,95.4L174.2,92.3L169,91.4C164.3,90.6,163.7,90.6,162.7,91.3C161.1,92.3,161.2,92.1,164,87.7C165.4,85.6,166.5,84.2,166.5,84.6C166.5,85.8,167.6,86.4,171.3,87.2C173.2,87.6,174.8,87.9,174.8,87.8C175.3,87.3,171.5,78.3,170.8,78.3C170.3,78.4,170,78.3,170.1,78.1C170.8,76.6,173,72.5,173.2,72.5C173.2,72.5,173.2,73.3,173.1,74.2C173,75.3,173.6,78.3,174.8,82.2C176.6,88.2,176.7,88.5,179.6,91.3C182.3,94,182.6,94.2,183.6,93.6C184.8,92.8,184.9,92.6,181.9,97.4C179.8,100.7,179.4,101.1,179.9,99.5ZM143.4,95.2C143.6,94.1,142.7,93,136.2,86.5C129.5,79.7,128.9,79,130,78.8C130.7,78.7,134.4,78.2,138.2,77.8C142.1,77.3,146,76.8,146.9,76.7L148.6,76.4L144.2,71.9C139.8,67.5,138.8,67,137.5,68.1C136.8,68.8,137.6,67.4,140.4,63.1C142.4,60.1,142.4,60.1,142.1,61.9C141.9,63.1,142.8,64.1,149.4,70.6C155.5,76.6,156.6,78,155.8,78.2C155.3,78.3,150.9,78.9,146.2,79.6C141.4,80.2,137.5,80.8,137.5,81C137.5,81.5,146.3,90,146.9,90C147.2,90,147.6,89.8,147.8,89.5C147.9,89.2,148.3,89,148.5,89C148.8,89,147.8,90.7,146.2,92.8C143,97,143.1,97,143.4,95.2ZM209,93.2C209,92,199.3,82.2,198.6,82.7C197.8,83.2,197.8,83.1,199.9,78.4C202.2,73.7,202.9,72.9,204.8,73.1C206.5,73.3,208,75.3,208.3,77.8L208.6,79.8L211.7,79.7C213.7,79.7,215.2,79.4,215.8,79C216.4,78.4,216.3,78.9,215.3,80.9C213.8,83.9,212.6,84.4,209.3,83.5C208.1,83.2,206.9,83,206.6,83.2C206.3,83.4,206.2,82.8,206.4,81.5C206.9,77.7,202.8,74.6,201.4,77.8C200.8,79,201.1,79.3,205.8,84C209.6,87.8,210.8,88.8,211.3,88.4C212.3,87.6,212.1,88.5,210.5,91.5C209.6,93.2,209,93.9,209,93.2ZM186.3,89.3C186.5,88.3,181.9,77,177.5,67.8C176.9,66.7,176.5,65.8,176.5,65.7C176.5,65.6,178.2,66.1,180.4,66.8C192.7,70.7,195,71.2,196,70.6C196.8,70.1,196.5,70.9,194.9,74.3C193.4,77.3,192.7,78.3,192.6,77.5C192.4,76.4,189.8,74.8,188.8,75.2C188.5,75.3,187.6,76.7,186.8,78.2L185.3,81L186.4,83.2C187,84.5,187.8,85.5,188.2,85.5C189.2,85.5,189.2,85.5,187.7,88.2C186.3,90.7,185.8,91.1,186.3,89.3ZM186,77C186.5,75.9,187,74.8,187,74.6C187,74.2,182,72,181.2,72C180.9,72,184.3,79,184.6,79C184.8,79,185.4,78.1,186,77ZM162.2,60.9L160.4,58.3L159.2,59.7C157.8,61.6,157.1,60.5,158.3,58.3L159.1,56.6L157.5,54.2C155.8,51.8,155.2,51.8,154.3,54.2C154.2,54.6,154,54.6,153.5,54.2C153,53.7,153.1,53.3,153.8,52C154.6,50.4,154.6,50.4,152.7,47.7C151.5,46,151,44.7,151.2,44.2C151.5,43.5,151.9,43.8,153.3,45.8C155.5,48.9,155.6,48.9,156.7,46.5L157.6,44.6L155.8,42.1C154.1,39.8,153.4,37.7,154.5,38.1C154.7,38.2,155.7,39.4,156.7,40.7L158.4,43.2L159.1,41.8C159.4,41.1,159.8,40.5,160.1,40.5C160.9,40.5,161,41.6,160.2,43C159.5,44.5,159.5,44.6,161.1,46.8C162.9,49.4,163.1,49.4,163.9,47.7C164.4,46.7,164.7,46.5,165.1,46.9C165.5,47.3,165.5,47.9,164.9,49.2L164.1,50.9L166,53.7C168,56.5,168.3,57.2,167.6,57.7C167.3,57.9,166.3,56.7,165.2,55.2C163.2,52.3,163.2,52.3,161.7,55.1C161,56.5,161.1,56.6,163.1,59.5C164.6,61.6,165,62.7,164.7,63C164.3,63.4,163.6,62.8,162.2,60.9ZM161.5,52.5C162,51.2,162,50.8,161,49.3C159.2,46.3,158.7,46.2,157.6,48.4L156.6,50.3L158.2,52.6C159.9,55.1,160.4,55.1,161.5,52.5ZM172.9,46.5C173.1,45.1,172.6,44.4,167.4,39.2C160.8,32.5,160.1,32,159.1,33.3C158.4,34.2,158.4,34.2,158.7,33.2C158.9,32.7,159.3,30.8,159.7,29.1C160,27.4,160.3,26,160.5,26C160.6,26,164,29.3,168,33.2C174.8,40,175.3,40.4,176.1,39.7C176.7,39.1,176.5,40.1,175.2,43.1C173.1,47.8,172.6,48.7,172.9,46.5Z'/%3E%3C/svg%3E") 0 0 / 100% 100% no-repeat;
}
.apg-seal-cover { fill: var(--apg-accent, #FFA200); }
.apg-seal-edges { fill: color-mix(in srgb, var(--apg-accent, #FFA200) 65%, var(--apg-parchment, #F4F7FA)); }
.apg-seal-pages { fill: var(--apg-parchment, #F4F7FA); }

.apg-book-cover .apg-seal { width: clamp(118px, 30%, 168px); right: 3%; top: 2%; }
.apg-moxxi-slot .apg-seal { width: clamp(112px, 30%, 155px); right: 0; top: 3%; }
.apg-books-grid li .apg-seal { width: clamp(104px, 25%, 142px); right: 3%; top: 2%; }

/* ===== 63. Accolade seal - neon rebuild from Website Badge.psd ===== */
/* Supersedes 62's colours, drop shadow and ::after lettering. Each surface now
   carries one self-contained <svg class="apg-seal"> with no wrapper: the author's
   magenta / amber / cream palette, the Cinzel Bold lettering (outlined from the
   TTF with the PSD's Arch -25 warp) and the neon glow (an SVG filter on the cover
   silhouette) all live inside the markup, so nothing in the CSS recolours it.
   Sizes, placement and z-index still come from 62; the art is pre-tilted, so no
   rotation here either. 62's ::after lettering never paints on an <svg> element,
   but it is switched off explicitly so a wrapper can never bring it back. */
.apg-seal { filter: none; overflow: visible; }
.apg-seal::after { content: none; }
/* Sizes, up from 62's 168 / 155 / 142 caps, for legibility: the box stays inside
   its host (right: 3% / 0 / 3%), so the text column's clearance is the grid gap. */
.apg-book-cover .apg-seal { width: clamp(128px, 32%, 190px); }
.apg-moxxi-slot .apg-seal { width: clamp(120px, 36%, 160px); }
.apg-books-grid li .apg-seal { width: clamp(110px, 38%, 150px); }


/* ===== 64. Accolade seal - theme colours, bottom-right, smaller ===== */
/* Shell, halo and rim stay brand pink via --apg-logo (#E92290 - the logo PNG is two
   thirds #E82490/#E41C8C; the badge PSD's #E34896 is its lighter half-tone). The page
   edges and face follow the active theme: edges accent -> accent 80% black, face
   accent 24% -> 42% over white, which on b2 reproduces the PSD's amber and cream. The
   gradient stops are addressed through the SVG's ids, so the markup keeps its b2
   attributes as the no-CSS fallback. */
.apg-seal-halo, .apg-seal-shell { fill: var(--apg-logo, #E92290); }
.apg-seal-rim { stroke: color-mix(in srgb, var(--apg-logo, #E92290) 62%, #fff); }
#apgSealEdge stop:first-child { stop-color: var(--apg-accent, #FFA200); }
#apgSealEdge stop:last-child  { stop-color: color-mix(in srgb, var(--apg-accent, #FFA200) 80%, #000); }
#apgSealFace stop:first-child { stop-color: color-mix(in srgb, var(--apg-accent, #FFA200) 24%, #fff); }
#apgSealFace stop:last-child  { stop-color: color-mix(in srgb, var(--apg-accent, #FFA200) 42%, #fff); }
/* Bottom-right of the book and ~13% smaller than 63. The book art sits at x 20-80%,
   y 12-88% of its file, so the offsets put the seal's centre just inside the book's
   bottom-right corner. On the index card the host is the li, whose group-mock figure
   is a square of the card's content width starting 43px down - hence container units:
   the seal is hung from the figure's bottom edge and pulled 60% back up over it. */
.apg-book-cover .apg-seal { width: clamp(96px, 27%, 165px); top: auto; bottom: 1.5%; right: 6.5%; }
.apg-moxxi-slot .apg-seal { width: clamp(96px, 32%, 140px); top: auto; bottom: -10%; right: 0; }
.apg-books-grid li { container-type: inline-size; }
.apg-books-grid li .apg-seal { width: clamp(96px, 33%, 130px); top: calc(100cqw + 7px); right: 8%; transform: translateY(-102%); }
/* Book page: the 600x750 mockup pads the book to x 20-80%, y 12-88%, and the author
   line ends at 67% of the box, 84-86% down. Homepage: image 3275 has no padding (book
   at x 12-88%, y 0-100%, author line ending at 68% at 94% down), so the seal hangs a
   tenth below the slot. The seal's silhouette is a tilted diamond whose left edge is
   inset 32% -> 13% of its box between 30% and 40% of its height; the offsets keep the
   lettering clear. Index card: the e-reader's corner sits at 73% of the mock square.
   Below 900px the book-page cover is 267px on a 393px phone and the 96px floor is 36%
   of it, so both seals are hung further off the corner. */
@media (max-width: 899px) {
  .apg-book-cover .apg-seal { right: 1%; bottom: -5%; }
  .apg-moxxi-slot .apg-seal { right: -3%; bottom: -10%; }
}

/* ===== 65. Accolade seal - 75% of 64, seated on the book's SE corner ===== */
/* Author's call, in two parts: every dimension in 64 multiplied by 0.75, then the
   seal set flush against the book's right and bottom edges and pulled back out along
   the 45-degree SE diagonal by a fraction of its own diagonal. A 45-degree move of k
   of the diagonal is exactly k of the side on each axis, so the pull is translate(k,
   k) - percentage translates are measured against the element's own box, so it tracks
   the seal at every size without a second set of numbers. The offsets are therefore
   the flush point and the transform is the overhang, and k is the only knob.
   k differs per surface because each one runs out of room somewhere else:
   - book page 80%: the seal's corner lands on the book's bottom-right corner and the
     author credit is fully clear. 68-73px still free to the text column at 1280-1440.
     100% (bbox corner exactly on the book's corner, the strict reading) reads as a
     detached floating object - checked, rejected.
   - homepage 20%: the book fills the slot top to bottom, so the pull goes straight
     into the "Pursuits of Moxxi: The Backrooms" heading below it. 20% leaves 20-22px;
     28% is the hard ceiling.
   - index card 55%: any further and the seal crosses the card's right border - 55%
     leaves 9px inside it, and 54-57px above the card title.
   Book art bounds are measured from the files' alpha channels, not from 64's notes:
   apg-moxxi-b1-backrooms-pb.webp (600x750) is opaque x 19.7-80.3%, y 12.1-88%.
   Sizes, offsets and the nudge only - colours and artwork stay with 63 and 64. */
.apg-book-cover .apg-seal { width: clamp(64px, 23.3%, 145px); bottom: 12%; right: 19.7%; transform: translate(45%, 25%); }
/* Homepage: the slot is a 4:5 box but pom-b1-paperback.webp is 800x1333 and sits in
   it object-fit:contain, so the file only spans the middle 75% of the box's width and
   all of its height. Its alpha runs x 4.1-95.9%, y 11.4-97.6%, which lands the book's
   own edges at x 15.6-84.4%, y 11.4-97.6% of the slot - hence right:15.6%/bottom:2.4%
   as this surface's flush point. Slot proportions hold at every width. */
.apg-moxxi-slot .apg-seal { width: clamp(79px, 26.4%, 116px); bottom: 2.4%; right: 15.6%; transform: translate(45%, 25%); }
/* Index card: the host is the li and the artwork is a <video class="apg-groupmock">
   in a square figure, so there is no alpha channel to measure and the flush point is
   the corner verified by eye - the seal's box on the e-reader's bottom-right. 64's
   anchor is kept (top:100cqw+7px, which lands on the figure's bottom edge because the
   figure is inset in the li) and only the pull-up changes: -196% becomes -186%, i.e.
   the same 10%-of-a-side step down, with translate's X carrying the other 10%. */
.apg-books-grid li .apg-seal { display: none; }

/* No breakpoint overrides: offsets and pull are both proportional, so the seal lands
   on the same point of the artwork at every width. Measured from the real CSS at
   1280 and 393: book page right 90.8/91.1%, bottom 92.7/92.8%; homepage 96.3% and
   102.9% at both. 64's max-width:899px rules sit earlier in the sheet, so these win.
   The book-page floor drops 72px -> 64px: at 393px that cover is 265px wide and 72px
   made the seal 27% of it instead of the 23.3% it is everywhere else. The index
   card's seal is switched off at the author's request - the markup is still on page
   393, so this is one line to undo. */


/* ===== 66. Homepage sections - one centre axis ===== */
/* The author asked why the Moxxi block read differently from the Magdalena Basil one.
   It was never text-align: both are left. The Moxxi paragraph carries .apg-lede
   (max-width: 62ch = 535px) and the MBT one does not, so it ran the full 800px
   column. WordPress's constrained layout gives any block narrower than the column
   margin-inline: auto, so the 535px lede centred inside 800px and started 133px
   right of its own heading, while MBT's full-width paragraph started flush with its
   heading. Her call was to centre rather than to unpick the indent, which resolves it
   the other way: heading, lede and figure now share one centre axis, so the 133px is
   no longer a mismatch. The bare MBT paragraph gets the same 62ch measure so the two
   sections finally read alike - that difference was the original complaint.
   Scoped to body.home. The same .apg-section class is also on the books index (3),
   the blog index (1) and the book page (1); those are deliberately left alone. */
body.home .apg-section > :is(h1, h2, h3, h4) { text-align: center; }
/* Headings centre, body copy does not - centred body text was tried and read badly.
   Left, not justified: at a 62ch measure with three-line paragraphs, justify stretches
   only the non-final lines and there is no hyphenation here, so it opens visible gaps
   ("A modern-day grim reaper who wonders if there's more to life than death," was the
   worst of them) and the gaps widen as the column narrows. Both were compared at 1:1
   before choosing. The paragraph BLOCK stays centred and only its text is left, so the
   ragged edge still sits on the section's centre axis rather than off to one side. */
body.home .apg-section > p { text-align: left; }
body.home .apg-section > p.wp-block-paragraph:not([class*="apg-"]) { max-width: 62ch; margin-inline: auto; }
/* Not body copy, so these keep the centring they already had: the ARC date pill, the
   hero standfirst and the rotator's "Explore all books" link. */
body.home .apg-section > .apg-ce-badge { margin-inline: auto; text-align: center; }
body.home .apg-hero > p { text-align: center; }
body.home .apg-section > .apg-rotator-all { text-align: center; }
body.home .apg-section > .wp-block-buttons.apg-cta { justify-content: center; }
/* The Amazon bestseller screenshot is a 420px <img> sitting inline in an 800px figure,
   so it hung on the column's left edge. Centred both ways - text-align for the inline
   case, auto margins once it is a block - so it holds if the image block is rebuilt. */
body.home .apg-section > figure.apg-proof { text-align: center; }
body.home .apg-section > figure.apg-proof img { display: block; margin-inline: auto; }
/* The "Pursuits of Moxxi: The Backrooms" caption under the cover is an h3, so the
   heading rule above centres it - it now behaves like MBT's "Explore all books".
   The rotator cards keep their left-aligned copy: their titles are <strong>, not
   headings, so nothing here reaches them. */


/* ===== 67. Header menu - the caret inside the hover pill ===== */
/* Why Books -> About looked wider than every other pair: it was not. Every li sits
   2-3px from the next, Books included. But hello-elementor gives
   li.menu-item-has-children a padding-inline-end of 15px and an ::after caret set at
   font-size 1.5em, which measures 20.7px - and the site's hover pill is on
   #menu-primary a, so it stopped after the 69px link and left 35.7px of unlit space
   before About's pill began. The fix is to light the whole item: drop the theme
   padding, bring the caret down to text size, and move the pill from the <a> to the
   <li> so it wraps word and caret as one. The li stays lit while its dropdown is
   open, because the sub-menu is a child. Books' box goes 105px -> 93px and every
   gap in the bar is now 2-3px of genuinely dead space. */
#menu-primary > li.menu-item-has-children { padding-inline-end: 0; }
#menu-primary > li.menu-item-has-children::after {
  font-size: 1em; line-height: 1; padding-inline-end: 0.62rem;
  color: color-mix(in srgb, var(--apg-parchment) 70%, transparent);
}
#menu-primary > li { border-radius: 6px; transition: background-color .18s; }
#menu-primary > li:not(.apg-arc-cta):hover { background: color-mix(in srgb, var(--apg-accent) 14%, transparent); }
#menu-primary > li:not(.apg-arc-cta):hover > a { background: none; }
#menu-primary > li.menu-item-has-children:hover::after { color: var(--apg-accent-pale); }


/* ===== 68. Inner pages open like About and Contact ===== */
/* The site had two page shapes. About (57) and Contact (59) start with a bare h1 in
   .page-content, so their heading sits 72px under the header - main's 64px padding
   plus the h1's own margin. Books, Blog, Events, Quiz and the book page open inside a
   .apg-section, which added 88px of its own: 152px before anything appeared, and on
   the books index the 88px bottom of the intro section met the 88px top of the next,
   putting 176px between them and pushing the book cards to 618px - off the first
   screen entirely at 1280x900.
   Author's call: "the space doesn't serve any purpose." So the first section on an
   inner page loses its top padding, and the rhythm between sections comes down from
   88px to 56px. Books index after: heading 152 -> 64px, Magdalena Basil 449 -> 297,
   first card 618 -> 466 - on screen. Blog and Events open at 64px too, which is the
   same place About and Contact's headings sit.
   Scoped to body:not(.home) - the homepage is the portal gate and keeps its 88px.
   About and Contact are untouched either way: they have no .apg-section to match. */
body:not(.home) .page-content > .apg-section { padding-block: 56px; }
body:not(.home) .page-content > .apg-section:first-child { padding-top: 0; }


/* ===== 69. One measure for the unwrapped pages, centred book-index headings,
   and the book-page cover sticky again ===== */

/* (a) The other half of 68's two-shapes problem: About (57) and Contact (59) have no
   .apg-section, so their blocks took main's full 1140px while every wrapped page uses
   an 800px column. :has() picks them out by structure rather than by id, so a future
   unwrapped page inherits it too. main is itself centred, so an 800px child lands on
   exactly the same left edge - 393 at a 1585px viewport - as the sections do.
   The author photo does NOT shrink: .apg-author is minmax(180px, 260px) + minmax(0, 1fr),
   so at 800px the photo column stays at its 260px cap and only the bio column narrows.
   The lede goes flush left instead of centring itself in the narrower column - the same
   133px indent 66 dealt with on the homepage. */
body:not(.home) .page-content:not(:has(> .apg-section)) > * { max-width: 800px; margin-inline: auto; }
body:not(.home) .page-content:not(:has(> .apg-section)) > .apg-lede { margin-inline: 0; }

/* (b) Books index gets the homepage treatment from 66: headings centred, body copy
   left inside its own centred block. Individual book pages are deliberately left
   alone - their title sits in the right-hand column of a two-column layout beside the
   cover, where centring reads as a mistake rather than a choice. */
body.page-id-393 .apg-section > :is(h1, h2, h3, h4) { text-align: center; }
body.page-id-393 .apg-section > p { text-align: left; }

/* (c) The cover on an individual book page used to stick under the header and stopped
   when the badge work added `.apg-book-cover, .apg-moxxi-slot, .apg-books-grid li
   { position: relative }` to give the seal a positioning context: same specificity,
   later in the sheet, so relative beat the sticky declared up in the 900px layout
   block. A sticky element is a positioned element, so it holds the seal just as well
   and the cover travels with the blurb again. */
@media (min-width: 900px) {
  body.parent-pageid-393 .apg-book-cover {
    position: sticky;
    top: calc(var(--wp-admin--admin-bar--height, 0px) + 76px);
  }
}


/* ===== 70. Accolade seal on the book page's group mock ===== */
/* Page 3274's cover changed from the 600x750 paperback to the group-mock video, so
   65's book-page geometry - measured against apg-moxxi-b1-backrooms-pb.webp, opaque
   x 19.7-80.3%, y 12.1-88% - no longer describes what is under the seal, and 65's
   .apg-book-cover .apg-seal rule is superseded here (it still governs nothing else:
   3274 is the only book page carrying a seal).
   New reference, measured the same way from the mock's own poster,
   apg-groupmock-m1-the-backrooms.webp (900x900, only 21.8% opaque): the books occupy
   x 24.6-75.4%, y 24.2-70.7% of the square. So the flush point is right: 24.6% /
   bottom: 29.3%, and translate(10%, 10%) hangs it off that corner by a tenth of the
   seal's own side - the same construction as everywhere else.
   At 1280 the cover renders 620x620 and the seal 136px, sitting x 55.6-77.6%,
   y 50.9-72.9% - on the front paperback's lower right, just clear of the e-reader.
   The mock rotates, so these are the poster's bounds, not every frame's. */
body.parent-pageid-393 .apg-book-cover.apg-groupmock-wrap .apg-seal {
  width: clamp(64px, 22%, 150px);
  right: 24.6%;
  bottom: 29.3%;
  transform: translate(10%, 10%);
}


/* ===== 71. Book pages: seal off, and a cover the blurb can outrun ===== */
/* Author's call - the seal now shows on the homepage only. 70's geometry is kept
   above rather than deleted: it is the measured placement for the group mock, so
   turning the seal back on is one line rather than a re-measure. */
body.parent-pageid-393 .apg-book-cover .apg-seal { display: none; }

/* "MBT sits until the very end while POM doesn't travel to the end of text."
   Correct, and it is arithmetic rather than a bug: a sticky element can only travel
   (containing block height - its own height), and the cover's containing block is its
   grid area, which is the height of the blurb column. At 1440 with the 620px cover:
   Legacy Academy's column is 864px, so 244px of travel; The Backrooms' column is only
   723px, so 103px - it unpins almost as soon as it pins. The covers are identical
   square mocks; the difference is entirely that the Backrooms blurb is shorter, and
   the blurb copy is not ours to lengthen.
   So the lever is the cover. Swept it: 620 gave 244/103, 520 gave 287/173, 470 gave
   307/223 - better, but the author could still see the difference. 440 is the value
   where the two converge, because a narrower cover widens the blurb column enough for
   the longer Legacy Academy text to re-wrap shorter and meet the Moxxi pages:

     at 1440   Legacy Academy 248 of 484 scroll (51%)
               The Backrooms  253 of 489 (52%)
               Camp Epiphany  243 of 479 (51%)

   At 1280 Legacy Academy pulls back ahead (63% against 52%) because its longer blurb
   wraps to more lines in a narrower column - that gap cannot be closed from CSS while
   the blurbs differ in length, and the copy is not ours to change. 51 vs 52 at the
   common desktop width, against 42 vs 23 before any of this, is where it lands.
   Scoped above 1180px so the existing 46% and stacked rules keep their behaviour. */
@media (min-width: 1181px) {
  body.parent-pageid-393 .apg-book { grid-template-columns: minmax(0, 440px) minmax(0, 1fr); }
}


/* ===== 72. Books index: centre a two-card row ===== */
/* .apg-books-grid is repeat(auto-fill, minmax(240px, 1fr)) in a 1040px box, so at
   desktop it lays three tracks. Magdalena Basil fills them; Pursuits of Moxxi has two
   books, and auto-fill KEEPS the empty third track - so those two cards sat hard left
   with a 325px hole beside them while the section's heading and lede were centred.
   auto-fit would collapse the empty track, but with a 1fr max the two cards would then
   stretch to ~504px and stop matching the Magdalena Basil cards. Constraining the
   container instead keeps the tracks identical: for a container W and gap g, a
   three-up track is (W - 2g)/3, so two of them plus a gap is (2W - g)/3 - which is
   what the max-width computes, capped at the 1040px box's own 682px. The grid already
   has margin-inline auto, so a narrower box centres itself.
   :has() picks the two-card grid by structure - the second li is also the last - so
   nothing here fires if a third Moxxi book is added; it simply fills the row again.
   Measured against the Magdalena Basil row: cards 325 vs 325 at 1440, 307 vs 308 at
   1024, 274 vs 275 at 920, and the grid's centre lands on the heading's centre to
   within a pixel at every width. Above 900px only - below that both rows stack
   full-width and there is nothing to centre. */
@media (min-width: 900px) {
  .apg-books-grid:has(> li:nth-child(2):last-child) {
    max-width: min(682px, calc((2 * 100% - 2rem) / 3));
  }
}


/* ===== 73. Group-mock still fallback ===== */
/* Companion to the apg-groupmock.php change: on devices whose decoder drops the VP9
   alpha plane and paints the loop on black (seen on the author's Android phone; iOS is
   fine, and Chrome's device emulation on a laptop cannot reproduce it because that uses
   the desktop software decoder), the loader replaces the <video> with an <img> of the
   poster it already carries. The replacement keeps the .apg-groupmock class so every
   existing size and object-fit rule still finds it; this is only a floor in case it
   ever lands somewhere none of those rules cover. */
.apg-groupmock-still { width: 100%; height: auto; display: block; }


/* ===== 74. Accolade line - the seal anchored to the text, not the artwork ===== */
/* The seal was re-placed four times because it was pinned to the cover art, and the
   art keeps moving: 3274's cover was swapped from a 600x750 paperback to a square
   group mock mid-session, and each change meant re-measuring alpha bounds and hoping
   it missed the author credit. Anchored to the text instead, one rule serves every
   surface and every future cover swap, and it can never cover a title or a credit.
   Base .apg-seal is position:absolute with a z-index and pointer-events:none, all of
   which have to be undone for an inline badge. */
.apg-accolade {
  display: flex;
  align-items: center;
  gap: .9rem;
  margin: 1.15rem 0 1.5rem;
  text-align: left;
}
.apg-accolade .apg-seal {
  position: static;
  z-index: auto;
  transform: none;
  flex: 0 0 auto;
  width: clamp(56px, 16%, 84px);
}
.apg-accolade-text {
  font-size: clamp(.9rem, 1.5vw, 1.02rem);
  line-height: 1.4;
  letter-spacing: .015em;
  color: var(--apg-accent-pale);
}
.apg-accolade-text strong { color: var(--apg-accent); font-weight: 700; }
/* Centred sections (homepage, books index) get the pair centred as a unit. */
body.home .apg-section > .apg-accolade,
body.page-id-393 .apg-section > .apg-accolade { justify-content: center; }
/* The Legacy Academy seal is built with its own id prefix so both badges can sit on
   one page without their gradients colliding. 64 themes the Moxxi one by id; these
   are the same four rules for the second prefix. */
#apgSealBEdge stop:first-child { stop-color: var(--apg-accent, #FFA200); }
#apgSealBEdge stop:last-child  { stop-color: color-mix(in srgb, var(--apg-accent, #FFA200) 80%, #000); }
#apgSealBFace stop:first-child { stop-color: color-mix(in srgb, var(--apg-accent, #FFA200) 24%, #fff); }
#apgSealBFace stop:last-child  { stop-color: color-mix(in srgb, var(--apg-accent, #FFA200) 42%, #fff); }

/* ─── 75 ── accolade inside the homepage trilogy card ─────────────────────────────
   The badge follows the book's own title, never the section heading. On the homepage
   the trilogy section is headed "The Magdalena Basil Trilogy", so an accolade sitting
   under it would read as a claim about all three books and would need a "Legacy
   Academy —" prefix to be honest. Legacy Academy already has its own titled card in
   .apg-rotator-books, so the badge goes there instead and no prefix is needed on any
   surface: one shortcode, no wording variants, four identical placements.

   Sized well down from block 74's default: the card is a navigation item, not a
   display panel, and the seal must not outweigh the book title above it. */

.apg-rotator-books .apg-accolade {
  margin: .7rem 0 0;
  gap: .55rem;
  align-items: center;
}

.apg-rotator-books .apg-accolade .apg-seal { width: clamp(34px, 26%, 46px); }

/* 0,2,0 beats a likely .apg-rotator-books li a span (0,1,3) on class count, so the
   caption keeps its own size and colour instead of inheriting the card blurb style. */
.apg-rotator-books .apg-accolade-text {
  font-size: clamp(.74rem, 1.05vw, .82rem);
  line-height: 1.35;
  letter-spacing: .01em;
}

/* ─── 76 ── two corrections after watching block 75 render ────────────────────────
   (a) The rotator card styles its own <strong> as the book title — block-level, at
       title size — and that selector (0,1,3) also catches the <strong>#2</strong>
       inside the accolade caption, so the number broke onto its own line at title
       size: "Amazon / #2 / in Free YA Myths & Legends". Reassert inline, own size.
   (b) The homepage Moxxi cover still carries the seal pinned to the artwork from
       block 65, and the accolade now repeats it under the title — the same badge
       twice on one screen, announced twice to a screen reader. The accolade is the
       survivor; the pinned copy goes. The SVG stays in page content for now (~9 KB,
       the same dead weight page 393 already carries) — one markup cleanup pass. */

.apg-rotator-books .apg-accolade-text strong {
  display: inline;
  font-size: inherit;
  line-height: inherit;
}

/* Element-qualified at both ends (0,2,3) so nothing earlier in this 217 KB sheet can
   out-specify it. The accolade seal never sits inside .apg-moxxi-slot, so this cannot
   reach the copy we are keeping.

   Note for whoever checks this next: the browser-pane accessibility tree still lists
   display:none SVGs, so "the seal appears twice in the a11y tree" is NOT evidence the
   rule failed - the same false positive shows on page 393, where block 65 has hidden
   the grid seals since August. Confirm this one by eye or by computed style. */

body.home figure.apg-moxxi-slot svg.apg-seal { display: none; }

/* ─── 78 ── spacing pass, 7 Sep ───────────────────────────────────────────────────
   Every number below was measured in the browser before it was written.

   (a) Homepage sections carried padding-block: 88px (the hero 113px on top), so each
       boundary was 176px of empty space. Inner pages have run at 56px since block 68
       and read better, so the homepage joins them. The Camp Epiphany band is already
       35/38 and keeps it.

   (b) The books index is classic (non-block) content, so wpautop turned every newline
       inside <li><a> into a <br>. Measured: five <br> per card at 21px each = 105px of
       dead space, most of it stacked above the cover. The Backrooms had SIX, because
       the hidden seal SVG sits on its own line - that sixth <br> is the extra gap under
       its title that no other card had. Remove them and restore real spacing as margins
       on the elements that should own it.

   (c) The quiz page prints its own h1 and lede, then Forminator prints the quiz's title
       and description again above the Start button. Hide Forminator's pair only; the
       page keeps its H1 and the Start button is untouched.

   (d) Block 69 gave .apg-lede margin-inline: 0 while everything else on those pages is
       centred in an 800px measure. Measured on /contact/: heading and form at x=553,
       the lede alone at x=383. Same axis as everything else now. */

body.home .apg-section { padding-block: 56px; }
body.home .apg-section.apg-hero { padding-block: 56px; }
body.home .apg-section.apg-ce { padding-block: 35px 38px; }

.apg-books-grid li a br { display: none; }
.apg-books-grid li a .apg-grid-mock { margin-bottom: 18px; }
.apg-books-grid li a strong { margin-bottom: 10px; }
.apg-books-grid li a .apg-book-order { margin-top: 16px; }

body.page-id-459 .forminator-quiz--title,
body.page-id-459 .forminator-quiz--description { display: none; }

body:not(.home) .page-content:not(:has(> .apg-section)) > .apg-lede { margin-inline: auto; }

/* ─── 79 ── books index: stop the empty mock canvas taking up layout ──────────────
   Measured from each poster's alpha at 360px resolution (threshold 6):
     b1/b2/b3 (Magdalena Basil)   art spans 21.67% -> 100%   vertically, full width
     m1/m2    (Pursuits of Moxxi) art spans 23.89% -> 70.83% vertically,
                                            24.44% -> 75.56% horizontally
   So every card carried a dead band above the covers, and the two Moxxi cards a
   second one of 29.17% below - that is the gap sitting between those covers and the
   title. Percentage margins resolve against the containing block's WIDTH and these
   figures are square, so -21.67% of width is exactly the band's height.

   Nothing is scaled or clipped. The artwork is untouched; only the transparent canvas
   around it stops occupying space.

   Matched on the asset slug in poster/src so the Android still fallback in
   apg-groupmock.php (<video> swapped for <img> when the hardware decoder drops VP9
   alpha) keeps the same geometry. */

.apg-books-grid li a .apg-grid-mock:has(video[poster*="apg-groupmock-b"], img[src*="apg-groupmock-b"]) {
  margin-top: -21.67%;
}

.apg-books-grid li a .apg-grid-mock:has(video[poster*="apg-groupmock-m"], img[src*="apg-groupmock-m"]) {
  margin-top: -23.89%;
  margin-bottom: calc(18px - 29.17%);
}

/* ─── 80 ── accolade as one piece: a mark, not a plate ────────────────────────────
   The seal carries its own engraved type, which only became legible around 84px -
   and 84px next to a single 16px line read as a slab bolted onto a sentence. The
   type was duplicated information anyway: the caption beside it says the same words,
   which is why the shortcode already hides the seal from screen readers.

   So the type comes off and the seal becomes a mark. Freed of the need to be
   readable it can drop to icon size, and the caption takes the weight instead:
   larger, italic, so the line reads as one object and sits apart from body copy.

   Only the type path is hidden - the shell, rim, edges and page faces still theme
   themselves from the custom properties in blocks 64 and 74. */

.apg-accolade .apg-seal .apg-seal-type { display: none; }

.apg-accolade { gap: .7rem; }
.apg-accolade .apg-seal { width: clamp(30px, 6.5%, 40px); }

.apg-accolade-text {
  font-size: clamp(1.02rem, 1.8vw, 1.22rem);
  font-style: italic;
  line-height: 1.35;
  letter-spacing: .012em;
}

/* Card variant: same relationship, one step down, so it stays a nav card. */
.apg-rotator-books .apg-accolade { gap: .5rem; }
.apg-rotator-books .apg-accolade .apg-seal { width: clamp(22px, 13%, 28px); }
.apg-rotator-books .apg-accolade-text { font-size: clamp(.86rem, 1.25vw, .95rem); }

/* ===== 78. Homepage Moxxi rotator, two centred cards, series-name rhythm ===== */
/* The Moxxi rotator (page 4557, section 4) is the trilogy rotator's markup with the
   apg-rotator-moxxi hook and its own poster fallback. Its two-card list reuses
   .apg-rotator-books; this is block 72's structural :has() trick scaled to that list:
   a three-up track is (780px - 2rem) / 3, so two tracks plus one gap is
   (2 * 780px - 1rem) / 3 = 514.67px, and the list's margin-inline auto centres the
   narrower box. Wrapped in min-width so the existing <=720px one-column rule (lower
   specificity) still wins on phones.
   Rhythm: homepage and books-index sections are padding-block 56px, so two stacked
   paddings put 113px above each series name. A section that follows another and opens
   with an h2 drops its top padding - one 56px unit above the series names; the hero H1
   and the books-index intro (h1-first) keep theirs. */
.apg-rotator-moxxi.is-fallback { background-image: url("/wp-content/uploads/2026/09/apg-moxxi-poster.webp"); }
@media (min-width: 721px) {
  .apg-rotator-books:has(> li:nth-child(2):last-child) { grid-template-columns: repeat(2, minmax(0, 1fr)); max-width: calc((2 * 780px - 1rem) / 3); }
}
body:is(.home, .page-id-393) .page-content > .apg-section + .apg-section:has(> h2:first-child) { padding-top: 0; }

/* ===== 79. Accolade line on the books-index cards ===== */
/* [apg_accolade] now sits inside the Legacy Academy and Backrooms cards on page 393, between
   the blurb and the "Book one" label, the same as the homepage rotator cards. Three grid rules
   would otherwise wreck it: .apg-books-grid span (0,1,1) restyles the caption, .apg-books-grid
   strong (0,1,1) turns the "#1" block-level at title size, and block 65's
   .apg-books-grid li .apg-seal { display: none } (0,2,1) plus its pinned-seal geometry hide and
   throw the small seal. Everything here is 0,2,x or 0,3,x so it wins on specificity, and the
   sizes are block 77's card variant. */
.apg-books-grid .apg-accolade { margin: .7rem 0 0; gap: .5rem; align-items: center; }
.apg-books-grid li .apg-accolade .apg-seal { display: block; position: static; top: auto; right: auto; transform: none; width: clamp(22px, 13%, 28px); }
.apg-books-grid .apg-accolade-text { font-size: clamp(.86rem, 1.25vw, .95rem); line-height: 1.35; letter-spacing: .01em; color: var(--apg-accent-pale); }
.apg-books-grid .apg-accolade-text strong { display: inline; font-size: inherit; line-height: inherit; margin: 0; }

/* ===== 80. Performance and contrast pass ===== */

/* 80a — explicit dark page ground.
   The dark behind the site is painted by .apg-bg on the homepage (position:
   fixed, background-IMAGE) and by the pooled-light pseudo-elements elsewhere.
   Neither is a background-COLOR, so axe resolved the ground to body's white
   and reported the homepage Camp Epiphany h2 and .apg-lede as contrast
   failures. Declaring the void makes the real ground computable — the h2's
   #FFA200 on #0A0B0E is 9.8:1 and the lede is 14:1, both already passing.
   Also removes the white flash before apg-bg-b2.webp arrives.
   Additive only: .apg-bg and the pseudo-layers still paint over it. */
body { background-color: #0A0B0E; }

/* 80b — gate logo mask, 985 KB to 94 KB.
   /2024/07/Logo-website.png is 1200x1200 and renders at 253px. mask-mode is
   match-source, so only its alpha channel is ever used. Replacement is a
   640x640 alpha WebP (attachment 5246), the original drawn to a canvas with
   RGB flattened to white so the lossy layer carries nothing. Verified
   identical at render size. Only the image is overridden — mask-size,
   -repeat and -position stay with the original rule.
   The 1200px original stays in the library; deleting this rule reverts.
   PUSH NOTE: media does not ride the database sync. apg-portal-logo-mask.webp
   must be uploaded to production or the gate logo masks to nothing. */
.apg-portal-logo {
  -webkit-mask-image: url(/wp-content/uploads/2026/09/apg-portal-logo-mask.webp);
  mask-image: url(/wp-content/uploads/2026/09/apg-portal-logo-mask.webp);
}

/* ==========================================================================
   81. PERFORMANCE PASS TWO — font subset, background weight, mobile sizing
   8 Sep 2026. Every number measured on production after the pass-one push.
   Baseline PSI mobile 62: FCP 4.5s, LCP 7.4s, SI 5.9s, TBT 0ms, CLS 0.
   ========================================================================== */

/* 81a · APG Cardo was the whole 143 KB WooCommerce face — every subset in one
   file. Self-hosted subsets of the same typeface, built from Google Fonts'
   Cardo-Regular.ttf and SFTP'd to /wp-content/uploads/fonts/: latin is
   15,516 B and covers every character the site renders; latin-ext (31,316 B)
   is fetched only if an extended-Latin character ever appears.
   Verified metrically identical to the WooCommerce file — 84 characters
   measured, zero advance-width mismatches, so nothing reflows.
   The WooCommerce file is untouched on disk — deleting 81a reverts. */
/* The latin face itself is NOT declared here — the original "APG Cardo" rule
   further up the sheet was repointed at the subset in place, because a
   full-range @font-face cannot be shadowed from a later block (tried twice;
   Chrome fetched both files). See the note beside that rule. All 81a adds is
   latin-ext, so an accented character still gets real Cardo instead of
   falling through to Georgia. It is fetched only if such a character
   appears, which on this site is close to never. */
@font-face {
  font-family: "APG Cardo";
  src: url("/wp-content/uploads/fonts/apg-cardo-400-latin-ext.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* 81b · The b2 backdrop shipped at 1920 wide and 135 KB. It is a fixed,
   dimmed, background-size:cover layer behind every page, never read as
   detail. Photon at 1600 / q65 measures 75 KB, with a mean per-channel
   difference of 2.03/255 against the original rendered at 1440 CSS px —
   invisible. b1 27 KB -> 18 KB, b3 46 KB -> 31 KB. Same masters, same
   filenames: the query string is the whole change, so deleting 81b reverts.
   Selectors mirror blocks 1, 2 and 31 exactly — one per theme, identical
   specificity, later in the sheet. */
:root,
body:has(#apg-t2:checked),
html[data-apg-theme="b2"] {
  --apg-bg-img: url("/wp-content/uploads/2026/08/apg-bg-b2.webp?w=1600&quality=65");
}
body:has(#apg-t1:checked),
html[data-apg-theme="b1"] {
  --apg-bg-img: url("/wp-content/uploads/2026/08/apg-bg-b1.webp?w=1600&quality=65");
}
body:has(#apg-t3:checked),
html[data-apg-theme="b3"] {
  --apg-bg-img: url("/wp-content/uploads/2026/08/apg-bg-b3.webp?w=1600&quality=65");
}

/* ⚠️ 81c AND apg-perf.php MUST STAY IN SYNC.
   A preload only matches the CSS request when the URL is byte-identical.
   apg-perf.php v1.1 (8 Sep 2026) emits two portal preloads:
     media="(max-width: 600px)"  -> apg-portal-b2.webp?w=560
     media="(min-width: 601px)" -> apg-portal-b2.webp
   If 81c's portal width ever changes, change the mu-plugin in the same pass,
   or a phone downloads both files -- 347 KB instead of 207 KB, worse than
   doing nothing at all. The backdrop and the mask are discovered from CSS and
   are not preloaded, so their 81c rules have no counterpart to keep in step. */

/* 81c · Mobile-only CDN sizing. Measured on a 492px-wide viewport with the
   gate locked: .apg-portal renders 226x226 and .apg-portal-logo 87x87, so at
   412-393px (what Lighthouse mobile emulates) the portal box is ~180px and
   the logo box ~70px. The files shipped are 740x900 and 640x640 — 4x and 7x
   oversized on a phone. These widths still cover dpr 3:
     portal  w=560  207 KB -> 140 KB   (560 / 180 = 3.1x)
     mask    w=320   94 KB ->  29 KB   (320 /  70 = 4.6x)
     backdrop w=1280 135 KB ->  57 KB  (full-bleed, dimmed, never read as detail)
   Scoped to 600px so tablets and desktops keep the full-resolution files.
   Query strings only — the masters are untouched, deleting 81c reverts. */
@media (max-width: 600px) {
  :root,
  body:has(#apg-t2:checked),
  html[data-apg-theme="b2"] {
    --apg-portal-img: url("/wp-content/uploads/2026/08/apg-portal-b2.webp?w=560");
    --apg-bg-img: url("/wp-content/uploads/2026/08/apg-bg-b2.webp?w=1280&quality=65");
  }
  body:has(#apg-t1:checked),
  html[data-apg-theme="b1"] {
    --apg-portal-img: url("/wp-content/uploads/2026/08/apg-portal-b1.webp?w=560");
    --apg-bg-img: url("/wp-content/uploads/2026/08/apg-bg-b1.webp?w=1280&quality=65");
  }
  body:has(#apg-t3:checked),
  html[data-apg-theme="b3"] {
    --apg-portal-img: url("/wp-content/uploads/2026/08/apg-portal-b3.webp?w=560");
    --apg-bg-img: url("/wp-content/uploads/2026/08/apg-bg-b3.webp?w=1280&quality=65");
  }
  .apg-portal-logo {
    -webkit-mask-image: url("/wp-content/uploads/2026/09/apg-portal-logo-mask.webp?w=320");
    mask-image: url("/wp-content/uploads/2026/09/apg-portal-logo-mask.webp?w=320");
  }
}

/* ==========================================================================
   82. WITHDRAWN — 8 Sep 2026
   Tested on the author's Android phone and it changed nothing, so the
   hardware-overlay theory is dead along with the rule that tested it.
   Kept as a tombstone so nobody re-adds opacity:0.999 thinking it is untried.
   See claude/groupmock-android-alpha-elimination-log.md
   ========================================================================== */
/* WITHDRAWN, deliberately inert:

/* Android composites video in a hardware overlay plane where it can, and an
   overlay plane has no alpha: the channel is punched through as opaque black.
   It is the COMPOSITOR discarding it, not the decoder — which is why every
   file-level theory failed. Proven by elimination on the author's phone:
   the identical apg-groupmock-b1-legacy-academy.webm plays with its alpha
   intact on a bare test page and goes black on the books index, same device,
   same session. Encoder, bitrate, dimensions, alt-ref and toolchain were all
   measured identical between the loops that fail and the ones that do not.

   There are only a few overlay planes. The loops nearest the top of the
   document claim them and lose their alpha; the ones further down fall back to
   normal texture compositing and keep it. That is the whole MBT-black /
   Moxxi-fine split — DOM order, not content.

   A sub-1 opacity forces the compositor to treat the element as a texture with
   an alpha channel, which makes it ineligible for an overlay plane. 0.999 is
   not perceptible — one part in a thousand, below any display's quantisation.
   Cheaper and less invasive than a filter or a transform, and it does not
   create the blur risk that a 3D transform can.

   If this turns out not to be the fix, delete block 82; nothing depends on it. */
/* WITHDRAWN 8 Sep 2026 — tested on the author's Android phone, changed
   nothing, so the hardware-overlay theory is dead. The rule was
   `video.apg-groupmock, .apg-rotator-vid { opacity: 0.999 }`. Deleted rather
   than commented out: a comment wrapped around a live rule in this sheet did
   NOT stop it applying. Delete, never comment, in here.
   See claude/groupmock-android-alpha-elimination-log.md */
