/* ==========================================================================
   KEDESHA TEMPLES — v2 site stylesheet
   The eleven-route build described in 08-build/ (post Fable 5 review).

   Grammar (unchanged from the measured brand):
     · black ground, one warm panel colour (#262220), nothing else
     · Sorts Mill Goudy 24px/36px — large, quiet, long-measure
     · enormous vertical space; sections breathe rather than stack
     · NO borders, NO radii, NO shadows, NO gradients on type
     · media is full-bleed and heavily darkened; type sits over it

   Full-viewport behaviour implements 13-FULL-VIEWPORT-AND-RESPONSIVE-SPEC.md:
     · stages: min-height 100vh -> 100svh, own ground, contiguous stacking
     · no dvh anywhere in layout
     · safe-area insets on type, never on media
     · veils are scrims first, transitions second — the scrim survives
       prefers-reduced-motion (spec 13 §8)
   ========================================================================== */

@import url('tokens.css');

/* ------------------------------------------------------------------ fonts
   Self-hosted (universal brief §5, D-25): exactly four files, latin subset,
   woff2, 74 KB total, SIL OFL. Built by 02-design-system/tools/build-fonts.py
   — provenance and licences sit beside the files in assets/fonts/. The
   subset carries № and → (U+2116, U+2190–2199) because the site sets them
   in Montserrat as structural devices; Google's own latin subset omits them.
   Every page preloads all four (<link rel="preload" as="font" crossorigin>).
   font-display: swap — the fallback stack in tokens.css is metric-close. */
@font-face {
  font-family: 'Sorts Mill Goudy'; font-style: normal; font-weight: 400;
  font-display: swap; src: url('../assets/fonts/goudy-regular.woff2') format('woff2');
}
@font-face {
  font-family: 'Sorts Mill Goudy'; font-style: italic; font-weight: 400;
  font-display: swap; src: url('../assets/fonts/goudy-italic.woff2') format('woff2');
}
@font-face {
  font-family: 'Montserrat'; font-style: normal; font-weight: 300;
  font-display: swap; src: url('../assets/fonts/montserrat-300.woff2') format('woff2');
}
@font-face {
  font-family: 'Montserrat'; font-style: normal; font-weight: 400;
  font-display: swap; src: url('../assets/fonts/montserrat-400.woff2') format('woff2');
}
/* Metric-matched fallbacks (tokens.css puts them second in each stack): the
   local face is scaled to the web font's advance width and given its ascent
   and descent, so the swap from fallback to web font moves nothing — CLS 0
   from fonts, and swap stays swap. Values [derived] in tokens.css. */
@font-face {
  font-family: 'Goudy Fallback'; font-style: normal; font-weight: 400;
  src: local('Georgia');
  size-adjust: 96.81%; ascent-override: 96%; descent-override: 47.8%; line-gap-override: 0%;
}
@font-face {
  font-family: 'Goudy Fallback'; font-style: italic; font-weight: 400;
  src: local('Georgia Italic'), local('Georgia-Italic');
  size-adjust: 85.44%; ascent-override: 96%; descent-override: 47.8%; line-gap-override: 0%;
}
@font-face {
  font-family: 'Montserrat Fallback'; font-style: normal; font-weight: 300;
  src: local('Arial');
  size-adjust: 102.65%; ascent-override: 96.8%; descent-override: 25.1%; line-gap-override: 0%;
}
@font-face {
  font-family: 'Montserrat Fallback'; font-style: normal; font-weight: 400;
  src: local('Arial');
  size-adjust: 103.42%; ascent-override: 96.8%; descent-override: 25.1%; line-gap-override: 0%;
}

/* ---------------------------------------------------------------- reset */

*, *::before, *::after { box-sizing: border-box; }
html { scroll-behavior: smooth; }
body {
  margin: 0;
  background: var(--kt-ink-1000);
  color: var(--kt-blush-50);
  font-family: var(--kt-font-body);
  font-size: 1.5rem;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}
img, video, svg { display: block; max-width: 100%; }
a { color: inherit; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
}

/* ------------------------------------------------------------- structure */

.wrap        { width: 100%; max-width: 68rem; margin-inline: auto; padding-inline: 2rem; }
.wrap--text  { max-width: 44rem; }

