/* journey/cards — the six windows' interiors (see cards/index.js for the
   contract and the one-building/six-worlds split).

   Loaded after site.css. Everything here scopes under .j-pop-rich or a
   card's own namespace (.ag- Arca, .ac- ArtCompute, .rp- 2RP, .ad- ADOS,
   .hm- Hivemind, .dc- Discord) so a node without a builder renders exactly
   yesterday's popover. The shell frame itself — panel, hairline, radius,
   shell lamp, filament — is site.css's and is deliberately not restated here. */

/* ---- shell extension ------------------------------------------------- */

/* Above the side rail (site.css z 4) — at phone widths the 86vw card runs
   under the rail's menu button, which read as the menu floating ON the
   card (Hannah, 2026-08-17, again 2026-08-18 on mobile). The FIRST fix
   set z-index on .j-pop itself, which is inert: the popover lives inside
   .j-hotspots, a fixed z-index:1 stacking context, so against the rail
   only the CONTAINER's index counts. Raising the layer is the real fix.
   Still under the menu scrim/panel (6/7), so an OPEN menu covers an open
   card, which is the right order; the container is pointer-events:none,
   so nothing new intercepts the rail's own hover. */
.j-hotspots { z-index: 5; }

.j-pop.j-pop-rich {
  max-width: min(21.5rem, 86vw);
  width: min(21.5rem, 86vw); /* media wants a stable measure, not max-content */
  /* THE MAT (Hannah, 2026-08-18: "a small border region to make them look
     consistent") — the house panel shows as a slim, even frame around every
     stage: one window treatment, six worlds behind it. */
  padding: 7px;
}

/* THE MYCELIAL BACKPLANE (2026-08-19) — nothing is painted ON the project.
   One loose network lives behind the opaque stage, extends beyond its box,
   disappears underneath the content, and re-emerges at unrelated edges. The
   card therefore stays wholly itself; the surrounding world is what grows.

   Per-node transforms are deterministic rather than freshly random on every
   hover: each initiative gets a different accidental-looking crop without a
   line jumping position when the visitor returns to it. */
.j-pop-frame {
  position: relative;
  isolation: isolate;
  box-sizing: border-box;
  width: 100%;
  overflow: visible;
}

/* Oversized on purpose. The two-layer mask subtracts the ENTIRE card box —
   stage, 7px mat, and border — from the network. The
   lines can approach and depart in the surrounding space, but no strand can
   paint across the edge. They meet the silhouette exactly, so the visible
   ends read as one network continuing underneath rather than loose decoration. */
