/* =========================================================================
   GIGA-GLOSSARY — PRISMATIC (slate glass)
   Light mode. Structure is shared: giga-kosmic-chorus.css and giga-radio.css
   carry the layout, and everything chromatic in them now reads from tokens,
   so this file is almost entirely a palette.

   The ground is a large soft wash that follows the cursor — set as
   --px / --py (0-1) by giga-prismatic.js. It is deliberately slow and low
   contrast: a light theme has nowhere to hide a bright gradient, and the
   readings are the subject.
   ========================================================================= */

[data-theme="prismatic"] {
  /* styles.css already sets --bg (the slate gradient), --text, --muted,
     --accent, --panel and the glass properties for this theme. Everything
     here is what the newer components need on top. */

  /* --- surfaces: translucent white over the slate, per the original --- */
  --ink: rgba(15, 23, 42, .55);
  --panel: rgba(255, 255, 255, .15);
  --panel-2: rgba(255, 255, 255, .11);
  --panel-3: rgba(255, 255, 255, .09);
  --panel-4: rgba(255, 255, 255, .13);
  --panel-5: rgba(255, 255, 255, .1);
  --panel-6: rgba(255, 255, 255, .16);
  --panel-7: rgba(255, 255, 255, .12);
  --field: rgba(255, 255, 255, .14);
  --skel: rgba(255, 255, 255, .2);
  --band-off: rgba(255, 255, 255, .12);

  /* --- lines: the original leans on a single white hairline --- */
  --line: rgba(255, 255, 255, .22);
  --line-soft: rgba(255, 255, 255, .14);
  --line-2: rgba(255, 255, 255, .17);
  --line-3: rgba(255, 255, 255, .24);
  --line-4: rgba(255, 255, 255, .3);
  --line-5: rgba(255, 255, 255, .24);
  --line-6: rgba(255, 255, 255, .36);
  --line-7: rgba(255, 255, 255, .44);

  /* --- greys: on slate these are LIGHTER, not darker. Every one carries
         7-10px letterspaced mono somewhere, so the dim end has to clear
         4.5:1 against #334155 rather than against black. --- */
  --grey-1: #94a3b8;
  --grey-2: #cbd5e1;
  --grey-3: #cbd5e1;
  --grey-4: #dbe4ee;
  --grey-5: #dbe4ee;
  --ghost: #b6c2d1;
  --faint: #c3cdda;
  --dim: #dbe4ee;
  --label: #e2e8f0;
  --muted: #cbd5e1;

  /* --- ink --- */
  --text: #f1f5f9;
  --text-2: #e2e8f0;
  --text-3: #dbe4ee;
  --text-4: #f1f5f9;
  --text-5: #f8fafc;
  --text-6: #e8eef6;
  --text-7: #cbd5e1;
  --text-hi: #f8fafc;

  --accent: #60a5fa;
  --accent-hi: #93c5fd;
  --sel-fg: #0f172a;

  --warn-fg: #fca5a5;
  --warn-bd: rgba(239, 68, 68, .5);
  --warn-tx: #fecaca;

  --scrim: 30, 41, 59;

  --font-display: 'Instrument Serif', Georgia, serif;
  --font-sans: 'IBM Plex Sans', system-ui, -apple-system, sans-serif;
  --font-mono: 'IBM Plex Mono', ui-monospace, 'SF Mono', Menlo, monospace;
}

/* =========================================================================
   ALTITUDE HUES
   Lifted for a mid-slate ground: the dark ramp is tuned for near-black and
   its deep end (teal, umber, infrared) loses separation here.

   Selectors must name the element that CARRIES the class — .voice.alt-x and
   friends. The base rules for these live unscoped in giga-kosmic-chorus.css
   at (0,2,0), so the theme prefix here is what wins.
   ========================================================================= */