.band        { padding-block: clamp(6rem, 14vh, 11rem); }
.band--tall  { padding-block: clamp(9rem, 22vh, 18rem); }
.band--tight { padding-block: clamp(3rem, 7vh, 6rem); }

/* ------------------------------------------------- the stage (spec 13 §1) */

.stage {
  position: relative;
  min-height: 82vh;
  min-height: 82svh;
  display: grid; place-items: center;
  overflow: hidden;
  background: var(--stage-ground, var(--kt-ink-1000)); /* rule 2: own ground */
}
.stage--full {
  min-height: 100vh;
  min-height: 100svh;
}
.stage__media {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  object-position: var(--focal-x, 50%) var(--focal-y, 50%);
}
/* The veil is a SCRIM first and a transition second. Its resting opacity is
   the contrast guarantee and persists under reduced motion (spec 13 §8). */
.stage__veil        { position: absolute; inset: 0; background: rgb(0 0 0 / .55); pointer-events: none; }
.stage__veil--heavy { background: rgb(0 0 0 / .72); }
.stage__veil--heaviest { background: rgb(0 0 0 / .78); }
/* T1 · the seam veil (M-LD-4, White Desert's occluding veil as black smoke):
   a 20vh band at the top and foot of a stage that fades to the ground, so two
   stages meet in black and the boundary reads as a veil rather than a cut.
   Static by construction — it is a scrim, so it persists under reduced motion
   (spec 13 §8) and costs nothing. Opt-in per stage: never on a T6 page, where
   the body ground is the gradient and a black band would break it. */
.stage--seam::before, .stage--seam::after {
  content: ''; position: absolute; left: 0; right: 0; z-index: 1;
  height: min(20vh, 12rem); pointer-events: none;
}
.stage--seam::before { top: 0;    background: linear-gradient(to bottom, var(--kt-ink-1000), transparent); }
.stage--seam::after  { bottom: 0; background: linear-gradient(to top,    var(--kt-ink-1000), transparent); }
.stage__body {
  position: relative; z-index: 2; text-align: center;
  padding-top:    max(2rem,   env(safe-area-inset-top));
  padding-right:  max(1.5rem, env(safe-area-inset-right));
  padding-bottom: max(2rem,   env(safe-area-inset-bottom));
  padding-left:   max(1.5rem, env(safe-area-inset-left));
  max-width: 52rem;
}

/* Short landscape (spec 13 §5) — the .display size for it sits after the base .display rule, below */
@media (max-height: 560px) and (orientation: landscape) {
  .stage__body {
    padding-top: max(1rem, env(safe-area-inset-top));
    padding-bottom: max(1rem, env(safe-area-inset-bottom));
  }
  .stage { min-height: 100vh; min-height: 100svh; height: auto; }
}

/* --------------------------------------------------------------- masthead */