.j-pop-frame::before {
  content: "";
  position: absolute;
  inset: -20px -24px;
  z-index: 0;
  box-sizing: border-box;
  padding: 12px 16px;
  pointer-events: none;
  background-image: url('../../assets/cards/mycelial-underlay.svg');
  background-position:
    calc(50% + var(--myc-x, 0px))
    calc(50% + var(--myc-y, 0px));
  background-size: var(--myc-w, 100%) var(--myc-h, 100%);
  background-repeat: no-repeat;
  opacity: 0.6;
  -webkit-mask:
    linear-gradient(#000 0 0) content-box,
    linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask:
    linear-gradient(#000 0 0) content-box,
    linear-gradient(#000 0 0);
  mask-composite: exclude;
}

/* Six stable crops of the same underground organism: related, never cloned. */
.j-pop[data-node="arca"]       { --myc-x: -6px; --myc-y: 2px;  --myc-w: 104%; --myc-h: 98%; }
.j-pop[data-node="artcompute"] { --myc-x: 6px;  --myc-y: -3px; --myc-w: 98%;  --myc-h: 106%; }
.j-pop[data-node="tworp"]      { --myc-x: -2px; --myc-y: 5px;  --myc-w: 108%; --myc-h: 102%; }
.j-pop[data-node="ados"]       { --myc-x: 5px;  --myc-y: 2px;  --myc-w: 102%; --myc-h: 108%; }
.j-pop[data-node="hivemind"]   { --myc-x: -7px; --myc-y: 0px;  --myc-w: 106%; --myc-h: 96%; }
.j-pop[data-node="discord"]    { --myc-x: 3px;  --myc-y: -5px; --myc-w: 96%;  --myc-h: 104%; }

/* A light-only wake: the network never moves after it is placed, which keeps
   the random crop calm and leaves popover measurement completely stable. */
@keyframes j-mycelium-wake {
  0%   { opacity: 0; }
  48%  { opacity: 0.72; }
  100% { opacity: 0.6; }
}
.j-pop.j-pop-enter .j-pop-frame::before {
  animation: j-mycelium-wake 0.62s ease-out both;
}

/* ONE PARENT STRUCTURE, SIX INTERIORS (2026-08-31, round 3). Round 1 put
   the whole house block around every stage and the six read as the old
   cards with a new border; round 2 removed the shell's words entirely and
   moved identity into each world, and the owner's verdict came back the
   other way: the shared skeleton is what makes the six navigable — "each
   of the cards should have a parent structure … the name and description
   at the top and then the button below it at the bottom. It's important
   that ALL of them share that in common."

   So the synthesis keeps both halves. The shell HEAD is back on every
   card — accent-coloured name, house short line, same place and same type
   on all six — and every card still closes on ONE full-width door band at
   the foot. What round 2 won stays won INSIDE the window: the record as
   chips and cells rather than sentences, no title said twice (the in-world
   identity bands that duplicated the head are gone), each door keeping its
   own label and accent voice (.card-door below), and the world-lit
   surface. The shell's own generic link stays retired for built cards —
   the door is the stage's, in its own voice, in the shell's place.

   The short line keeps double duty as the chip's aria-describedby text
   throughout, and the unbuilt pair (quark, brotchen) wear all four shell
   parts exactly as before. */
.j-pop.j-pop-rich {
  align-items: stretch;
  gap: 0;
}
.j-pop-rich:not(.j-pop-unbuilt) .j-pop-link { display: none; }
/* Built and unbuilt alike keep the shell-head composition the third state
   was drawn around: accented name, house short line, stage as the body. */
.j-pop-rich .j-pop-t {
  order: 0;
  margin: 0.42rem 0.5rem 0.08rem;
  font-size: 0.76rem;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--card-accent, var(--parchment));
}
.j-pop-rich .j-pop-s {
  order: 1;
  margin: 0 0.5rem 0.55rem;
  font-size: 0.73rem;
  line-height: 1.45;
}
.j-pop-rich .j-pop-frame { order: 2; }

/* Each initiative's accent — the one colour the shell borrows from the world
   behind its window. Sources: arca's site red warmed to read on the house
   dark; ArtCompute's own #39ff14; 2RP as print parchment; ADOS's white mark;
   Hivemind wears the house gold (it has no brand colour, by its own note
   below); Discord's blurple lifted for small-caps legibility. */
.j-pop[data-node="arca"]       { --card-accent: #e0745f; }
.j-pop[data-node="artcompute"] { --card-accent: #39ff14; }
.j-pop[data-node="tworp"]      { --card-accent: #f4e4c1; }
.j-pop[data-node="ados"]       { --card-accent: #ffffff; }
.j-pop[data-node="hivemind"]   { --card-accent: #d9a441; }
.j-pop[data-node="discord"]    { --card-accent: #8891f2; }

/* THE SURFACE IS LIT BY THE WORLD (2026-08-30, §2D). Two quiet moves push
   the panel from "sits on the site" toward "revealed from it":

   1) MATERIAL — the mat is not a flat swatch: a barely-there top-light
      gradient gives the frame the same falling light the organism's ember
      field casts, so the card reads as a surface in the scene rather than
      an overlay above it.
   2) CONTACT — the edge nearest the chip answers the filament: a soft,
      warm bloom sits where the filament touches (keyed off the same
      data-side placement the filament reads), as if its light lands on the
      surface it just grew. Static after entry; never brighter than the
      filament's own ping, never a border animation (the monotone gate's
      territory), never ornament. */
.j-pop.j-pop-rich {
  background: linear-gradient(168deg, rgba(19, 13, 5, 0.95), rgba(11, 7, 2, 0.965) 62%);
}
.j-pop-rich:not(.j-pop-unbuilt)::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 3;
  border-radius: 2px;
  pointer-events: none;
  background: radial-gradient(58% 44% at 0% 50%, rgba(240, 190, 90, 0.14), transparent 70%);
}
.j-pop-rich[data-side="left"]:not(.j-pop-unbuilt)::after {
  background: radial-gradient(58% 44% at 100% 50%, rgba(240, 190, 90, 0.14), transparent 70%);
}
.j-pop-rich[data-side="below"]:not(.j-pop-unbuilt)::after {
  background: radial-gradient(48% 38% at 50% 0%, rgba(240, 190, 90, 0.14), transparent 70%);
}
/* ...and the hairline itself warms on that side — a static value, never a
   keyframe (the entry's shared border animation simply lands on it) */
.j-pop-rich[data-side="right"]:not(.j-pop-unbuilt) { border-left-color: rgba(233, 184, 90, 0.55); }
.j-pop-rich[data-side="left"]:not(.j-pop-unbuilt) { border-right-color: rgba(233, 184, 90, 0.55); }
.j-pop-rich[data-side="below"]:not(.j-pop-unbuilt) { border-top-color: rgba(233, 184, 90, 0.55); }
/* the bloom arrives with the filament's ping and then simply holds */
.j-pop.j-pop-enter.j-pop-rich:not(.j-pop-unbuilt)::after {
  animation: card-fade 0.62s ease-out both;
}
/* THE FILAMENT RESTS LIT, faintly, for a built card (round 2, §2D): the
   entry still pings it once at full brightness (site.css), but instead of
   going out entirely it settles to a quarter-glow, so the card stays
   visibly grown from its node for as long as it is open. The generic
   popover keeps site.css's dark rest. */
.j-pop.j-pop-rich:not(.j-pop-unbuilt)::before { opacity: 0.25; }

.j-pop-stage {
  position: relative;
  z-index: 2;
  width: 100%;
  margin: 0;
  overflow: hidden;
}

/* THE ENDING IS PART OF THE COMPOSITION (2026-08-30, round 2). The hover-
   revealed corner CTA and the shell's anchored door are both gone: a card
   that ends with an empty band looks unfinished in every still, and the
   owner asked for endings that fit each card rather than one door with six
   labels. Every stage now closes with its own always-present band — an
   action (arca, artcompute, ados, hivemind, discord) or a status (2RP) —
   set quiet and brightening under the pointer via each card's own rule.
   The shared .card-cta reveal machinery went with the corner doors. */
.card-cta { text-decoration: none; }

/* THE DOOR IS THE SHARED FOOT (round 3). The six endings keep their round-2
   words and colours, but they stand in ONE place with ONE geometry: a
   full-width band on the card's bottom edge — same padding, same tracked
   micro-type, same quiet-until-reached-for behaviour — so the eye that has
   learned one card's exit has learned them all. Each card's own rule keeps
   only its voice: face, colour, hover, hairline tint. 2RP's status band
   wears the same skeleton with nothing behind it, which is the point. */
.card-door {
  display: block;
  box-sizing: border-box;
  width: 100%;
  padding: 0.52rem 0.6rem 0.58rem;
  border-top: 1px solid rgba(255, 255, 255, 0.1);
  text-align: center;
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.26em;
  text-indent: 0.26em;               /* trailing-tracking comp, centred */
  text-transform: uppercase;
  transition: color 0.25s ease, background 0.25s ease;
}

/* Keep the contact filament above stage media. */
.j-pop.j-pop-rich::before { z-index: 2; }

/* The stage joins the entry choreography as the shell lamps on. */
.j-pop.j-pop-enter .j-pop-stage { animation: j-pop-word 0.34s ease both; animation-delay: 0.06s; }

@media (prefers-reduced-motion: reduce) {
  .j-pop-stage, .j-pop-stage * { animation: none !important; transition: none !important; }
  .j-pop-frame::before { animation: none !important; }
  .j-pop-rich::after { animation: none !important; }
}

/* ---- arca — arcagidan.com's hero, miniaturized ------------------------ */
/* Its own faces, self-hosted subsets (assets/cards/PROVENANCE.md). */
@font-face {
  font-family: 'Eubergine';
  src: url('../../assets/cards/arca/eubergine-sub.woff2') format('woff2');
  font-display: swap;
}
@font-face {
  font-family: 'Geosans Light';
  src: url('../../assets/cards/arca/geosans-sub.woff2') format('woff2');
  font-display: swap;
}

/* The whole approach is the site's own hero (Hannah, 2026-08-17: "copy the
   whole approach from the website"): four full-color posters, one ARCA GIDAN
   line across them, THE/PRIZE eyebrows, and a spotlight-on-hover — the
   non-hovered panels drop into darkness (their duration-300 crossfade), the
   hovered syllable goes #7b0b0b at scale 1.1 (their duration-500). */

.j-pop-stage.ag { background: #1a1a1a; }   /* their dark-section ink */

.ag-panels {
  display: grid;
  grid-template-columns: repeat(4, 1fr);   /* their hero grid */
  height: 178px;                           /* the art; the record sits below it */
}
.ag-panel { position: relative; margin: 0; overflow: hidden; }
.ag-panel img,
.ag-video {
  position: absolute;
  inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  /* REST IS TEMPERED (round 3). The site's hero rests fully vibrant, but
     under the restored shell head the full-strength poster palette — the
     acid green especially — read as a second header shouting over the
     first. The art now rests a step dimmer and quieter, integrated into
     the house dark, and earns its full colour back under the spotlight
     (.lit below) — dimming further is still what HOVER does to everyone
     else (their bg-black/70-style drop). */
  filter: saturate(0.72) brightness(0.86);
  transition: filter 0.3s ease;            /* their duration-300 */
}
/* the figure's video panel surfaces only once frames are rendering —
   hover-streamed from the site itself, the poster beneath is the fallback */
.ag-video { opacity: 0; transition: opacity 0.3s ease; }
.ag-video.playing { opacity: 1; }
/* the spotlit poster DRIFTS while it holds the light — guaranteed motion
   under the pointer even while (or if) the figure's video never arrives
   (2026-08-18: arcagidan.com's host began 403ing cross-site media; the
   video layer stays wired for the day it opens up or the files land in
   assets/) */
.ag .ag-panel.lit img { animation: ag-drift 7s ease-in-out infinite alternate; }
@keyframes ag-drift {
  from { transform: scale(1.05) translateY(0); }
  to   { transform: scale(1.14) translateY(-2.5%); }
}
/* the spotlight buys the full palette back */
.ag .ag-panel.lit img,
.ag .ag-panel.lit .ag-video { filter: none; }
.ag.ag-hovering .ag-panel:not(.lit) img,
.ag.ag-hovering .ag-panel:not(.lit) .ag-video { filter: brightness(0.28) saturate(0.5); }

.ag-word {
  position: absolute;
  inset: 0 0 auto 0;
  height: 178px;                           /* covers the art exactly — the
                                              record below stays un-shadowed */
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.3rem;
  pointer-events: none;
  color: #f5f0eb;                          /* their cream */
  /* heavier than the site needs at 160px: at card scale the letters cross
     bright artwork, so the halo does the legibility work */
  text-shadow:
    0 1px 14px rgba(0, 0, 0, 0.9),
    0 0 6px rgba(0, 0, 0, 0.85),
    0 0 2px rgba(0, 0, 0, 1);
}
.ag-title {
  font-family: 'Eubergine', 'Didot', Georgia, serif;  /* their h1 face */
  /* Under the shell head that already says ARCA GIDAN PRIZE, the in-art
     wordmark steps down from headline to signature (round 3): smaller and
     a touch translucent, it reads as part of the artwork's character — and
     it stays, because the syllables ARE the site's hover instrument. The
     THE/PRIZE eyebrows are gone with the same stroke; the head says PRIZE
     now, and two saying it 40px apart was the duplication being cured. */
  font-size: 1.72rem;
  letter-spacing: 0.12em;                  /* their tracking-[0.12em] */
  line-height: 0.9;                        /* their lineHeight .9 */
  white-space: nowrap;
  opacity: 0.94;
}
.ag-syl {
  pointer-events: auto;                    /* the syllables ARE the hover zones */
  transition: color 0.5s ease, transform 0.5s ease;   /* their duration-500 */
  display: inline-block;
}
.ag-syl:nth-child(3) { margin-left: 0.3em; }  /* their w-[0.3em] word gap */
.ag-syl.lit { color: #7b0b0b; transform: scale(1.1); }  /* their hover state */

/* THE RECORD SITS UNDER THE ARTWORK (round 3): three cells on the ink
   plate, the value in their own Eubergine over a microlabel, hairlines
   between. The round-2 caption line above them is gone — it was the shell
   head's short line word for word, and the head says it now. */
.ag-foot {
  position: relative;
  background: #141414;
  font-family: 'Geosans Light', 'Inter', sans-serif;
}
.ag-band {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  border-top: 1px solid rgba(245, 240, 235, 0.12);
}
.ag-stat {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  padding: 0.55rem 0.25rem 0.5rem;
}
.ag-stat + .ag-stat { border-left: 1px solid rgba(245, 240, 235, 0.1); }
.ag-num {
  font-family: 'Eubergine', 'Didot', Georgia, serif;   /* their h1 face */
  font-size: 1.02rem;
  line-height: 1;
  color: #f5f0eb;
}
.ag-lab {
  font-size: 0.5rem;
  letter-spacing: 0.24em;
  padding-left: 0.24em;                    /* trailing-tracking comp */
  color: rgba(245, 240, 235, 0.5);
}

/* the ending — their cream on ink; geometry is .card-door's */
.ag-door {
  border-top-color: rgba(245, 240, 235, 0.12);
  color: rgba(245, 240, 235, 0.62);
}
.ag-door:is(:hover, :focus-visible) { color: #f5f0eb; }

/* ---- entry choreography (Hannah, 2026-08-17: "make sure every card has a
   nice little entry animation") ----------------------------------------
   Each stage's internals arrive in that card's own vocabulary, keyed to the
   shell's `.j-pop-enter` window (0.7s, ui.js POP_ENTER_MS) so the lamp,
   the filament, and the interior land as one gesture. The global REDUCE
   kill-switch above silences all of it. */
@keyframes card-rise { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }
@keyframes card-fade { from { opacity: 0; } to { opacity: 1; } }

/* arca — the site's own fade-in-up, panel by panel, wordmark last */
.j-pop-enter .ag-panel { animation: card-rise 0.42s cubic-bezier(0.22, 1, 0.36, 1) both; }
.j-pop-enter .ag-panel:nth-child(2) { animation-delay: 0.07s; }
.j-pop-enter .ag-panel:nth-child(3) { animation-delay: 0.14s; }
.j-pop-enter .ag-panel:nth-child(4) { animation-delay: 0.21s; }
.j-pop-enter .ag-word  { animation: card-fade 0.4s ease both; animation-delay: 0.3s; }
.j-pop-enter .ag-foot  { animation: card-fade 0.4s ease both; animation-delay: 0.38s; }

/* artcompute — a terminal painting itself in, band by band, with the same
   steps() flicker the house link-strike uses */
.j-pop-enter .ac-grid   { animation: card-fade 0.26s steps(2, end) both; }
.j-pop-enter .ac-ledger-label,
.j-pop-enter .ac-ledger { animation: card-fade 0.3s steps(3, end) both; animation-delay: 0.14s; }
.j-pop-enter .ac-door   { animation: card-fade 0.3s steps(3, end) both; animation-delay: 0.28s; }

/* ados — the window fades up, then the centred identity stack rises */
.j-pop-enter .ad-media { animation: card-fade 0.5s ease both; }
.j-pop-enter .ad-word  { animation: card-rise 0.45s cubic-bezier(0.22, 1, 0.36, 1) both; animation-delay: 0.15s; }
.j-pop-enter .ad-door  { animation: card-fade 0.4s ease both; animation-delay: 0.32s; }

/* tworp — the cover fades, the masthead stamps down like a printed cover */
@keyframes rp-stamp { from { opacity: 0; transform: scale(1.12); } to { opacity: 1; transform: scale(1); } }
.j-pop-enter .rp-cover { animation: card-fade 0.5s ease both; }
.j-pop-enter .rp-head  { animation: rp-stamp 0.45s cubic-bezier(0.22, 1, 0.36, 1) both; animation-delay: 0.12s; }
.j-pop-enter .rp-foot  { animation: card-fade 0.4s ease both; animation-delay: 0.34s; }

/* hivemind — the console lamp comes up (the typing itself is the real
   entrance, started by activate()), the stat footer after */
.j-pop-enter .hm-console { animation: card-fade 0.35s ease both; animation-delay: 0.05s; }
.j-pop-enter .hm-foot,
.j-pop-enter .hm-door    { animation: card-fade 0.4s ease both; animation-delay: 0.25s; }

/* discord — the topic rises first, dots and door last */
.j-pop-enter .dc-body { animation: card-fade 0.4s ease both; }
.j-pop-enter .dc-dots,
.j-pop-enter .dc-door { animation: card-fade 0.35s ease both; animation-delay: 0.3s; }

/* ---- exit choreography (Hannah, 2026-08-18: "a nice exit animation for
   when we dehover") ----------------------------------------------------
   The mirror of the entry, quicker and quieter — the interior settles a
   breath before the panel goes, so the card exhales instead of cutting.
   ui.js holds `.open` through POP_EXIT_MS (230ms) while this plays; a
   pointer returning mid-exit cancels it and the panel never blinks.
   Durations stay inside the window so nothing is caught mid-move. */
@keyframes card-settle {
  from { opacity: 1; transform: none; }
  to   { opacity: 0; transform: translateY(5px); }
}
/* The shell itself uses site.css's shared j-detail-depart gesture; only the
   rich interior adds this quiet settle, so its own media can finish in its
   own visual language without moving the world-anchored vessel. */
.j-pop-exit .j-pop-stage { animation: card-settle 0.2s ease-in both; }
.j-pop-exit .j-pop-t,
.j-pop-exit .j-pop-s   { animation: card-fade 0.16s ease-in reverse both; }
.j-pop-exit .j-pop-link { animation: card-fade 0.12s ease-in reverse both; }

/* ---- artcompute — artcompute.org's terminal ledger, miniaturized ----- */
/* Its own language: a hard-edged monospace terminal under scanlines. System
   mono matches their JetBrains Mono at card scale, so no webfont is loaded.
   Colors verified from its live CSS/bundle 2026-08-17:
     #0a0a0a                 their ground
     #c8c8c8                 their body text
     #39ff14                 neon green (primary accent)
     #a78bfa                 violet (secondary)
     #38bdf8                 sky (tertiary)
     rgba(255,255,255,0.08)  their hairline border
   No border-radius anywhere in the stage: their aesthetic is square. */

.j-pop-stage.ac {
  background: #0a0a0a;                                              /* their ground */
  color: #c8c8c8;                                                   /* their body text */
  font-family: ui-monospace, 'SFMono-Regular', Menlo, monospace;    /* their JetBrains Mono */
}

/* Their scanlines overlay, exact gradient, above content, inert. */
.j-pop-stage.ac::after {
  content: '';
  position: absolute;
  inset: 0;
  background: repeating-linear-gradient(0deg,
    transparent, transparent 2px,
    rgba(0, 0, 0, 0.03) 2px, rgba(0, 0, 0, 0.03) 4px);
  pointer-events: none;
}

/* ---- band 1: stat grid (their gap-px hairline grid). Round 3: the
   round-2 identity band above it — wordmark, tagline, status dot — is
   gone; the shell head says ARTCOMPUTE now, and the terminal opens
   straight onto its data, which is its personality. ---- */
.ac-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1px;                            /* hairline shows through */
  background: rgba(255, 255, 255, 0.08);
  border-bottom: 1px solid rgba(255, 255, 255, 0.08);
}
.ac-cell {
  display: flex;
  flex-direction: column;
  gap: 3px;
  padding: 0.6rem 0.7rem 0.55rem;
  background: #0a0a0a;
}
.ac-label {
  font-size: 8px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.16em;
  color: rgba(57, 255, 20, 0.4);       /* green micro-label, #39ff14 at 40% */
}
.ac-value {
  font-size: 19px;
  line-height: 1.1;
  color: rgba(255, 255, 255, 0.85);    /* white/85 */
}
.ac-value--green  { color: #39ff14; }              /* the hero number */
.ac-value--violet { color: #a78bfa; }              /* their secondary */

/* ---- band 3: cycling ledger under its micro-label ---- */
.ac-ledger-label {
  display: block;
  padding: 0.55rem 0.85rem 0.2rem;
  font-size: 8px;
  font-weight: 700;
  letter-spacing: 0.16em;
  color: rgba(255, 255, 255, 0.35);
}
.ac-ledger {
  position: relative;
  height: 22px;
  margin: 0 0.85rem 0.6rem;
}
.ac-ledger-row {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  gap: 6px;
  opacity: 0;
  transform: translateY(-5px);
  /* the shuffle: the settled row slips up and out on this transition... */
  transition: opacity 170ms ease-in, transform 170ms ease-in;
}
/* ...and the next one rises in from below on this animation — a ledger
   advancing a line, not a crossfade */
.ac-ledger-row.on {
  opacity: 1;
  transform: none;
  animation: ac-row-in 260ms cubic-bezier(0.22, 1, 0.36, 1) both;
  animation-delay: 130ms;              /* let the old row clear first */
}
@keyframes ac-row-in {
  from { opacity: 0; transform: translateY(7px); }
  to   { opacity: 1; transform: none; }
}
.ac-avatar {
  width: 14px; height: 14px;
  flex: none;
  object-fit: cover;                   /* 56px source; no radius — their aesthetic */
}
.ac-ledger-text {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;             /* 62vw phones truncate the project text */
  font-size: 9px;
  color: #c8c8c8;
}
.ac-chip {
  flex: none;
  font-size: 9px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  padding: 1px 5px;
  border: 1px solid rgba(57, 255, 20, 0.2);
  background: rgba(57, 255, 20, 0.05);
  color: rgba(57, 255, 20, 0.6);       /* green PAID chip, their accent */
}

/* ---- ados — ados.events' hero, miniaturized ---------------------------- */
/* Its own face, self-hosted subset (assets/cards/PROVENANCE.md). */
@font-face {
  font-family: 'Pilowlava';
  src: url('../../assets/cards/ados/pilowlava-sub.woff2') format('woff2');
  font-display: swap;
}

.j-pop-stage.ad {
  background: #0a0a0a;   /* their theme-color ground */
  /* horizontal swipes are the card's (the ‹ › walker), never the page's:
     pan-y keeps vertical pans native and leaves horizontal gestures to the
     swipe listeners in ados.js (2026-08-19) */
  touch-action: pan-y;
}

/* the bounded event window: media, scrim, identity and walker in one frame */
.ad-frame {
  position: relative;
  height: 168px;
  overflow: hidden;
}

/* Full-bleed event-media window: poster underneath, animated preview over. */
.ad-media,
.ad-media img,
.ad-scrim {
  position: absolute;
  inset: 0;
}
.ad-media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
/* the animated preview crossfades in over the poster — their opacity 500ms.
   Same fill/cover as the poster: without these the <video> renders at its
   intrinsic size, top-left in the frame (Peter, 2026-08-18: "centred within
   the frame"). */
.ad-preview {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  transition: opacity 0.5s ease;
}
.ad-preview.on { opacity: 1; }

/* their event-media scrim: linear-gradient(to top, black, rgba(0,0,0,.4),
   rgba(0,0,0,.15)) */
.ad-scrim {
  background: linear-gradient(to top, #000, rgba(0,0,0,0.4), rgba(0,0,0,0.15));
  pointer-events: none;
}

/* ONE CENTRED IDENTITY STACK AT EVERY WIDTH (Hannah, 2026-08-26: "the place
   and date label should be above the ADOS, and all 3 elements should be
   vertically aligned together").

   Desktop used to pin .ad-center OUT of this flex column -- absolute, bottom
   -50px, negative left/right cancelling the padding -- so the caption sat on
   the card's bottom edge as a footer strip while mark and eyebrow floated
   centred above it. Three elements, two alignments. Phones already had the
   stack (the 620px rule below), so this is desktop adopting what mobile was
   given, not a new composition: caption, ADOS, eyebrow, one column, one
   centre line. .ad-word loses the bottom: 50px that reserved the strip's
   room. */
.ad-word {
  position: absolute;
  inset: 0 38px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  text-align: center;
  pointer-events: none;
}
.ad-mark {
  font-family: 'Pilowlava', 'Arial Black', sans-serif;  /* their hero face */
  /* Round 3: the shell head above the window already says ADOS, so the
     in-art mark steps down a size and a step of opacity — kept, because
     the Pilowlava mark over the event footage IS the site's identity, but
     reading as the artwork's signature under the head, not a second head.
     Its AI ART GATHERINGS eyebrow is gone with the same stroke — the
     head's short line is the one descriptor this card gets. */
  font-size: 1.72rem;
  line-height: 1;
  color: rgba(255, 255, 255, 0.92);
}
/* the scrim is lightest at the top, so a halo keeps the mark legible over
   arbitrary poster art — same reasoning as arca's word shadow */
.ad-mark {
  text-shadow: 0 1px 10px rgba(0,0,0,0.8), 0 0 2px rgba(0,0,0,0.7);
}

/* The walker arrows stay on the side edges at vertical middle, independent
   of caption length. The card sits above the rail (z-index 5), so the right
   arrow stays clickable at phone widths. */
.ad-nav {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);   /* vertical middle of the window — the
                                    arrows never move as captions change */
  width: 26px; height: 26px;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  font-size: 14px;
  line-height: 1;
  color: rgba(255, 255, 255, 0.75);
  background: rgba(10, 10, 10, 0.45);
  border: 1px solid rgba(255, 255, 255, 0.22);
  border-radius: 999px;
  cursor: pointer;
}
.ad-prev { left: 8px; }
.ad-next { right: 8px; }
.ad-nav:is(:hover, :focus-visible) { color: #fff; border-color: rgba(255, 255, 255, 0.55); }
.ad-center {
  flex: 0 1 auto;
  min-width: 0;
  max-width: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: 6px;
}
.ad-mark { order: 1; }
.ad-center { order: 0; }

/* THE ACTION ENDS THE CARD (round 2): the site's own eyebrow voice, on the
   shared foot; geometry is .card-door's. */
.ad-door {
  border-top-color: rgba(255, 255, 255, 0.12);
  color: rgba(255, 255, 255, 0.6);
}
.ad-door:is(:hover, :focus-visible) { color: #fff; }

/* The 620px portrait rule that used to carry this composition is gone: it
   said `inset: 0 38px`, `position: static`, `order: 0`, `min-height: 0` --
   every one of which is now the shared default above. A media query whose
   body equals its own default is a rule that cannot be seen to fire. */
/* the caption shuffles with its event — a small rise under the media
   crossfade so the two read as one gesture (re-armed per swap in ados.js) */
.ad-center.ad-swap { animation: ad-cap-in 320ms cubic-bezier(0.22, 1, 0.36, 1) both; }
@keyframes ad-cap-in {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: none; }
}
.ad-line {
  font-size: 10px;
  line-height: 1.35;
  color: rgba(255,255,255,0.9);
  max-width: 100%;           /* panes narrower than any real phone wrap to a
                                second line — a date is never amputated */
  text-align: center;
}
.ad-tag {
  font-size: 8px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: rgba(255,255,255,0.7);
  border: 1px solid rgba(255,255,255,0.2);  /* their white/20 hairline */
  border-radius: 999px;
  padding: 2px 7px;
  white-space: nowrap;
}

/* ---- tworp — "2RP" editorial cover, miniaturized ----------------------- */
/* Its own faces, self-hosted subsets (assets/cards/PROVENANCE.md) — the
   wordmark rotates per session across these three display faces, which IS
   the site's identity. */
@font-face {
  font-family: 'Monoton';
  src: url('../../assets/cards/tworp/monoton-sub.woff2') format('woff2');
  font-display: swap;
}
@font-face {
  font-family: 'Sixtyfour';
  src: url('../../assets/cards/tworp/sixtyfour-sub.woff2') format('woff2');
  font-display: swap;
}
@font-face {
  font-family: 'Rubik Glitch';
  src: url('../../assets/cards/tworp/rubikglitch-sub.woff2') format('woff2');
  font-display: swap;
}

.j-pop-stage.rp {
  background: #000;   /* fallback behind the cover art */
}

/* the cover plate: media, scrim and masthead share one bounded frame */
.rp-media {
  position: relative;
  height: 176px;
  overflow: hidden;
}

.rp-cover {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transform: scale(1.0);
}
/* Ken Burns drift — scale 1.0 -> 1.06 over 9s, alternate; runs only while the
   stage is live (REDUCE stays static, plus the global REDUCE kill-switch in
   cards.css) */
.j-pop-stage.rp.rp-live .rp-cover {
  animation: rp-drift 9s ease-in-out alternate infinite;
}
@keyframes rp-drift {
  from { transform: scale(1.0); }
  to   { transform: scale(1.06); }
}

/* warm dark scrim, bottom half */
.rp-scrim {
  position: absolute;
  inset: 0;
  background: linear-gradient(
    to top,
    rgba(26,16,8,0.82),
    rgba(26,16,8,0.4) 50%,
    rgba(26,16,8,0) 100%
  );
  pointer-events: none;
}

/* tagline, masthead, and launch status, centred */
.rp-head {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding-bottom: 8px;    /* a breath above true centre — cover-mast optics */
  text-align: center;
  pointer-events: none;
}
.rp-masthead {
  /* Round 3: the shell head already says 2RP and carries the publication
     line, so the round-2 dek above the mark is gone and the masthead —
     which IS the artwork: the rotating face is the site's identity — steps
     down half a size to read as the cover's mark under the head rather
     than a second head. */
  font-size: 2.25rem;
  line-height: 0.9;
  color: rgba(255, 255, 255, 0.94);
  /* the face rotates per session (set inline in build); the halo keeps the
     white mark legible over arbitrary cover art */
  text-shadow:
    0 1px 14px rgba(0,0,0,0.85),
    0 0 4px rgba(0,0,0,0.7);
}

/* THE STATUS ENDS THE CARD (round 2): a letterpress band under the cover —
   the promise gets the same defined foot the other five give their doors,
   so the unlaunched thing no longer trails off inside its own art. Round 3:
   it wears the shared .card-door skeleton (same band, same type) with the
   compositor's rules flanking the words and nothing behind it — the one
   foot on the family sheet that is a status, not an exit. */
.rp-foot {
  display: flex;                             /* over .card-door's block: the
                                                flanking rules need a row */
  align-items: center;
  justify-content: center;
  gap: 0.7rem;
  text-indent: 0;                            /* rules are symmetric; no comp */
  background: #120b06;                       /* warm print-ink ground */
  border-top-color: rgba(244, 228, 193, 0.16);
}
.rp-foot::before,
.rp-foot::after {
  content: "";
  width: 22px;
  height: 1px;
  background: rgba(244, 228, 193, 0.28);     /* compositor's rules */
}
.rp-soon {
  color: rgba(244, 228, 193, 0.78);          /* parchment on ink */
}

/* ---- hivemind — a replayed query, in the house's own light ------------ */
/* Hivemind has no brand color; the card wears site.css's house gold
   (#d9a441) on the house-dark ground, and a terminal mono face. Ground is
   the brief's house-dark rgba(10,7,3,…) (#0a0703), kept at 0.94 so the
   transcript reads as a dim console against the panel. */

.j-pop-stage.hm {
  position: relative;
  height: auto;         /* the transcript sets the height — a narrow pane
                           wraps lines and the card breathes instead of
                           clipping its own foot */
  min-height: 170px;
  display: flex;
  flex-direction: column;
  background: rgba(10, 7, 3, 0.94);   /* house-dark ground #0a0703 */
}

/* Round 3: the round-2 title bar — wordmark plus descriptor — is gone;
   the shell head says HIVEMIND and carries the one thesis line, so the
   console opens straight onto the replayed query, which is the
   personality. */
.hm-console {
  position: relative;                   /* the librarian's corner */
  flex: 1 1 auto;
  padding: 0.7rem 1rem 0.2rem;
  font-family: ui-monospace, 'SF Mono', Menlo, Consolas, 'Liberation Mono', monospace;
  font-size: 0.72rem;
  line-height: 1.55;
  color: rgba(233, 221, 201, 0.82);
}

.hm-line {
  margin: 0 0 0.26rem;
  opacity: 0;
  transform: translateY(2px);
  transition: opacity 0.3s ease, transform 0.3s ease;
}
.hm-line.on { opacity: 1; transform: none; }

.hm-query { opacity: 1; transform: none; }   /* prompt always present */
.hm-query .hm-prompt { color: #d9a441; }     /* house gold */
.hm-query .hm-typed { color: rgba(233, 221, 201, 0.92); }

.hm-search { color: rgba(217, 164, 65, 0.55); }   /* muted amber */
.hm-spinner {
  display: inline-block;
  width: 0.55em;
  height: 0.55em;
  margin-right: 0.4em;
  vertical-align: -0.05em;
  border: 1px solid currentColor;
  border-top-color: transparent;
  border-radius: 50%;
  animation: hm-spin 0.7s linear infinite;
}
.hm-search.done .hm-spinner {
  animation: none;
  opacity: 0.35;
  border-top-color: currentColor;   /* search settled: a still full ring */
}
@keyframes hm-spin { to { transform: rotate(1turn); } }

.hm-hit { color: #d9a441; }                     /* house gold: the land */

.hm-ans {
  color: rgba(233, 221, 201, 0.78);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* THE RECORD IS A CHIP BAND (round 2, kept): the corpus as two compact
   chips — tag-like units, not a sentence. Round 3 moves the repo door out
   of this band onto the shared full-width foot below it, so Hivemind ends
   exactly where the other five do. */
.hm-foot {
  flex: 0 0 auto;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.4rem 0.6rem;
  padding: 0.45rem 1rem 0.5rem;
  border-top: 1px solid rgba(217, 164, 65, 0.15);
  font-family: ui-monospace, 'SF Mono', Menlo, Consolas, 'Liberation Mono', monospace;
}
.hm-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 5px;
}
.hm-chip {
  font-size: 8px;
  letter-spacing: 0.08em;
  padding: 2px 6px;
  border: 1px solid rgba(217, 164, 65, 0.28);
  color: rgba(217, 164, 65, 0.62);
  white-space: nowrap;
}
.hm-chip b {
  font-weight: 700;
  color: rgba(240, 200, 119, 0.92);     /* the number leads the tag */
}
/* the ending — house gold in the console's mono; geometry is .card-door's */
.hm-door {
  font-family: ui-monospace, 'SF Mono', Menlo, Consolas, 'Liberation Mono', monospace;
  border-top-color: rgba(217, 164, 65, 0.15);
  color: rgba(217, 164, 65, 0.8);       /* house gold, resting */
}
.hm-door:is(:hover, :focus-visible) { color: #f0c87a; }

.hm-mascot {
  position: absolute;
  right: 0.7rem;
  bottom: 0.15rem;
  height: 40px;
  width: auto;
  image-rendering: pixelated;
  opacity: 0.9;
}

/* ---- discord — banodoco.ai's own Community feed, miniaturized ---------- */
/* Live data is the site's own Supabase table daily_summaries (discord.js);
   colors below borrow its TopicCard language:
     parchment #f4e4c1  — the community section's warm text tone
     blurple   #5865F2  — Discord's brand blue, as a 2px left-rule nod
   Ground stays the house-dark panel (rgba(10,7,3,…) #0a0703). */

.j-pop-stage.dc {
  height: auto;         /* topics set the height — clamped copy keeps it
                           steady; narrow panes breathe instead of clipping */
  min-height: 176px;
  display: flex;
  flex-direction: column;
  background: rgba(10, 7, 3, 0.94);   /* house-dark ground #0a0703 */
  /* horizontal swipes are the card's (the topic walk), never the page's:
     pan-y keeps vertical pans native and leaves horizontal gestures to the
     swipe listeners in discord.js (2026-08-19) */
  touch-action: pan-y;
}

/* The live-status corner (round 3's beacon-plus-word row) is gone — the
   shell head already says DISCORD, and the body's own top padding now
   carries the top of the card (owner direction, 2026-08-31). */
.dc-body {
  flex: 1 1 auto;
  display: flex;
  align-items: center;
  padding: 0.65rem 1rem 0.35rem 1.1rem;
}

.dc-topic {
  border-left: 2px solid #5865F2;    /* blurple, a nod not a theme */
  padding-left: 0.7rem;
}

/* the shuffle: out through the top, in from below — two beats, eased so
   the swap reads as one motion (durations paired with discord.js LEAVE_MS) */
.dc-leave { animation: dc-out 190ms ease-in both; }
.dc-enter { animation: dc-in 300ms cubic-bezier(0.22, 1, 0.36, 1) both; }
@keyframes dc-out {
  from { opacity: 1; transform: none; }
  to   { opacity: 0; transform: translateY(-7px); }
}
@keyframes dc-in {
  from { opacity: 0; transform: translateY(9px); }
  to   { opacity: 1; transform: none; }
}

.dc-title {
  margin: 0 0 0.3rem;
  font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Helvetica Neue', Arial, sans-serif;
  font-size: 11px;
  line-height: 1.35;
  color: #f4e4c1;                     /* parchment */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.dc-text {
  margin: 0 0 0.35rem;
  font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Helvetica Neue', Arial, sans-serif;
  font-size: 10px;
  line-height: 1.45;
  color: rgba(244, 228, 193, 0.55);   /* parchment, muted */
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* provenance as tags (round 2): a channel and a day are chips, not copy */
.dc-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Helvetica Neue', Arial, sans-serif;
}
.dc-tag {
  font-size: 7px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  padding: 1px 6px 2px;
  border: 1px solid rgba(244, 228, 193, 0.22);
  border-radius: 999px;
  color: rgba(244, 228, 193, 0.5);
  white-space: nowrap;
}

.dc-dots {
  display: flex;
  justify-content: center;
  gap: 6px;
  padding: 0 0 0.5rem;
}

/* THE INVITE ENDS THE CARD (round 2): their blurple, quiet until reached
   for; geometry is .card-door's. */
.dc-door {
  font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Helvetica Neue', Arial, sans-serif;
  border-top-color: rgba(88, 101, 242, 0.28);
  color: rgba(136, 145, 242, 0.75);   /* blurple, lifted */
}
.dc-door:is(:hover, :focus-visible) {
  color: #a6adf7;
  background: rgba(88, 101, 242, 0.08);
}

.dc-dot {
  position: relative;
  width: 4px;
  height: 4px;
  padding: 0;
  border: 0;
  border-radius: 999px;
  background: rgba(244, 228, 193, 0.25);
  cursor: pointer;
  overflow: hidden;
  transition: width 0.25s ease;
}
/* the active dot stretches into a pill whose fill IS the load state for
   the next item — banodoco.ai's own progress grammar (its MediaGallery
   bars and EventSelector pills). Fill duration = discord.js CYCLE_MS. */
.dc-dot.is-on { width: 16px; background: rgba(88, 101, 242, 0.28); }
.dc-dot.is-on::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: 999px;
  background: #5865F2;               /* blurple fill */
  transform: translateX(-100%);
}
.dc-live .dc-dot.is-on.dc-filling::after {
  /* duration rides --dc-cycle (discord.js sets it per window: a shorter
     opener, then CYCLE_MS) so the fill never lies about the wait */
  animation: dc-fill var(--dc-cycle, 7s) linear both;
}
@keyframes dc-fill { from { transform: translateX(-100%); } to { transform: none; } }
/* no cycle running (single topic, REDUCE, fallback): the pill reads full */
.dc-dot.is-on:not(.dc-filling)::after { transform: none; }

/* ---- the endings (round 2): each stage closes in its own voice ------- */

/* artcompute — their green CTA voice; geometry is .card-door's */
.ac-door {
  border-top-color: rgba(255, 255, 255, 0.08);
  color: rgba(57, 255, 20, 0.65);
}
.ac-door:is(:hover, :focus-visible) {
  color: #39ff14;
  background: rgba(57, 255, 20, 0.06);   /* their bg-[#39ff14] wash */
}

/* discord — the topic's image, very small, beside the copy (live data
   only; the baked fallback has no media and an expired link never shows) */
.dc-topic { display: flex; align-items: flex-start; gap: 0.6rem; }
.dc-copy { flex: 1 1 auto; min-width: 0; }
.dc-thumb {
  flex: none;
  width: 46px; height: 46px;
  margin-top: 2px;
  object-fit: cover;
  border-radius: 4px;
  border: 1px solid rgba(244, 228, 193, 0.15);
  opacity: 0;
  transition: opacity 0.3s ease;
}
.dc-thumb.on { opacity: 1; }
.dc-thumb:not([src]) { display: none; }

/* ---- chip pictographs (Hannah, 2026-08-18: custom minimal icons in place
   of the white dots) — same footprint and dodge-pin as the dot they
   replace, same gold, one hairline drawing per initiative -------------- */
.j-hot-dot.j-hot-ico {
  width: 13px; height: 13px;
  border-radius: 0;
  background: none;
  box-shadow: none;
  color: rgba(240, 200, 119, 0.72);       /* the dot's own gold */
  filter: drop-shadow(0 0 5px rgba(240, 170, 60, 0.45));
}
.j-hot-dot.j-hot-ico svg {
  display: block;
  width: 100%; height: 100%;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.1;
  stroke-linecap: round;
  stroke-linejoin: round;
}
/* hover/hot/focus brightens the drawing exactly as it brightened the dot */
.j-hot:is(:hover, .hot, :focus-visible) .j-hot-dot.j-hot-ico {
  background: none;
  box-shadow: none;
  color: rgba(255, 228, 170, 0.98);
  filter: drop-shadow(0 0 7px rgba(240, 170, 60, 0.7));
}

/* THE INITIATIVE MARKER IS ONE JOINED SHAPE (2026-08-19). The six custom
   marks used to read as decorative bullets to the LEFT of each name. Here the
   pictograph sits in a small tab centred over the word: icon first, label
   second, one compact spatial marker.

   This is genuinely one optical fill. The first attempt painted translucent
   backgrounds on both the tab and body and overlapped them by 5px; alpha
   accumulated at the join and made a dirty shadow seam. The body (`::after`)
   now begins at y=16 exactly where the tab's own 16px backing ends — adjacent,
   never overlapping. ui.js separately centres the measured button on its
   projected world point, so the visual change does not lie about its node. */
.j-hot.j-hot--icon-tab {
  --j-chip-bg: rgba(12, 8, 3, 0.48);
  display: grid;
  grid-template-columns: max-content;
  grid-template-rows: 22px auto;
  justify-items: center;
  align-items: start;
  gap: 0;
  padding: 0;
  background: transparent;
  border-radius: 0;
}
.j-hot.j-hot--icon-tab::after {
  content: "";
  position: absolute;
  z-index: 0;
  /* Four pixels above the label's ink, matching its four-pixel lower pad.
     Starting at 12px made the capsule look top-heavy and swallowed the tab. */
  top: 16px;
  right: 0;
  bottom: 0;
  left: 0;
  border-radius: 999px;
  background: var(--j-chip-bg);
  opacity: var(--j-hot-shell-in, 1);
  transform: translateY(var(--j-hot-shell-y, 0));
  transform-origin: 50% 30%;
  pointer-events: none;
}
.j-hot.j-hot--icon-tab:is(:hover, .hot, :focus-visible) {
  --j-chip-bg: rgba(18, 12, 4, 0.72);
  background: transparent;
}
.j-hot--icon-tab .j-hot-dot.j-hot-ico {
  position: relative;
  z-index: 1;
  box-sizing: border-box;
  width: 22px;
  height: 22px;
  padding: 4.5px;
  margin: 0;
  border-radius: 0;
  background: transparent;
  /* A collision dodge moves this joined marker as ONE object. The generic
     ember dot is pinned back to its world node, but pinning this tab would
     tear it away from the capsule (and can put it underneath, as on Arca at
     430px). --j-dot-dy still keeps the invisible node hit-pad truthful. */
  opacity: var(--j-hot-icon-in, 1);
  transform: translateY(var(--j-hot-icon-y, 0)) scale(var(--j-hot-icon-scale, 1));
  transform-origin: 50% 75%;
}
.j-hot--icon-tab .j-hot-dot.j-hot-ico::before {
  content: "";
  position: absolute;
  z-index: -1;
  top: 0;
  left: 0;
  width: 22px;
  height: 16px;
  border-radius: 5px 5px 0 0;
  background: var(--j-chip-bg);
  pointer-events: none;
}
.j-hot--icon-tab .j-hot-dot.j-hot-ico svg {
  position: relative;
  z-index: 1;
  width: 13px;
  height: 13px;
  transform-origin: center;
  transition: transform 0.3s ease;
}
.j-hot--icon-tab .j-hot-label {
  position: relative;
  /* Sit above the single body paint. At z=0 the translucent ::after could
     composite over the glyphs, making the joined marker look inexplicably
     muddy even though the geometry itself was correct. */
  z-index: 1;
  grid-row: 2;
  box-sizing: border-box;
  min-height: 17px;
  margin-top: -4px;
  padding: 2px 12px 4px;
  border-radius: 0;
  background: transparent;
  line-height: 1.05;
  text-align: center;
  white-space: nowrap;
  opacity: var(--j-hot-label-in, 1);
  transform: translateY(var(--j-hot-label-y, 0));
}
/* The generic hotspot hover grows its dot. Here that dot is the TAB itself,
   so scaling it would split the silhouette; only the drawing breathes. */
.j-hot--icon-tab:is(:hover, .hot, :focus-visible) .j-hot-dot.j-hot-ico {
  background: transparent;
  transform: translateY(var(--j-hot-icon-y, 0)) scale(var(--j-hot-icon-scale, 1));
}
.j-hot--icon-tab:is(:hover, .hot, :focus-visible) .j-hot-dot.j-hot-ico svg {
  transform: scale(1.14);
}
@media (max-width: 720px), (max-height: 560px) {
  .j-hot.j-hot--icon-tab { --j-chip-bg: rgba(12, 8, 3, 0.66); }
  .j-hot.j-hot--icon-tab:is(:hover, .hot, :focus-visible) {
    --j-chip-bg: rgba(18, 12, 4, 0.78);
  }
}

/* Keep the initiative entry motion-free when the visitor has asked for less
   motion. The parent hotspot's visibility still changes, but its joined
   shell, icon, and name appear in their final positions immediately. */
@media (prefers-reduced-motion: reduce) {
  .j-hot.j-hot--icon-tab::after,
  .j-hot--icon-tab .j-hot-dot.j-hot-ico,
  .j-hot--icon-tab:is(:hover, .hot, :focus-visible) .j-hot-dot.j-hot-ico,
  .j-hot--icon-tab .j-hot-label {
    opacity: 1;
    transform: none;
  }
  .j-hot--icon-tab .j-hot-dot.j-hot-ico svg,
  .j-hot--icon-tab:is(:hover, .hot, :focus-visible) .j-hot-dot.j-hot-ico svg {
    transform: none;
    transition: none;
  }
}

/* ==================================================================== *
 * THE WINDOW WITH NOTHING BEHIND IT YET — Equip's two initiatives.
 * ====================================================================
 * Quark and Brötchen are real destinations whose interiors do not exist,
 * and this is that fact drawn rather than written. The shell is the SAME
 * shell the six built cards use — accented name, house short line, the
 * stage as the body, a door at the foot — and every one of those four
 * parts is present. What is missing is legibility, deliberately: the
 * description is out of focus, the stage is an abstract field where the
 * artwork will be, and the door is a door SHAPE with no destination
 * behind it (content/content.js gives these nodes no link, so the real
 * `.j-pop-link` is hidden and nothing in the accessible tree pretends to
 * lead anywhere).
 *
 * ONE THING STAYS SHARP, and it is the whole difference between
 * "intriguing" and "broken": the small caption at the foot of the field.
 * A blurred card with no legible word in it reads as a rendering failure;
 * a blurred card that says IN DEVELOPMENT reads as a promise. It is also
 * the only honest thing to hand a screen reader, which is why it is real
 * text and not a background.
 *
 * There is no module in journey/cards/ for either node. The interior is
 * this stylesheet's, keyed off `.j-pop-unbuilt` — see the third-state note
 * in journey/ui/popover-tier.js for why a builder that draws nothing would
 * have been the wrong shape.                                            */

.j-pop[data-node="quark"]    { --card-accent: #f0c274; --uc-a: #f0c274; --uc-b: #7fb2a4; --uc-c: #c98a5e; }
.j-pop[data-node="brotchen"] { --card-accent: #9fbcd0; --uc-a: #9fbcd0; --uc-b: #c9a2d8; --uc-c: #6f93a8; }
/* Two more stable crops of the same underground organism, so the pair sit
   in the same world as the six built cards rather than beside it. */
.j-pop[data-node="quark"]    { --myc-x: 4px;  --myc-y: -4px; --myc-w: 103%; --myc-h: 101%; }
.j-pop[data-node="brotchen"] { --myc-x: -5px; --myc-y: 3px;  --myc-w: 99%;  --myc-h: 105%; }

/* THE DESCRIPTION IS PRESENT AND OUT OF FOCUS — AND SO IS THE NAME (R3:
   "even the name should be blurred"). The words are real, the title is the
   chip's accessible name and the short line its aria-describedby text, and a
   screen reader reads both in full — only the pixels are withheld. The name
   keeps a lighter blur than the line: at its size and letter-spacing 2.2px
   makes it a coloured shape while 2.9px would make it a smear, and a shape
   in the card's accent is what still says "this window is somebody's". */
.j-pop.j-pop-unbuilt .j-pop-t {
  filter: blur(2.2px);
  opacity: 0.78;
  user-select: none;
}
.j-pop.j-pop-unbuilt .j-pop-s {
  filter: blur(2.9px);
  opacity: 0.62;
  user-select: none;
}
/* ...and a viewer who has asked for more contrast gets them back. An
   unreadable line is a design statement to most people and an obstacle to
   some, and the statement is worth less than the sentence. */
@media (prefers-contrast: more) {
  .j-pop.j-pop-unbuilt .j-pop-t,
  .j-pop.j-pop-unbuilt .j-pop-s { filter: none; opacity: 0.9; }
}

/* THE FIELD. Four soft colour masses over the house dark, blurred past any
   readable form, with the card's own two accents in them so the abstraction
   still belongs to this initiative and not to the other one. */
.j-pop-stage.j-pop-unbuilt {
  position: relative;
  aspect-ratio: 16 / 10;
  border-radius: 3px;
  background:
    radial-gradient(48% 56% at 24% 32%, color-mix(in oklab, var(--uc-a) 92%, transparent) 0%, transparent 70%),
    radial-gradient(44% 52% at 76% 28%, color-mix(in oklab, var(--uc-b) 82%, transparent) 0%, transparent 68%),
    radial-gradient(56% 50% at 60% 80%, color-mix(in oklab, var(--uc-c) 74%, transparent) 0%, transparent 72%),
    radial-gradient(38% 40% at 46% 54%, color-mix(in oklab, var(--uc-a) 40%, transparent) 0%, transparent 76%),
    radial-gradient(75% 75% at 40% 58%, rgba(34, 21, 8, 0.72) 0%, rgba(11, 8, 3, 0.94) 100%);
  filter: blur(13px) saturate(1.3) brightness(1.12);
  transform: scale(1.06);          /* hide the blur's own soft edge inside the box */
  transform-origin: 50% 50%;
  overflow: hidden;
}
/* The blur is on the field, so the caption and the door ride a sibling layer
   that is never blurred with it. */
.j-pop-stage.j-pop-unbuilt::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, rgba(6, 4, 1, 0) 46%, rgba(6, 4, 1, 0.42) 100%);
}
.j-pop.j-pop-unbuilt .j-pop-frame {
  position: relative;
  /* The blurred short line's own soft edge would otherwise sit on the field's;
     one line of air keeps the two out-of-focus surfaces legibly separate. */
  margin-top: 0.18rem;
}
/* THE CAPTION — the one legible thing, and the reason the card reads as
   intentional. Sits over the field's own foot, in the card's accent. */
.j-pop.j-pop-unbuilt .j-pop-frame::after {
  content: "IN DEVELOPMENT";
  position: absolute;
  left: 0.62rem;
  bottom: 2.1rem;
  z-index: 3;
  font-size: 0.55rem;
  letter-spacing: 0.26em;
  color: var(--card-accent, var(--gold-bright));
  opacity: 0.92;
  pointer-events: none;
}
/* THE DOOR SHAPE. The same geometry `.j-pop-rich .j-pop-link` occupies —
   a hairline, a label's worth of width, an arrow's worth of gap — with no
   text, no href and no tab stop. It is the fourth part of the structure,
   present and unreadable like the rest. */
.j-pop.j-pop-unbuilt .j-pop-frame > .j-pop-stage + * { display: none; }
.j-pop.j-pop-unbuilt::after {
  content: "";
  order: 3;
  display: block;
  height: 1.6rem;
  margin: 0.55rem 0 0;
  border-top: 1px solid rgba(217, 164, 65, 0.16);
  background:
    linear-gradient(90deg,
      color-mix(in oklab, var(--card-accent) 34%, transparent) 0 42%,
      transparent 42% 88%,
      color-mix(in oklab, var(--card-accent) 44%, transparent) 88% 100%);
  background-size: 100% 0.42rem;
  background-position: 0 0.62rem;
  background-repeat: no-repeat;
  filter: blur(2.4px);
  opacity: 0.55;
  pointer-events: none;
}

/* The field breathes, slowly, and only while the card is open — the same
   rule the six built stages follow, and it parks under reduced motion with
   everything else (the block at the top of this file). */
@keyframes j-uc-drift {
  0%   { transform: scale(1.06) translate3d(0, 0, 0); }
  50%  { transform: scale(1.10) translate3d(-1.4%, -1.1%, 0); }
  100% { transform: scale(1.06) translate3d(0, 0, 0); }
}
.j-pop.j-pop-open .j-pop-stage.j-pop-unbuilt,
.j-pop:hover .j-pop-stage.j-pop-unbuilt {
  animation: j-uc-drift 14s ease-in-out infinite;
}
@media (prefers-reduced-motion: reduce) {
  .j-pop-stage.j-pop-unbuilt { animation: none !important; }
}

/* Withheld names keep the exact initiative marker housing and icon above.
   Blur is intentional content treatment, including on touch devices and in
   increased-contrast mode; neither setting should reveal the name's ink. */
.j-hot--icon-tab.j-hot--soon .j-hot-label {
  -webkit-filter: blur(2.6px);
  filter: blur(2.6px);
  user-select: none;
}