[data-theme="prismatic"] .voice.alt-infrared,
[data-theme="prismatic"] .card.alt-infrared,
[data-theme="prismatic"] .focus.alt-infrared,
[data-theme="prismatic"] .focus-index-row.alt-infrared,
[data-theme="prismatic"] .ag-lane.alt-infrared,
[data-theme="prismatic"] .address.alt-infrared { --alt: #e59b96; }
[data-theme="prismatic"] .voice.alt-magenta,
[data-theme="prismatic"] .card.alt-magenta,
[data-theme="prismatic"] .focus.alt-magenta,
[data-theme="prismatic"] .focus-index-row.alt-magenta,
[data-theme="prismatic"] .ag-lane.alt-magenta,
[data-theme="prismatic"] .address.alt-magenta { --alt: #f0a3d0; }
[data-theme="prismatic"] .voice.alt-red,
[data-theme="prismatic"] .card.alt-red,
[data-theme="prismatic"] .focus.alt-red,
[data-theme="prismatic"] .focus-index-row.alt-red,
[data-theme="prismatic"] .ag-lane.alt-red,
[data-theme="prismatic"] .address.alt-red { --alt: #ff9c93; }
[data-theme="prismatic"] .voice.alt-amber,
[data-theme="prismatic"] .card.alt-amber,
[data-theme="prismatic"] .focus.alt-amber,
[data-theme="prismatic"] .focus-index-row.alt-amber,
[data-theme="prismatic"] .ag-lane.alt-amber,
[data-theme="prismatic"] .address.alt-amber { --alt: #f5c876; }
[data-theme="prismatic"] .voice.alt-umber,
[data-theme="prismatic"] .card.alt-umber,
[data-theme="prismatic"] .focus.alt-umber,
[data-theme="prismatic"] .focus-index-row.alt-umber,
[data-theme="prismatic"] .ag-lane.alt-umber,
[data-theme="prismatic"] .address.alt-umber { --alt: #d4bdb3; }
[data-theme="prismatic"] .voice.alt-orange,
[data-theme="prismatic"] .card.alt-orange,
[data-theme="prismatic"] .focus.alt-orange,
[data-theme="prismatic"] .focus-index-row.alt-orange,
[data-theme="prismatic"] .ag-lane.alt-orange,
[data-theme="prismatic"] .address.alt-orange { --alt: #ffab7a; }
[data-theme="prismatic"] .voice.alt-green,
[data-theme="prismatic"] .card.alt-green,
[data-theme="prismatic"] .focus.alt-green,
[data-theme="prismatic"] .focus-index-row.alt-green,
[data-theme="prismatic"] .ag-lane.alt-green,
[data-theme="prismatic"] .address.alt-green { --alt: #8fe0a8; }
[data-theme="prismatic"] .voice.alt-teal,
[data-theme="prismatic"] .card.alt-teal,
[data-theme="prismatic"] .focus.alt-teal,
[data-theme="prismatic"] .focus-index-row.alt-teal,
[data-theme="prismatic"] .ag-lane.alt-teal,
[data-theme="prismatic"] .address.alt-teal { --alt: #7fd6e8; }
[data-theme="prismatic"] .voice.alt-turquoise,
[data-theme="prismatic"] .card.alt-turquoise,
[data-theme="prismatic"] .focus.alt-turquoise,
[data-theme="prismatic"] .focus-index-row.alt-turquoise,
[data-theme="prismatic"] .ag-lane.alt-turquoise,
[data-theme="prismatic"] .address.alt-turquoise { --alt: #7cf0e4; }
[data-theme="prismatic"] .voice.alt-indigo,
[data-theme="prismatic"] .card.alt-indigo,
[data-theme="prismatic"] .focus.alt-indigo,
[data-theme="prismatic"] .focus-index-row.alt-indigo,
[data-theme="prismatic"] .ag-lane.alt-indigo,
[data-theme="prismatic"] .address.alt-indigo { --alt: #b4a8ff; }

/* =========================================================================
   THE BACKGROUND
   app.js's initprismaticDynamicBackground() already paints this theme: a
   three-stop HSL gradient lerped toward the cursor at 0.08/frame, written to
   --dynamic-bg and applied by styles.css on body::before with !important.
   Nothing to add here — a second wash would only fight it.

   All that is needed is to keep our own layers above it.
   ========================================================================= */
[data-theme="prismatic"] .stage,
[data-theme="prismatic"] .threshold,
[data-theme="prismatic"] .tune-strip {
  position: relative;
  z-index: 1;
}

/* these own their positioning — only the stacking order is ours to set */
[data-theme="prismatic"] .app-header { z-index: 50; }
[data-theme="prismatic"] .status { z-index: 60; }
[data-theme="prismatic"] .focus { z-index: 100; }

/* the kosmic ground mirror would sit on top of the slate gradient */
[data-theme="prismatic"] .ground { display: none; }

/* =========================================================================
   CHROME
   ========================================================================= */
[data-theme="prismatic"] body:not(.is-empty) .app-header,
[data-theme="prismatic"] .app-header {
  border-bottom: 1px solid var(--line);
}

[data-theme="prismatic"] .brand {
  font-family: var(--font-display);
  color: var(--accent);
}

[data-theme="prismatic"] .input-wrap input {
  background: rgba(255, 255, 255, .14);
  backdrop-filter: blur(20px);
  border: 1px solid var(--line);
  color: var(--text);
}

[data-theme="prismatic"] .input-wrap input::placeholder { color: var(--grey-1); }
[data-theme="prismatic"] .input-wrap input:focus { border-color: var(--accent); outline: none; }

[data-theme="prismatic"] .primary {
  background: var(--accent);
  color: #0f172a;
  border: 0;
}

[data-theme="prismatic"] .primary:hover { background: var(--accent-hi); }

[data-theme="prismatic"] .ghost {
  background: rgba(255, 255, 255, .1);
  border: 1px solid var(--line);
  color: var(--muted);
}

[data-theme="prismatic"] .ghost:hover { border-color: var(--accent); color: var(--accent); }

[data-theme="prismatic"] select,
[data-theme="prismatic"] #themeSelect {
  background: rgba(255, 255, 255, .12);
  border: 1px solid var(--line);
  color: var(--muted);
}

[data-theme="prismatic"] .chip,
[data-theme="prismatic"] .chip--sm {
  background: rgba(255, 255, 255, .12);
  border: 1px solid var(--line);
  color: var(--muted);
}

[data-theme="prismatic"] .chip:hover { border-color: var(--accent); color: var(--accent); }

[data-theme="prismatic"] .status {
  background: rgba(255, 255, 255, .1);
  border-bottom: 1px solid var(--line);
  color: var(--accent);
}

[data-theme="prismatic"] .spinner {
  border-color: rgba(255, 255, 255, .28);
  border-top-color: var(--accent);
}

/* =========================================================================
   CARDS
   The dark theme separates a card from the page with a lift in luminance.
   On paper that inverts: the card is white and the page is the tint, so the
   hairline and a small shadow do the work instead of a fill.
   ========================================================================= */
/* NO backdrop-filter here. Every frame the cursor gradient repaints the
   whole viewport, and each blurred element above it must re-read and re-blur
   its backdrop on that repaint — Chrome recomputes on demand where Safari
   and Firefox cache. With a blur per card that is N re-blurs x 60 repaints a
   second, which is what made Chrome specifically lag. The translucent fill
   plus hairline reads as glass without it. */
[data-theme="prismatic"] .chorus .voice,
[data-theme="prismatic"] .card {
  background: rgba(255, 255, 255, .10);
  border: 1px solid rgba(255, 255, 255, .2);
  box-shadow: 0 8px 32px rgba(0, 0, 0, .15);
  /* keep each card's paint out of its neighbours' */
  contain: paint;
}

[data-theme="prismatic"] .chorus .voice:hover {
  background: rgba(255, 255, 255, .22);
  border-color: rgba(255, 255, 255, .38);
  box-shadow: 0 12px 40px rgba(0, 0, 0, .2);
}

[data-theme="prismatic"] .core {
  background: rgba(255, 255, 255, .17);
  backdrop-filter: blur(20px) saturate(180%);
  -webkit-backdrop-filter: blur(20px) saturate(180%);
  border: 1px solid rgba(255, 255, 255, .22);
  box-shadow: 0 12px 40px rgba(0, 0, 0, .2);
}

/* the generated art is drawn for a black ground, so it keeps one */
[data-theme="prismatic"] .core-img-wrap,
[data-theme="prismatic"] .hero-img-wrap { background: #0b0d12; }

[data-theme="prismatic"] .voice-add {
  border-color: rgba(255, 255, 255, .3);
  background: rgba(255, 255, 255, .06);
}

[data-theme="prismatic"] .voice-add:hover { border-color: var(--accent); background: rgba(255, 255, 255, .14); }

/* =========================================================================
   THRESHOLD
   ========================================================================= */
[data-theme="prismatic"] .th-head { color: var(--text-hi); }
[data-theme="prismatic"] .th-sub { color: var(--muted); }

/* Feather with a MASK, not an overlay. Kosmic's ::after fades to var(--bg),
   which here is a cursor-driven GRADIENT — invalid in a colour slot, which is
   why it was disabled and the effect was lost. Masking the plate dissolves
   the image AND its black ground into whatever is actually behind, so the
   bars at the edges stop existing rather than being covered up. */
[data-theme="prismatic"] .th-plate {
  background: transparent;
  border: 0;
  /* ONE layer, no compositing. Two mask layers default to composite: add —
     a union — so the fully opaque vertical layer filled the horizontal fade
     straight back in and the left/right edges stayed hard. An ellipse
     feathers all four edges at once and cannot be undone by a sibling. */
  -webkit-mask-image: radial-gradient(112% 150% at 50% 50%, #000 42%, rgba(0,0,0,.75) 66%, transparent 100%);
  mask-image: radial-gradient(112% 150% at 50% 50%, #000 42%, rgba(0,0,0,.75) 66%, transparent 100%);
}

/* the art is drawn for black, so it keeps a ground — but only under itself,
   inside the mask, so no bar reaches the page edge */
[data-theme="prismatic"] .th-plate img,
[data-theme="prismatic"] .th-plate video {
  background: #0b0d12;
  /* cover, so the slit is filled edge to edge and never letterboxed */
  object-fit: cover;
}

/* the aperture's own veil is built for a dark page; on paper it only needs
   to soften the edges into the background */
/* ::after is replaced by the mask above */
[data-theme="prismatic"] .th-plate::after { display: none; }

[data-theme="prismatic"] .threshold::before { display: none; }



/* =========================================================================
   FOCUS
   ========================================================================= */
[data-theme="prismatic"] .focus { background: rgba(20, 29, 45, .72); backdrop-filter: blur(8px); }

[data-theme="prismatic"] .focus-sheet {
  background: rgba(255, 255, 255, .16);
  backdrop-filter: blur(24px) saturate(180%);
  border: 1px solid rgba(255, 255, 255, .22);
  box-shadow: 0 30px 80px rgba(0, 0, 0, .35);
}

[data-theme="prismatic"] .focus-porthole-eye { background: #0b0d12; }

/* =========================================================================
   THE CONSOLE
   It carries its own token block (so the standalone tuner page is
   self-sufficient), which means the light values have to be re-stated here.
   Higher specificity plus later load order wins.
   ========================================================================= */
[data-theme="prismatic"] .console {
  /* The console declares its own token set so the standalone tuner page is
     self-sufficient. That set is DARK by default, so under this theme it has
     to be restated in slate — otherwise the deck labels and station titles
     keep near-black paper ink on translucent slate panels (~1.5:1).
     --ink stays dark: it is the porthole's ground, and the generated art is
     drawn for black. */
  --ink: #0b0d12;
  --panel: rgba(255, 255, 255, .12);
  --panel-2: rgba(255, 255, 255, .1);
  --panel-3: rgba(255, 255, 255, .08);
  --panel-4: rgba(255, 255, 255, .12);
  --panel-5: rgba(255, 255, 255, .1);
  --panel-6: rgba(255, 255, 255, .15);
  --panel-7: rgba(255, 255, 255, .1);
  --field: rgba(255, 255, 255, .13);
  --skel: rgba(255, 255, 255, .2);
  --band-off: rgba(255, 255, 255, .12);
  --line: rgba(255, 255, 255, .22);
  --line-soft: rgba(255, 255, 255, .15);
  --line-2: rgba(255, 255, 255, .18);
  --line-3: rgba(255, 255, 255, .24);
  --line-4: rgba(255, 255, 255, .3);
  --line-5: rgba(255, 255, 255, .24);
  --line-6: rgba(255, 255, 255, .36);
  --line-7: rgba(255, 255, 255, .44);
  --grey-1: #94a3b8;
  --grey-2: #cbd5e1;
  --grey-3: #cbd5e1;
  --grey-4: #dbe4ee;
  --grey-5: #dbe4ee;
  --ghost: #b6c2d1;
  --faint: #c3cdda;
  --dim: #dbe4ee;
  --label: #e2e8f0;
  --muted: #cbd5e1;
  --text: #f1f5f9;
  --text-2: #e2e8f0;
  --text-3: #dbe4ee;
  --text-4: #f1f5f9;
  --text-5: #f8fafc;
  --text-6: #e8eef6;
  --text-7: #cbd5e1;
  --text-hi: #f8fafc;
  --accent: #60a5fa;
  --accent-hi: #93c5fd;
  --sel-fg: #0f172a;
  --warn-fg: #fca5a5;
  --warn-bd: rgba(239, 68, 68, .5);
  --warn-tx: #fecaca;

  background: rgba(255, 255, 255, .13);
  backdrop-filter: blur(20px) saturate(180%);
  -webkit-backdrop-filter: blur(20px) saturate(180%);
  border: 1px solid rgba(255, 255, 255, .2);
  box-shadow: 0 12px 40px rgba(0, 0, 0, .2);
}

[data-theme="prismatic"] .console .con-head { background: rgba(255, 255, 255, .07); }
[data-theme="prismatic"] .console .deck { background: rgba(15, 23, 42, .18); }
[data-theme="prismatic"] .console .bank { background: rgba(15, 23, 42, .18); }

/* the porthole and the state rings sit on paper now */
[data-theme="prismatic"] .console .rd-quad-cell,
[data-theme="prismatic"] .console .rd-per-cell { background: rgba(255, 255, 255, .08); }

[data-theme="prismatic"] .console .porthole-eye { background: #0b0d12; }

[data-theme="prismatic"] .console .dial i { border-color: rgba(255, 255, 255, .28); }

[data-theme="prismatic"] .console .station { background: rgba(255, 255, 255, .12); }

/* on a light ground the tuned station needs more than an 12% tint */
[data-theme="prismatic"] .console .station.on {
  background: color-mix(in srgb, var(--sc) 26%, transparent);
  border-color: color-mix(in srgb, var(--sc) 60%, transparent);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--sc) 22%, transparent);
}

[data-theme="prismatic"] .console .tune-in { color: #0f172a; }

/* the selected-control fill is an alpha wash of the altitude colour, which is
   near-invisible on white — give it a solid tint and dark ink */
[data-theme="prismatic"] .console .rd-quad-cell.on,
[data-theme="prismatic"] .console .rd-per-cell.on,
[data-theme="prismatic"] .console .fam.on {
  background: color-mix(in srgb, var(--alt) 26%, transparent);
  border-color: var(--alt);
}

[data-theme="prismatic"] .console .rd-quad-cell.on b,
[data-theme="prismatic"] .console .rd-per-cell.on b,
[data-theme="prismatic"] .console .fam.on b { color: var(--text-hi); }

[data-theme="prismatic"] .console .rd-state-row.on { background: color-mix(in srgb, var(--alt) 22%, transparent); }
[data-theme="prismatic"] .console .rd-state-row.on b { color: var(--text-hi); }

/* =========================================================================
   ARRANGEMENT BAR
   ========================================================================= */
[data-theme="prismatic"] .arr-btn {
  background: rgba(255, 255, 255, .1);
  border: 1px solid var(--line);
  color: var(--muted);
}

[data-theme="prismatic"] .arr-btn:hover { border-color: var(--accent); color: var(--accent); }

[data-theme="prismatic"] .arr-btn.is-on {
  background: var(--accent);
  border-color: var(--accent);
  color: #0f172a;
}

[data-theme="prismatic"] .arr-btn--tuner.is-on {
  background: rgba(255, 255, 255, .14);
  border-color: var(--accent);
  color: var(--accent);
}

/* =========================================================================
   SPECTRUM / QUADRANTS
   ========================================================================= */
/* the sticky spectrum head is the same bar as the grid's, so it gets the
   same glass rather than a slate fill of its own */
[data-theme="prismatic"] .spec-head {
  background: rgba(255, 255, 255, .1);
  border-bottom: 1px solid rgba(255, 255, 255, .18);
  backdrop-filter: blur(20px) saturate(180%);
}

[data-theme="prismatic"] .chorus.arr-spectrum .ag-rail::-webkit-scrollbar-track { background: var(--panel-6); }
[data-theme="prismatic"] .chorus.arr-spectrum .ag-rail::-webkit-scrollbar-thumb { background: var(--line-6); }

[data-theme="prismatic"] .rail-nav {
  background: rgba(51, 65, 85, .88);
  border-color: rgba(255, 255, 255, .24);
  color: var(--text);
}

/* =========================================================================
   HARDCODED rgba SURFACES
   These were written as literal rgba() in the shared files rather than as
   tokens, so a palette swap cannot reach them. Overridden by hand.
   ========================================================================= */
[data-theme="prismatic"] .tune-strip {
  background: rgba(255, 255, 255, .08);
  border-color: rgba(255, 255, 255, .16);
}

[data-theme="prismatic"] .th-tune #coordTools,
[data-theme="prismatic"] .th-block .chip,
[data-theme="prismatic"] .th-starters .chip {
  background: rgba(255, 255, 255, .12);
  border-color: rgba(255, 255, 255, .2);
}

/* card body copy: #b8bac8 was hardcoded and lands at ~2:1 on slate */
[data-theme="prismatic"] .voice-quote,
[data-theme="prismatic"] .voice p,
[data-theme="prismatic"] .meta-description { color: #e8eef6; }

[data-theme="prismatic"] .threshold::before { display: none; }

/* --- last contrast pass on slate ------------------------------------- */
/* the disclosure summary and card index were hardcoded greys tuned for
   near-black; on mid slate they fall under 3:1 */
[data-theme="prismatic"] #coordTools > details > summary,
[data-theme="prismatic"] .th-tune > summary,
[data-theme="prismatic"] .th-adv > summary,
[data-theme="prismatic"] .th-help > summary { color: #e2e8f0; }

[data-theme="prismatic"] .voice-n,
[data-theme="prismatic"] .card-n,
[data-theme="prismatic"] .voice .n { color: #cbd5e1; }

[data-theme="prismatic"] .hint,
[data-theme="prismatic"] .hint.small,
[data-theme="prismatic"] .tune-label,
[data-theme="prismatic"] .arrange-label,
[data-theme="prismatic"] .chorus-label-text,
[data-theme="prismatic"] .chorus-count,
[data-theme="prismatic"] .meta-label,
[data-theme="prismatic"] .section-title,
[data-theme="prismatic"] .th-label,
[data-theme="prismatic"] .th-eyebrow { color: #dbe4ee; }

[data-theme="prismatic"] .hint code,
[data-theme="prismatic"] .th-help code { color: #bfdbfe; }

/* chips: the pill has a fixed height from styles.css and long starters wrap
   out of it on this theme's larger type */
[data-theme="prismatic"] .chip,
[data-theme="prismatic"] .chip--sm {
  height: auto !important;
  min-height: 32px;
  display: inline-flex;
  align-items: center;
  white-space: nowrap;
}

/* the arrangement bar's rule was a near-black hairline */
[data-theme="prismatic"] .chorus-rule { background: rgba(255, 255, 255, .18); }

/* html paints var(--bg); under this theme that is styles.css's slate
   gradient, and the shared rule would otherwise repeat it per element */
[data-theme="prismatic"] body { background: transparent; }

/* --- surfaces the palette cannot reach ------------------------------- */
/* The composing placeholders and the spectrum's sticky core are near-black
   fills written as literal rgba() in the shared files, so a token swap does
   nothing for them. On slate they read as holes in the page. */
[data-theme="prismatic"] .chorus .loading-card,
[data-theme="prismatic"] .loading-card {
  background: rgba(255, 255, 255, .12);
  backdrop-filter: blur(20px) saturate(180%);
  border: 1px solid rgba(255, 255, 255, .2);
}

[data-theme="prismatic"] .skel-line,
[data-theme="prismatic"] .loading-card .skel-line {
  background: rgba(255, 255, 255, .22) !important;
}

[data-theme="prismatic"] .spec-head .core,
[data-theme="prismatic"] .chorus.arr-spectrum .core {
  background: rgba(255, 255, 255, .14);
  backdrop-filter: blur(20px) saturate(180%);
  border: 1px solid rgba(255, 255, 255, .2);
}

/* the threshold aperture keeps its black ground — the generated art is drawn
   for one — but the hard edges into slate need softening */


/* the status toast, in glass */
[data-theme="prismatic"] .status {
  background: rgba(255, 255, 255, .16);
  backdrop-filter: blur(20px) saturate(180%);
  border-color: rgba(255, 255, 255, .24);
  color: var(--text);
}

/* composing placeholders must not be darker than the finished cards */
[data-theme="prismatic"] body.gg-composing .chorus .voice,
[data-theme="prismatic"] body.gg-composing .chorus .loading-card,
[data-theme="prismatic"] .chorus .loading-card {
  background: rgba(255, 255, 255, .13);
  border-color: rgba(255, 255, 255, .2);
}


/* =========================================================================
   CHROME PERFORMANCE
   The blur budget is spent on the few LARGE surfaces only. Anything that
   repeats per reading gets translucency without a filter.
   ========================================================================= */
[data-theme="prismatic"] .chorus .loading-card,
[data-theme="prismatic"] .chorus .voice,
[data-theme="prismatic"] .card,
[data-theme="prismatic"] .console .station,
[data-theme="prismatic"] .chip,
[data-theme="prismatic"] .arr-btn,
[data-theme="prismatic"] .ghost,
[data-theme="prismatic"] .input-wrap input,
/* styles.css's own prismatic block blurs these too — small, repeated, and
   from the old card markup */
[data-theme="prismatic"] .coord-value,
[data-theme="prismatic"] .quad-cell,
[data-theme="prismatic"] .quad-grid,
[data-theme="prismatic"] .tune-strip,
[data-theme="prismatic"] .panel {
  backdrop-filter: none !important;
  -webkit-backdrop-filter: none !important;
}

/* the moving gradient lives behind everything and never needs to composite
   with what is above it */
[data-theme="prismatic"] body::before { will-change: opacity; contain: strict; }

/* while composing, stand the remaining blurs down entirely — that is when
   the placeholder shimmer and the loop are also running */
[data-theme="prismatic"] body.gg-composing .core,
[data-theme="prismatic"] body.gg-composing .console,
[data-theme="prismatic"] body.gg-composing .spec-head {
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}

/* =========================================================================
   STICKY BACKDROPS
   --bg here is a GRADIENT, and a gradient used as a sticky backdrop restarts
   inside its own box — that was the slab over Spectrum, and over the header
   once its blur was stood down for Chrome.

   The bug in the first attempt was the VALUE, not the technique: I hand-copied
   a three-stop gradient, while styles.css paints four stops at 0/30/70/100
   AND hue-shifts them live through --dynamic-bg. Reading the same custom
   property, with the same fallback, is the only way to match — and
   background-attachment: fixed is what aligns it to the viewport, exactly as
   styles.css does for the page itself.

   (A position:fixed pseudo-element would be cheaper on scroll, but overflow
   cannot clip a fixed child — its containing block is the viewport — so it
   would cover the page instead of the header.)
   ========================================================================= */
[data-theme="prismatic"] body.arr-is-spectrum .chorus-label,
[data-theme="prismatic"] body.arr-is-spectrum .spec-head,
[data-theme="prismatic"] .chorus.arr-spectrum .ag-lane .ag-head,
[data-theme="prismatic"] body .app-header,
[data-theme="prismatic"] body:not(.is-empty) .app-header {
  background-image: var(--dynamic-bg, linear-gradient(135deg, #1e293b 0%, #334155 30%, #475569 70%, #64748b 100%));
  background-color: #1e293b;
  background-attachment: fixed;
  background-size: 100vw 100vh;
  background-position: 0 0;
  background-repeat: no-repeat;
}

/* the header keeps a glass tint, but laid over that backdrop rather than over
   the scrolling page: it had gone see-through when the composing pass stood
   its blur down, so buttons and text read straight through it */
[data-theme="prismatic"] .app-header {
  box-shadow: inset 0 0 0 999px rgba(255, 255, 255, .09);
  border-bottom: 1px solid rgba(255, 255, 255, .16);
}

/* opaque by construction now, so no blur is needed in this set — which also
   returns three surfaces to Chrome's budget */
[data-theme="prismatic"] .app-header,
[data-theme="prismatic"] body.arr-is-spectrum .chorus-label,
[data-theme="prismatic"] body.arr-is-spectrum .spec-head {
  backdrop-filter: none !important;
  -webkit-backdrop-filter: none !important;
}

/* the core is a card in every view; the slab was flattening it */
[data-theme="prismatic"] .spec-head .core {
  background: rgba(255, 255, 255, .17);
  border: 1px solid rgba(255, 255, 255, .22);
  box-shadow: 0 12px 40px rgba(0, 0, 0, .2);
}


/* =========================================================================
   CONTRAST REPAIRS
   All three of these are the same class of mistake: a value tuned against
   near-black, reused on a light slate where it has almost no separation.
   ========================================================================= */

/* The unlit cells were --grey-2 (#cbd5e1 here) — a LIGHT grey drawn on a
   light card, so the empty half of the glyph vanished and the diagram lost
   its meaning. On this theme the unlit cell has to go DARKER than the card,
   not lighter. */
[data-theme="prismatic"] .qg-cell {
  border-color: rgba(15, 23, 42, .42);
  background: rgba(15, 23, 42, .06);
}

[data-theme="prismatic"] .qg-code { color: rgba(15, 23, 42, .62); }

/* Small letterspaced mono needs more separation than body text at the same
   ratio, and these were the lightest values in the palette:
     .voice-num       the card index
     .meta-hint       "growing fuller with each new perspective"
     .ag-n / .cf-q    zone counts, the rating question
   --grey-1 is the darkest grey here, so point the small-type tokens at it.

   --grey-4 belongs in this list and was missed. Its consumers are all small
   type on a LIGHT ground here:
     .voice-coord               "Leadership · LR 1p · Physical", 9px mono
     .tune-hint                 12px
     .ag-lane.is-empty .ag-code an unreached altitude's label
   At #dbe4ee — a near-white chosen for Kosmic's near-black cards — that
   signature line washed out against Prismatic's light glass. It carries the
   whole address, so it is the last thing that should be hard to read.
   Matched to --faint, which was already reasoned for exactly this: small
   letterspaced mono on this ground. */
[data-theme="prismatic"] {
  --grey-1: #64748b;
  --ghost: #64748b;
  --faint: #56637a;
  --grey-4: #56637a;
  --grey-5: #64748b;
  --label: #f1f5f9;
}

[data-theme="prismatic"] { --num-ink: #5b6a82; }
[data-theme="prismatic"] .meta-hint { color: #5b6a82; }
[data-theme="prismatic"] .voice-coord,
[data-theme="prismatic"] .nx-sig,
[data-theme="prismatic"] .station .st-freq { color: #e7ecf4; }

/* ---------- 3. the threshold aperture ----------
   Two mask layers default to composite: ADD — a union — so the opaque part
   of the vertical fade filled the horizontal one straight back in and the
   left and right edges stayed hard black. The single ellipse that replaced
   them still carried ~30% alpha at x=0, which is why a bar survived.

   INTERSECT is the operator this needs: a pixel is kept only where BOTH
   fades keep it, so all four edges dissolve and the art's baked-in black
   ground goes with them. */
[data-theme="prismatic"] .th-plate {
  -webkit-mask-image:
    linear-gradient(90deg, transparent 0%, #000 17%, #000 83%, transparent 100%),
    linear-gradient(180deg, transparent 0%, #000 24%, #000 76%, transparent 100%);
  mask-image:
    linear-gradient(90deg, transparent 0%, #000 17%, #000 83%, transparent 100%),
    linear-gradient(180deg, transparent 0%, #000 24%, #000 76%, transparent 100%);
  -webkit-mask-composite: source-in;
  mask-composite: intersect;
}

/* the art keeps its ground only under itself, and the mask dissolves the
   edges of that too */
[data-theme="prismatic"] .th-plate img,
[data-theme="prismatic"] .th-plate video { background: #0b0d12; }


/* =========================================================================
   HEADER — PRISMATIC
   The shared header rules are token-driven, so most of this theme comes for
   free. These are the places where a dark-theme assumption is baked in.
   ========================================================================= */

/* The teal stays. It is the brand mark, and a brand that changes colour by
   theme is not a brand — my earlier "legibility" fix was solving a problem
   that did not exist: #9be3dd on this slate measures roughly 6.5:1, well
   clear of the 4.5 needed at this size. */
[data-theme="prismatic"] .brand { color: #9be3dd; }

[data-theme="prismatic"] .app-header .input-wrap input {
  background: rgba(15, 23, 42, .28);
  border-color: rgba(255, 255, 255, .26);
  color: #f8fafc;
}

[data-theme="prismatic"] .app-header .input-wrap input::placeholder { color: #cbd5e1; }

[data-theme="prismatic"] .rc-toggle { border-color: rgba(255, 255, 255, .26); }
[data-theme="prismatic"] .rc-toggle:hover { border-color: rgba(255, 255, 255, .42); }
[data-theme="prismatic"] .rc-dot { border-color: rgba(255, 255, 255, .5); }
[data-theme="prismatic"] .rc-text { color: #f1f5f9; }

[data-theme="prismatic"] .theme-seg {
  border-color: rgba(255, 255, 255, .26);
  background: rgba(15, 23, 42, .2);
}

/* --grey-1 here is a mid slate that disappears on the darkened segment —
   the unselected half needs its own value */
[data-theme="prismatic"] .theme-btn { color: #cbd5e1; }
[data-theme="prismatic"] .theme-btn:hover { color: #fff; }

[data-theme="prismatic"] .theme-btn.is-on {
  background: rgba(255, 255, 255, .2);
  color: #fff;
}

[data-theme="prismatic"] .theme-chip { border-color: rgba(255, 255, 255, .45); }


/* =========================================================================
   THRESHOLD APERTURE — FRAMED, NOT FEATHERED

   The mask was the wrong device for this theme, not a mistuned one.

   The generated art is drawn on black. Feathering it edge-to-edge means
   fading BLACK into slate — so the fade itself is a dark gradient, and it
   reads as a drop shadow around the image however the stops are placed. No
   amount of adjusting the mask fixes that, because the shadow IS the fade.

   A full-bleed feather is a dark-theme move: it works in Kosmic because the
   thing being faded into is also near-black, so the transition is invisible.
   Here the honest equivalent of "the image dissolves into the page" is "the
   image is a plate ON the page" — contained, cornered, and edged. Kosmic
   keeps its bleed; this theme gets a frame.
   ========================================================================= */
[data-theme="prismatic"] .th-plate {
  /* no mask at all — see above */
  -webkit-mask-image: none;
  mask-image: none;
  -webkit-mask-composite: source-over;
  mask-composite: add;

  /* a plate, not a bleed: contained rather than 100vw */
  position: relative;
  left: auto;
  width: min(1280px, calc(100% - 48px));
  max-width: 1280px;
  margin-left: auto;
  margin-right: auto;
  border-radius: 16px;
  overflow: hidden;

  /* the edge does the work the feather was doing badly — a bright hairline
     reads as glass on slate, consistent with every other surface here */
  border: 1px solid rgba(255, 255, 255, .22);
  box-shadow: 0 18px 50px rgba(15, 23, 42, .28);
  background: #0b0d12;
}

/* the art fills the plate exactly, so its black ground is never visible as
   a margin — the plate's own rounded edge is the boundary */
[data-theme="prismatic"] .th-plate img,
[data-theme="prismatic"] .th-plate video {
  position: absolute;
  inset: 0;
  left: 0;
  top: 0;
  /* the shared rule centres with left:50% + translateX(-50%); inset:0 alone
     leaves the TRANSFORM in place, which put the art half its own width to
     the left of the plate */
  transform: none;
  width: 100%;
  height: 100%;
  max-width: none;
  object-fit: cover;
  background: transparent;
  -webkit-mask-image: none;
  mask-image: none;
}

/* no gradient overlays either: both existed to blend a bleed into black */
[data-theme="prismatic"] .th-plate::before,
[data-theme="prismatic"] .th-plate::after { display: none; }

@media (max-width: 760px) {
  [data-theme="prismatic"] .th-plate {
    width: calc(100% - 24px);
    border-radius: 12px;
    box-shadow: 0 10px 28px rgba(15, 23, 42, .24);
  }
}


/* The console's context field takes the deck's own ink — which under this
   theme is #0b0d12, i.e. the dark field the brief asked for rather than the
   translucent white every other control uses. Stated explicitly so a future
   pass at --ink cannot quietly turn it back into a white box. */
[data-theme="prismatic"] .console .rds-input {
  background: rgba(11, 13, 18, .72);
  border-color: rgba(255, 255, 255, .2);
  color: #f8fafc;
}

[data-theme="prismatic"] .console .rds-input::placeholder { color: #a9b4c6; }
[data-theme="prismatic"] .console .rd-subject .dk-label i { color: #a9b4c6; }


/* =========================================================================
   CARD BODY

   The summary is the most-read text on a card and it was still carrying the
   dark theme's muted grey — rgb(184,186,200), which measures 2.94:1 on the
   Prismatic card. The title and the coordinate line were corrected in
   20260915-29/30; this is the same fault in the element that matters most.
   ========================================================================= */
[data-theme="prismatic"] .voice-quote,
[data-theme="prismatic"] .voice-quote.is-plain,
[data-theme="prismatic"] .nx-summary,
[data-theme="prismatic"] .focus-summary { color: #eceff6; }

/* the first-person passage keeps its display face and a touch more presence,
   as it does in Kosmic */
[data-theme="prismatic"] .nx-exp,
[data-theme="prismatic"] .focus-experience { color: #f4f6fb; }