.masthead {
  position: sticky; top: 0; z-index: 100;
  padding-top: max(0.75rem, env(safe-area-inset-top));
  padding-bottom: 1rem;
  text-align: center;
  background: color-mix(in srgb, #000 88%, transparent);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--kt-ink-600);
}
.masthead__word {
  display: inline-block;
  font-family: var(--kt-font-display);
  font-size: clamp(1.3rem, 3.4vw, 1.9rem);
  letter-spacing: .3em; text-indent: .3em;
  text-transform: uppercase;
  color: var(--kt-blush-50);
  text-decoration: none;
  font-weight: 400;
}
.nav { margin-top: 1rem; display: flex; justify-content: center; gap: 2.25rem; flex-wrap: wrap; }
.nav a {
  font-family: var(--kt-font-meta);
  font-size: 0.784rem; font-weight: 300;
  letter-spacing: .16em; text-transform: uppercase;
  text-decoration: none; color: var(--kt-blush-50);
  padding-bottom: .35rem;
  border-bottom: 1px solid transparent;
  transition: border-color var(--kt-dur-base) var(--kt-ease),
              opacity var(--kt-dur-base) var(--kt-ease);
  opacity: .82;
}
.nav a:hover { opacity: 1; }
.nav a[aria-current='page'] { opacity: 1; border-bottom-color: currentColor; }
/* Hover discipline (Geof, 18 Aug): while any item is hovered (or holds focus)
   the others darken and the one under the pointer lightens. */
.nav:hover > a, .nav:hover .nav__item > a,
.nav:focus-within > a, .nav:focus-within .nav__item > a { opacity: .38; }
.nav:hover > a:hover, .nav:hover .nav__item:hover > a,
.nav:focus-within > a:focus-visible, .nav:focus-within .nav__item:focus-within > a { opacity: 1; }
/* The Realms drop-down: hover (or focus) on REALMS lists the four realms; the
   panel sits on the masthead's own ground; the same darken/lighten discipline
   inside it. Touch: REALMS is a link to /realms, which lists them. */
.nav__item { position: relative; display: inline-flex; flex-direction: column; align-items: center; }
.nav__item > a { display: inline-block; }
.nav__sub {
  position: absolute; top: 100%; left: 50%; transform: translate(-50%, 0);
  margin: 0; padding: 1.1rem 1.75rem 1.25rem; list-style: none;
  display: grid; gap: .85rem; justify-items: center;
  background: color-mix(in srgb, #000 92%, transparent);
  backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
  opacity: 0; visibility: hidden; pointer-events: none;
  transition: opacity var(--kt-dur-base) var(--kt-ease), visibility 0s linear var(--kt-dur-base);
  z-index: 101;
}
.nav__sub::before { content: ''; position: absolute; left: 0; right: 0; top: -.75rem; height: .75rem; }  /* the reach across the gap */
.nav__item:hover .nav__sub, .nav__item:focus-within .nav__sub {
  opacity: 1; visibility: visible; pointer-events: auto; transition-delay: 0s;
}
.nav__sub li { margin: 0; }
.nav__sub a { white-space: nowrap; opacity: .82; }
.nav__sub:hover a { opacity: .38; }
.nav__sub a:hover, .nav__sub a:focus-visible { opacity: 1; }
@media (prefers-reduced-motion: reduce) { .nav__sub { transition: none; } }
@media (hover: none) { .nav__sub { display: none; } }   /* touch: no hover — REALMS goes to /realms */

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

h1, h2, h3 { font-weight: 400; margin: 0; font-family: var(--kt-font-display); line-height: 1.15; }

.display {
  font-size: clamp(2.4rem, 7vw, 5rem);
  line-height: 1.05; letter-spacing: .02em;
}
.lede {
  font-size: clamp(1.6rem, 3.4vw, 2.4rem);
  line-height: 1.5; font-style: italic;
  margin: 0; max-width: 22em; margin-inline: auto;
}
.epithet {
  font-style: italic;
  font-size: clamp(1.15rem, 2.2vw, 1.5rem);
  color: var(--kt-blush-300);
  margin: .75rem 0 0;
}
.question {
  font-style: italic;
  font-size: clamp(1.4rem, 2.6vw, 1.9rem);
  max-width: 20em; margin-inline: auto;
}
.body-text { font-size: 1.5rem; line-height: 1.5; max-width: 30em; }
.body-text p { margin: 0 0 1.5rem; }
.body-text p:last-child { margin-bottom: 0; }
.small { font-size: 1.2rem; line-height: 1.5; }

.meta {
  font-family: var(--kt-font-meta);
  font-size: 0.784rem; font-weight: 300;
  letter-spacing: .16em; text-transform: uppercase;
  line-height: 1.9; margin: 0;
  color: var(--kt-blush-300);
}
.numeral {
  font-family: var(--kt-font-meta);
  font-size: 0.9rem; font-weight: 300;
  letter-spacing: .1em;
  color: var(--kt-gold-500);
}

/* ------------------------------------------------------------------- CTA */

.cta {
  display: inline-block;
  font-family: var(--kt-font-meta);
  font-size: 0.784rem; font-weight: 400;
  letter-spacing: .2em; text-indent: .2em;
  text-transform: uppercase;
  color: var(--kt-blush-50);
  text-decoration: none;
  padding: 1.1rem 2.2rem;
  background: var(--kt-ink-800);
  transition: background var(--kt-dur-base) var(--kt-ease),
              color var(--kt-dur-base) var(--kt-ease);
}
.cta:hover { background: var(--kt-gold-500); color: var(--kt-ink-1000); }
.cta--quiet {
  background: none; padding: 0 0 .5rem;
  border-bottom: 1px solid transparent;
}
.cta--quiet:hover { background: none; color: var(--kt-blush-50); border-bottom-color: currentColor; }

:focus-visible { outline: 2px solid var(--kt-gold-300); outline-offset: 4px; }

/* ---------------------------------------------------------------- footer */

.footer {
  padding: clamp(3rem, 7vh, 6rem) 2rem
           max(3rem, env(safe-area-inset-bottom));
  text-align: center;
}
.footer .meta a { text-decoration: none; opacity: .82; }
.footer .meta a:hover { opacity: 1; text-decoration: underline; }
.realm-row { display: flex; justify-content: center; gap: 2.5rem; flex-wrap: wrap; margin-bottom: 2rem; }
.realm-row a {
  font-family: var(--kt-font-meta); font-size: .7rem; font-weight: 300;
  letter-spacing: .18em; text-transform: uppercase; text-decoration: none;
  color: var(--kt-blush-400);
}
.realm-row a:hover { color: var(--kt-blush-50); }

/* --------------------------------------------------------------- reveals */

.rise { opacity: 0; transform: translateY(14px);
        transition: opacity var(--kt-dur-slow) var(--kt-ease),
                    transform var(--kt-dur-slow) var(--kt-ease); }
.rise.is-in { opacity: 1; transform: none; }
/* Dante's inversion: above the bottom, content enters from above */
.sink { opacity: 0; transform: translateY(-14px);
        transition: opacity var(--kt-dur-slow) var(--kt-ease),
                    transform var(--kt-dur-slow) var(--kt-ease); }
.sink.is-in { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  .rise, .sink { opacity: 1; transform: none; }
}
/* No-JS baseline: content is visible; classes above are activated by JS
   adding .js to <html>. Without it, everything renders sharp and present. */
html:not(.js) .rise, html:not(.js) .sink { opacity: 1; transform: none; }

/* ------------------------------------------ the three phases (08-build/17)
   .phase — a tall track; .phase__stage sticky full-screen inside it (spec 13
   §1: 100vh then 100svh, own ground); .phase__media full-bleed with the focal
   point (spec 13 §2) and, for a still, the parallax drift set by KT.phase;
   .phase__veil the scrim; .phase__statements the rail of banners. Reduced
   motion and JS-off: the statements stack, all present, the scrim stays. */
.phase { position: relative; height: var(--phase-track, 280vh); height: var(--phase-track, 280svh); background: var(--kt-ink-1000); }
.phase__stage {
  position: sticky; top: 0; height: 100vh; height: 100svh;
  overflow: hidden; display: grid; place-items: center;
  background: var(--stage-ground, var(--kt-ink-1000));
}
.phase__media {
  position: absolute; inset: -6vh 0; width: 100%; height: calc(100% + 12vh);
  object-fit: cover; object-position: var(--focal-x, 50%) var(--focal-y, 50%);
  will-change: transform;
}
.phase__media picture, .phase__media img, .phase__media video { width: 100%; height: 100%; object-fit: cover; object-position: inherit; display: block; }
/* a still sequence inside the media: frames stacked, one shown, a slow
   dissolve between (KT.phase). JS off: the first frame stands. */
.phase__frame { position: absolute; inset: 0; opacity: 0; transition: opacity var(--kt-dur-ritual) var(--kt-ease); }
.phase__media .phase__frame { object-position: var(--focal-x, 50%) var(--focal-y, 50%); }  /* per-frame focal point */
.phase__frame.is-shown, html:not(.js) .phase__frame:first-child { opacity: 1; }
.phase__frame--ground { background: #000; }
.phase__veil { position: absolute; inset: 0; background: rgb(0 0 0 / .45); pointer-events: none; }
.phase__mark {
  position: absolute; top: 0; left: 0; right: 0;
  /* the masthead is sticky above the pinned stage: the mark sits below it (site.js publishes --masthead-h) */
  padding: calc(var(--masthead-h, 0px) + max(1.25rem, env(safe-area-inset-top))) 0 2.25rem;
  background: linear-gradient(to bottom, rgb(0 0 0 / .62), transparent);   /* a scrim for a small mark over media */
  text-align: center; z-index: 3; margin: 0;
  font-family: var(--kt-font-meta); font-weight: 300; font-size: .72rem;
  letter-spacing: .3em; text-indent: .3em; text-transform: uppercase; color: var(--kt-gold-500);
}
.phase__statements {
  position: relative; z-index: 2; display: grid; place-items: center;
  width: 100%; max-width: 44rem;
  padding: max(2rem, env(safe-area-inset-top)) max(1.5rem, env(safe-area-inset-right))
           max(2rem, env(safe-area-inset-bottom)) max(1.5rem, env(safe-area-inset-left));
}
.phase__statement {
  grid-area: 1 / 1; margin: 0; width: 100%;
  padding: clamp(1.5rem, 4vw, 2.75rem) clamp(1.5rem, 5vw, 3rem);
  background: rgb(0 0 0 / .55);            /* the veil scrim — ≥ 7:1 on any frame */
  backdrop-filter: blur(14px); -webkit-backdrop-filter: blur(14px);
  text-align: center;
  font-family: var(--kt-font-display); font-size: clamp(1.35rem, 2.6vw, 1.9rem); line-height: 1.5;
  opacity: 0; transform: translateY(10px);
  transition: opacity var(--kt-dur-slow) var(--kt-ease), transform var(--kt-dur-slow) var(--kt-ease);
  pointer-events: none;
}
.phase__statement p { margin: 0; }
.phase__statement p + p { margin-top: 1rem; }
.phase__statement .meta { margin-top: 1.25rem; }
.phase__statement.is-current { opacity: 1; transform: none; pointer-events: auto; }
/* JS off / reduced motion: every statement present, stacked, readable */
html:not(.js) .phase { height: auto; }
html:not(.js) .phase__stage { position: relative; height: auto; min-height: 100vh; min-height: 100svh; }
html:not(.js) .phase__statements { display: block; padding-block: clamp(6rem, 14vh, 11rem); }
html:not(.js) .phase__statement { opacity: 1; transform: none; margin-bottom: 1.5rem; }
@media (prefers-reduced-motion: reduce) {
  .phase { height: auto; }
  .phase__stage { position: relative; height: auto; min-height: 100vh; min-height: 100svh; }
  .phase__statements { display: block; padding-block: clamp(6rem, 14vh, 11rem); }
  .phase__statement { opacity: 1; transform: none; margin-bottom: 1.5rem; }
  .phase__media { transform: none !important; }
}
@media (max-height: 560px) and (orientation: landscape) {
  .phase__statement { font-size: clamp(1.1rem, 4vh, 1.4rem); padding: 1rem 1.25rem; }
  .display { font-size: clamp(1.8rem, 8vh, 3rem); }   /* spec 13 §5: after the base .display rule, so it wins */
}
/* the conditions quote — the realm pages' closing lines in the basics band (Morphia, Midsummer) */
.conditions-quote { font-size: 1.25rem; line-height: 1.55; max-width: 34em; margin: 0 auto; }
.conditions-quote p { margin: 0 0 1rem; }
.conditions-quote p:last-child { margin: 0; }
/* ------------------------------------------ the arrival veil (D-30 amended)
   Laid by site.js only when the visitor has just come through the door: the
   page fades in from black over --kt-dur-ritual, then the veil is removed. */
.arrival-veil { position: fixed; inset: 0; z-index: 1000; background: #000; pointer-events: none; opacity: 1; transition: opacity var(--kt-dur-ritual) var(--kt-ease); }
.arrival-veil.is-lifting { opacity: 0; }

/* ------------------------------------------ the realm banners (M-BN, 08-build/18)
   .banner — a promo cut in a stage. At rest a centred 16:9 box (data-rest="box")
   or the whole stage (data-rest="bleed"), dim, at z 0 beneath the veil and the
   copy. Expanded (KT.banner): the <video> is pinned fixed and transformed to
   fill the screen above everything but the masthead, bright, while .banner-edge
   — two drifting radial gradients — hides the frame's outer edges. */
.banner {
  position: absolute; overflow: hidden;   /* z-index auto — the box must not form a
                                             stacking context, or the fixed video's
                                             z 40 would be trapped beneath the copy */
  inset: 0; margin: auto;                 /* centred — never a transform here: a
                                             transformed ancestor would become the
                                             fixed video's containing block */
  width: min(78vw, 138vh, 96rem); aspect-ratio: 16 / 9;
  background: #000;
}
.banner[data-rest="bleed"] { width: 100%; height: 100%; aspect-ratio: auto; }
.banner__video {
  display: block; width: 100%; height: 100%; object-fit: cover;
  object-position: var(--focal-x, 50%) var(--focal-y, 50%);
  filter: brightness(.8);                 /* the stage veil above it does the dimming;
                                             expanded, it escapes the veil — that is the brightening */
  transform-origin: center center;
  transition: transform var(--kt-dur-ritual) var(--kt-ease), filter var(--kt-dur-ritual) var(--kt-ease);
  will-change: transform;
}
.banner__video.is-lifted {
  position: absolute; inset: 0; z-index: 40; pointer-events: none;   /* above the copy (2) and the star (1); the masthead (100) stays */
}
/* while a banner is up, its box lets it overflow — but the STAGE keeps clipping:
   the stage is the viewport's size when it is centred, so the covered frame is
   trimmed exactly to the screen and never widens the page (no sideways overflow,
   no scrollWidth growth). Scrolled past 82% it comes home anyway. */
.stage.has-banner-expanded .banner { overflow: visible; }
.banner.is-expanded .banner__video { filter: brightness(1); }
.stage.has-banner-expanded .stage__body { transition: opacity var(--kt-dur-slow) var(--kt-ease); }
/* the edge — a fixed layer above the expanded video whose two gradients breathe
   at different periods so the frame never shows a straight edge */
.banner-edge {
  position: fixed; inset: 0; z-index: 41; pointer-events: none;
  opacity: 0; transition: opacity var(--kt-dur-ritual) var(--kt-ease);
  /* layer 1 (drifts in position): clear across the middle 57%, .5 black at
     10% from the edge, .9 at the last 4%; layer 2 (breathes in size): a softer,
     larger vignette. Percentages are of each layer's own size, below. */
  background:
    radial-gradient(ellipse 72% 66% at 50% 50%, transparent 50%, rgb(0 0 0 / .6) 78%, rgb(0 0 0 / .97) 100%),
    radial-gradient(ellipse 84% 80% at 50% 50%, transparent 58%, rgb(0 0 0 / .8) 100%);
  background-size: 124% 124%, 146% 146%;
  background-position: 50% 50%, 50% 50%;
  background-repeat: no-repeat;
}
.banner-edge.is-on { opacity: 1; animation: banner-edge-a 11s ease-in-out infinite alternate, banner-edge-b 17s ease-in-out infinite alternate; }
@keyframes banner-edge-a {
  0%   { background-position: 34% 40%, 50% 50%; }
  50%  { background-position: 66% 58%, 50% 50%; }
  100% { background-position: 44% 66%, 50% 50%; }
}
@keyframes banner-edge-b {
  0%   { background-size: 124% 124%, 146% 146%; }
  50%  { background-size: 124% 124%, 164% 150%; }
  100% { background-size: 124% 124%, 152% 168%; }
}
@media (max-width: 900px) { .banner { width: min(92vw, 138vh); } }
@media (prefers-reduced-motion: reduce) {
  .banner__video { transition: none; }
  .banner-edge { display: none; }
}

/* ------------------------------------------------ the accordion (.acc)
   <details class="acc" id="…"><summary><span class="acc__q">…</span></summary>
     <div class="acc__body"><div class="acc__inner">…</div></div></details>
   Native <details> underneath (JS off: opens and closes plainly; find-in-page
   and print open it); with JS, KT.accordions() animates the body — a grid row
   from 0fr to 1fr, the house slow beat — and a deep link (#id) opens its
   accordion on arrival. The mark is a drawn hairline cross that turns to a
   dash when open (T5, no glyph, no border-radius). Reduced motion: no
   transition, same states. Gold is the only interactive colour. */
.acc { border-top: 1px solid var(--kt-ink-600); }
.acc:last-of-type { border-bottom: 1px solid var(--kt-ink-600); }
.acc > summary {
  list-style: none; cursor: pointer; display: grid; grid-template-columns: 1fr auto; align-items: center;
  gap: 1.5rem; padding: 1.35rem 0; color: var(--kt-blush-50);
  transition: color var(--kt-dur-base) var(--kt-ease);
}
.acc > summary::-webkit-details-marker { display: none; }
.acc > summary:hover, .acc > summary:focus-visible { color: var(--kt-gold-300); }
.acc > summary:focus-visible { outline: 2px solid var(--kt-gold-300); outline-offset: 4px; }
.acc__q { font-family: var(--kt-font-display); font-style: italic; font-size: 1.35rem; line-height: 1.35; }
.acc__q--plain { font-style: normal; }
.acc__mark { position: relative; width: 1.1rem; height: 1.1rem; flex: none; color: var(--kt-gold-500); }
.acc__mark::before, .acc__mark::after {
  content: ''; position: absolute; left: 0; top: 50%; width: 100%; height: 1px; background: currentColor;
  transition: transform var(--kt-dur-slow) var(--kt-ease), opacity var(--kt-dur-slow) var(--kt-ease);
}
.acc__mark::after { transform: rotate(90deg); }
.acc[open] > summary { color: var(--kt-gold-300); }
.acc__body { display: grid; grid-template-rows: 1fr; }
.acc__inner { overflow: hidden; padding-bottom: 1.75rem; }
/* with JS the fold is a class: .is-open grows the row from 0fr; without it the
   row is simply 1fr whenever the details is open */
html.js .acc .acc__body { grid-template-rows: 0fr; transition: grid-template-rows var(--kt-dur-base) var(--kt-ease); }   /* ≤ the input grace, so a fold never counts as a shift */
html.js .acc.is-open .acc__body { grid-template-rows: 1fr; }
html.js .acc__inner { min-height: 0; }
.acc[open] > summary .acc__mark::after { transform: rotate(0deg); opacity: 0; }
@media (prefers-reduced-motion: reduce) {
  html.js .acc .acc__body { transition: none; }
  .acc__mark::before, .acc__mark::after { transition: none; }
}
@media print {
  .acc__body { grid-template-rows: 1fr !important; }
  /* nothing that reveals on scroll may print blank */
  .rise, .sink, .arrive, .arc-line { opacity: 1 !important; transform: none !important; }
  .acc > summary, .acc__q { color: #111 !important; }
}

/* the basics — the band every realm page ends on (17 §4): a definition list,
   Montserrat keys in gold, Goudy values, one column on a phone */
.basics {
  display: grid; grid-template-columns: max-content 1fr; gap: 1rem 2.5rem;
  margin: 0 auto; max-width: 40rem; text-align: left;
}
.basics dt {
  font-family: var(--kt-font-meta); font-size: .72rem; font-weight: 300;
  letter-spacing: .2em; text-transform: uppercase; color: var(--kt-gold-500);
  padding-top: .4rem; margin: 0;
}
.basics dd { margin: 0; font-size: 1.25rem; line-height: 1.5; }
.basics dd .meta { display: inline; font-size: .784rem; }
@media (max-width: 40rem) {
  .basics { grid-template-columns: 1fr; gap: .2rem 0; }
  .basics dd { margin-bottom: 1.1rem; }
}

/* ---------------------------------------------------- origins fragments
   Shared by /origins and its nine sub-pages (07-PAGE-ORIGINS.md §7, §11):
   lowercase titles by text-transform — the deliberate break from the site's
   uppercase meta that marks the quiet page — one image per fragment, and the
   sibling grid at every foot so you fall sideways rather than forward. */
.fragment-grid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
  gap: 2.5rem 2rem;
}
.fragment { text-decoration: none; display: block; }
.fragment .f-img {
  aspect-ratio: 4 / 3; background: var(--kt-ink-900);
  display: grid; place-items: center;
  color: var(--kt-blush-400); font-family: var(--kt-font-meta);
  font-size: .65rem; letter-spacing: .16em; text-transform: uppercase;
  margin-bottom: 1rem;
}
.fragment .f-img--rule {
  background: none;
  border-top: 1px solid var(--kt-gold-900); border-bottom: 1px solid var(--kt-gold-900);
}
.fragment h3 {
  font-size: 1.3rem; text-transform: lowercase; font-style: italic;
  transition: color var(--kt-dur-base) var(--kt-ease);
}
.fragment:hover h3, .fragment:focus-visible h3 { color: var(--kt-gold-300); }
.fragment-title { text-transform: lowercase; font-style: italic; margin-top: 1rem; }
.fragment-figure { margin: 0 auto; }
.fragment-figure.plate-placeholder { aspect-ratio: 3 / 2; width: min(100%, 44rem); min-height: 0; }
.fragment-figure--star { width: clamp(12rem, 26vw, 18rem); color: var(--kt-gold-500); }
.fragment-figure--star img { width: 100%; height: auto; }
.fragment-figure--rule { width: 6rem; height: 1px; background: var(--kt-gold-900); margin-block: 2rem; }
.fragment-body blockquote { margin: 0 0 1.5rem; padding-left: 1.5rem; border-left: 1px solid var(--kt-gold-900); }
.fragment-body blockquote p:last-child { margin-bottom: 0; }
.fragment-body .inscription {
  font-family: var(--kt-font-meta); font-weight: 300; font-size: .9em;
  letter-spacing: .34em; text-transform: uppercase; color: var(--kt-blush-300);
}
/* the four suits, as a definition list — the dossier's own words, set in the site's grammar */
.suits { margin: 2.5rem 0; padding: 0; display: grid; gap: 2.25rem; }
.suits dt { margin: 0 0 .5rem; }
.suits dt .numeral { font-size: 1rem; letter-spacing: .2em; text-transform: uppercase; }
.suits dd { margin: 0; display: grid; gap: .35rem; }
.suits .suit-emblem, .suits .suit-domain {
  font-family: var(--kt-font-meta); font-weight: 300; font-size: .9rem;
  letter-spacing: .08em; color: var(--kt-blush-300); line-height: 1.7;
}
.suits .suit-q { font-size: 1.35rem; margin-top: .25rem; }
/* A marked data gap — the page does not ship while one is present. The visit
   page's age line was one and was answered (D-55); the rule is kept for the
   archived origins fragments awaiting evidence or sign-off, which are its only
   remaining users. */
.data-gap {
  font-family: var(--kt-font-meta); font-weight: 400; font-size: .85rem;
  letter-spacing: .08em; line-height: 1.7; color: var(--kt-gold-300);
  margin-top: 2.5rem; padding-top: 1.25rem; border-top: 1px solid var(--kt-gold-900);
}

/* ------------------------------------------------------------- utilities */

.visually-hidden {
  position: absolute; width: 1px; height: 1px; margin: -1px;
  padding: 0; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.gold { color: var(--kt-gold-500); }
hr.rule { border: 0; height: 1px; background: var(--kt-ink-600); margin: 0; }

/* Placeholder plates — honest placeholders, never broken images.
   Each names its asset ID so the manifest row is one glance away, and
   carries data-asset / data-focal / data-cut so the arrival markup in
   assets/MEDIA-SLOTS.md can be dropped in without re-reading the manifest. */
.plate-placeholder {
  display: grid; place-items: center;
  background: var(--kt-ink-900);
  color: var(--kt-blush-400);
  font-family: var(--kt-font-meta);
  font-size: .7rem; letter-spacing: .18em; text-transform: uppercase;
  min-height: 40vh;
  text-align: center; padding: 1rem;
}
/* Plates (spec 13 §2): content photography in a reserved aspect box —
   never stage-cropped. The box reserves the space (CLS 0); the image fills
   it; object-fit only ever trims a hair if a plate is not exactly 4:5. */
.plate-box { aspect-ratio: 4 / 5; width: min(100%, 34rem); margin-inline: auto; }
.plate { margin: 0; overflow: hidden; }
.plate img { width: 100%; height: 100%; object-fit: cover; }
.plate-pair {
  display: grid; grid-template-columns: 1fr 1fr; gap: 2rem;
  max-width: 56rem; margin-inline: auto;
}
.plate-pair .plate-box { width: 100%; }
@media (max-width: 40rem) { .plate-pair { grid-template-columns: 1fr; gap: 1.5rem; } }
