/* ══════════════════════════════════════════════════════════════════════
   RDx Decks · Slide vocabulary — scoped to .slide, authored at 1920×1080.
   Faithful to deck-system.css (Paper): cover, title-block, lede, agenda,
   bignum, cards, feature rows, stat-row, quote, cta, s-ink bands.
   The parent scales the whole .slide via transform: scale().
   ══════════════════════════════════════════════════════════════════════ */

/* ── Scaler: fits a 1920×1080 .slide into any box ─────────────────────── */
.slide-scaler.fit-width { width: 100%; }
.slide-scaler.fit-contain { width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; }
.slide-box { position: relative; overflow: hidden; flex: none; }
.slide-mount { width: 1920px; height: 1080px; transform-origin: top left; position: absolute; top: 0; left: 0; }

.slide {
  /* projection type scale */
  --type-mega: 160px; --type-display: 112px; --type-title: 72px;
  --type-cardtitle: 56px; --type-subtitle: 44px; --type-h3: 34px; --type-body: 28px; --type-small: 24px;
  --type-eyebrow: 26px;
  --pad-top: 72px; --pad-bottom: 64px; --pad-x: 112px;
  --gap-title: 40px; --gap-item: 32px;
  /* palette — flips with theme via surface tokens */
  --slide-canvas: var(--surface-base);
  --slide-surface: var(--surface-raised);
  /* Text ink, layered: a manual deck text colour (--deck-text) wins; else the
     runtime auto-contrast ink (--slide-text-auto, set by slide-stage when the
     theme text would be illegible on the actual background); else the theme
     token. So text is themeable AND dynamically reads its background.
     (Named --deck-text, NOT --brand-text — the latter is the legacy
     text-on-brand token = #fff and would whiten every slide.) */
  --slide-text: var(--deck-text, var(--slide-text-auto, var(--text-primary)));
  --slide-text-2: var(--deck-text-2, var(--slide-text-auto-2, var(--text-secondary)));
  --slide-text-3: var(--deck-text-3, var(--slide-text-auto-3, var(--text-tertiary)));
  --slide-ink: #1E2A3A; --slide-on-ink: #FDFCFA;
  --slide-accent: var(--brand-primary);
  /* ink for text/glyphs placed ON a solid accent fill. Derives from an injected
     --brand-on-accent (the renderer computes it per deck brand via WCAG
     luminance) and falls back to --brand-on-primary so the default look is
     unchanged. This keeps a custom/customer accent legible (a light brand gets
     dark ink automatically) instead of hardcoding white. */
  --slide-on-accent: var(--brand-on-accent, var(--brand-on-primary, #fff));
  --slide-border: var(--border-subtle);

  width: 1920px; height: 1080px; flex: none;
  /* The slide canvas is the THEME's background — never the accent. A deck can
     override it with an explicit background colour (--deck-bg); otherwise it is
     the theme canvas. The skin / media overrides below set their own
     background, so they are unaffected. */
  background: var(--deck-bg, var(--slide-canvas));
  color: var(--slide-text);
  font-family: "Inter", var(--font-sans);
  padding: var(--pad-top) var(--pad-x) var(--pad-bottom);
  display: flex; flex-direction: column; position: relative; overflow: hidden;
  box-sizing: border-box; /* 1920×1080 is the TOTAL box incl. padding — self-contained
                             so the artifact is correct without the app's global reset */
}
.slide, .slide * { box-sizing: border-box; }

/* Section bands */
.slide.s-ink { background: var(--slide-ink); color: var(--slide-on-ink); }
.slide.s-ink .eyebrow { color: rgba(253,252,250,0.6); }
.slide.s-ink .lede, .slide.s-ink .body { color: rgba(253,252,250,0.82); }
.slide.s-ink .footer-meta { color: rgba(253,252,250,0.5); }
.slide.s-surface { background: var(--slide-surface); }

/* Eyebrow */
.slide .eyebrow { font-size: var(--type-eyebrow); font-weight: 500; text-transform: uppercase;
  letter-spacing: 0.14em; color: var(--slide-text-3); margin: 0 0 28px 0; }
.slide .eyebrow.accent { color: var(--slide-accent); }

/* Headlines — display type follows the [data-font] axis via --font-display
   (inter: sans = current look; editorial: Fraunces serif). Body text keeps
   --font-sans. (The !important that once defeated the legacy global font reset
   is gone now the app no longer loads deck-system.css — nothing competes for
   heading font-family, so a plain rule wins.) */
.slide h1, .slide .title, .slide h2.subtitle,
.slide .bignum-value, .slide .stat .n, .slide .cta-headline,
.slide .qi-quote, .slide .testimonial-quote {
  font-family: var(--font-display);
}
/* editorial layouts stay serif regardless of the deck font axis */
.slide.cover--editorial, .slide.k-quote.lay-editorial-serif { --font-display: var(--font-serif); }
.slide h1.title { font-weight: 400; font-size: var(--type-title); line-height: 1.02;
  letter-spacing: -0.035em; color: var(--slide-text); margin: 0; text-wrap: balance; }
.slide h1.display { font-weight: 400; font-size: var(--type-display); line-height: 0.98;
  letter-spacing: -0.045em; margin: 0; text-wrap: balance; }
.slide h2.subtitle { font-weight: 400; font-size: var(--type-subtitle); line-height: 1.15;
  letter-spacing: -0.025em; color: var(--slide-text-2); margin: 0; text-wrap: pretty; }
.slide h3.h3 { font-weight: 500; font-size: var(--type-h3); line-height: 1.2;
  letter-spacing: -0.012em; color: var(--slide-text); margin: 0; }
.slide .lede { font-size: var(--type-body); line-height: 1.45; color: var(--slide-text-2);
  max-width: 1300px; font-weight: 400; margin: 0; text-wrap: pretty; }
.slide .body { font-size: var(--type-body); line-height: 1.45; color: var(--slide-text); margin: 0; }
.slide .small { font-size: var(--type-small); line-height: 1.45; color: var(--slide-text-2); margin: 0; }

.slide.editorial h1.title, .slide.editorial h1.display, .slide.editorial h2.subtitle {
  font-family: var(--font-serif); letter-spacing: -0.025em; font-weight: 400; }

.slide .title-block { display: flex; flex-direction: column; gap: 24px; margin-bottom: 56px; }
.slide .title-block.tight { margin-bottom: 36px; gap: 18px; }
/* the data/card-kind heading: a real heading, not the 16px the bare div inherits
   (only h1.title was sized; div.title from titleBlock was tiny). Scoped to
   .title-block so split's h1.title keeps its larger --type-title. */
.slide .title-block .title { font-size: var(--type-cardtitle); font-weight: 500; line-height: 1.06;
  letter-spacing: -0.025em; color: var(--slide-text); margin: 0; text-wrap: balance; }
.slide .rule { height: 2px; background: var(--slide-border); width: 100%; }

/* Cover */
.slide.cover { justify-content: space-between; }
.slide.cover .cover-mid { display: flex; flex-direction: column; }
.slide.cover .top { display: flex; justify-content: space-between; align-items: flex-start; }
.slide.cover .mark { font-size: 24px; font-weight: 500; letter-spacing: 0.18em;
  text-transform: uppercase; color: var(--slide-text-2); }
.slide.cover h1 { font-size: 150px; line-height: 0.95; letter-spacing: -0.05em; font-weight: 400; margin: 0; text-wrap: balance; }
.slide.cover .subtitle { font-size: var(--type-subtitle); color: var(--slide-text-2); margin-top: 36px; max-width: 1400px; font-weight: 400; }
.slide.cover .bottom { display: flex; justify-content: space-between; align-items: flex-end; }
.slide.cover .byline { font-size: 24px; color: var(--slide-text-3); letter-spacing: 0.01em; }

/* ── Customer branding (Phase 3) ──────────────────────────────────────────
   The cover wordmark becomes a customer logo when meta.brandKit.logo is set
   (the renderer's brandMark); otherwise the literal "RICOH RDx" text is kept.
   When no brandKit is present NONE of these rules apply, so the cover is
   unchanged. The logo sits inside .cover .mark, so it inherits the mark's
   position but overrides the text sizing. */
.slide.cover .mark .brand-logo { display: block; height: 40px; width: auto; max-width: 420px;
  object-fit: contain; object-position: left center; }
/* co-brand lockup: customer logo × RICOH RDx (the default). The "× RICOH RDx"
   keeps the wordmark's uppercase letterspacing so it reads as the house mark. */
.slide.cover .mark .cobrand { display: inline-flex; align-items: center; gap: 18px; }
.slide.cover .mark .cobrand .brand-logo { height: 36px; }
.slide.cover .mark .cobrand-x { font-size: 24px; font-weight: 400; color: var(--slide-text-3);
  line-height: 1; letter-spacing: 0; }
.slide.cover .mark .cobrand-rdx { font-size: 24px; font-weight: 500; letter-spacing: 0.18em;
  text-transform: uppercase; color: var(--slide-text-2); }
/* On a media-backed cover (.has-bg), the mark text already reads light — the
   co-brand wordmark + separator follow suit so the lockup stays legible. */
.slide.cover.has-bg .mark .cobrand-rdx { color: rgba(255,255,255,0.72); }
.slide.cover.has-bg .mark .cobrand-x { color: rgba(255,255,255,0.5); }
/* a small bottom-corner customer logo on content (non-media) slides — a new
   structural element (.brand-foot), not a region. Sits opposite the optional
   footer-meta text so the two never collide. */
.slide .brand-foot { position: absolute; right: var(--pad-x); bottom: 44px; z-index: 3;
  display: flex; align-items: center; pointer-events: none; }
.slide .brand-foot-logo { height: 26px; width: auto; max-width: 240px; object-fit: contain;
  opacity: 0.82; }

/* Footer meta */
.slide .footer-meta { position: absolute; left: var(--pad-x); right: var(--pad-x); bottom: 44px;
  display: flex; justify-content: space-between; align-items: center; font-size: 22px;
  color: var(--slide-text-3); font-weight: 400; }
.slide .footer-meta .tag { font-size: 20px; letter-spacing: 0.16em; text-transform: uppercase;
  padding: 6px 14px; border: 1px solid var(--slide-border); border-radius: 100px; }

/* Agenda */
.slide .agenda { list-style: none; margin: 8px 0 0; padding: 0; counter-reset: ag;
  display: flex; flex-direction: column; flex: 1; justify-content: center; }
.slide .agenda-item { counter-increment: ag; display: grid; grid-template-columns: 110px 1fr;
  gap: 32px; padding: 30px 0; border-top: 1px solid var(--slide-border); align-items: baseline; }
.slide .agenda-item:last-child { border-bottom: 1px solid var(--slide-border); }
.slide .agenda-item::before { content: counter(ag, decimal-leading-zero); font-size: 30px;
  font-weight: 400; color: var(--slide-text-3); font-variant-numeric: tabular-nums; letter-spacing: 0.04em; }
.slide .agenda-item.active::before { color: var(--slide-accent); }
/* Active item: a subtle, theme-aware accent panel (overrides the legacy
   deck-system.css `.agenda-item.active{background:var(--accent-soft)}` which
   defaults to a soft RED and fails contrast on dark themes). Higher specificity
   (.slide .agenda .agenda-item.active) beats the legacy rule; the inset bar
   replaces the old negative-margin border so nothing shifts. */
.slide .agenda .agenda-item.active { background: color-mix(in srgb, var(--slide-accent) 12%, transparent);
  border-radius: 14px; box-shadow: inset 4px 0 0 var(--slide-accent); padding-left: 24px; }
.slide .agenda-title { font-size: 40px; font-weight: 400; color: var(--slide-text); letter-spacing: -0.02em; display: block; }
.slide .agenda-note { font-size: 24px; color: var(--slide-text-2); margin-top: 8px; display: block; }
.slide .agenda-item:not(.active) .agenda-title { color: var(--slide-text-2); }
/* Density: tighten the row rhythm as the list grows so the last item never
   falls off the slide — the layout adapts to the item count instead of
   clipping. (6 items is the common "six things" case.) */
.slide .agenda:has(.agenda-item:nth-child(6)) .agenda-item { padding: 18px 0; }
.slide .agenda:has(.agenda-item:nth-child(6)) .agenda-title { font-size: 34px; }
.slide .agenda:has(.agenda-item:nth-child(6)) .agenda-note { margin-top: 6px; }
.slide .agenda:has(.agenda-item:nth-child(7)) .agenda-item { padding: 12px 0; }
.slide .agenda:has(.agenda-item:nth-child(7)) .agenda-title { font-size: 30px; }
.slide .agenda:has(.agenda-item:nth-child(7)) .agenda-note { font-size: 21px; }

/* Bignum */
.slide .bignum { flex: 1; display: flex; flex-direction: column; justify-content: center; align-items: flex-start; }
.slide .bignum-value { font-size: 220px; line-height: 0.9; letter-spacing: -0.05em; font-weight: 400; color: var(--slide-text); }
.slide .bignum-label { font-size: 30px; color: var(--slide-text-2); max-width: 1100px; margin-top: 24px; line-height: 1.35; }
/* theme-aware delta pill — V2 owns the padding/radius/background so the legacy
   deck-system.css `.bignum-delta.down{background:var(--danger-soft)}` (a fixed
   light pink that jars on dark themes) is overridden. The tint is derived from
   the semantic colour via color-mix so it reads on light AND dark. */
.slide .bignum .bignum-delta { display: inline-flex; align-items: center; gap: 8px; margin-top: 24px;
  font-size: 26px; font-weight: 500; padding: 8px 18px; border-radius: 999px;
  color: var(--success-strong); background: color-mix(in srgb, var(--success-strong) 15%, transparent); }
.slide .bignum .bignum-delta.down { color: var(--error-strong);
  background: color-mix(in srgb, var(--error-strong) 15%, transparent); }
/* Direction caret — internalised from legacy deck-system.css:3235-3236 so the
   ↑/↓ glyph survives once the app stops loading legacy CSS (and so the
   self-contained artifact, which never loaded it, finally shows it). */
.slide .bignum .bignum-delta.up::before   { content: "\2191"; font-weight: 500; }
.slide .bignum .bignum-delta.down::before { content: "\2193"; font-weight: 500; }
/* `.faint` (chart caption, trend note/source) lived only in studio.css, so the
   self-contained artifact rendered those lines at full strength. Internalised
   here so slides.css is self-sufficient on tokens + slides + present alone. */
.slide .faint { color: var(--slide-text-3); }

/* Stat row */
.slide .stat-row { display: grid; grid-auto-flow: column; grid-auto-columns: 1fr; gap: 56px; margin-top: 24px; flex: 1; align-items: center; }
.slide .stat .n { font-size: 96px; font-weight: 400; letter-spacing: -0.04em; line-height: 1; color: var(--slide-text); }
.slide .stat .n .accent { color: var(--slide-accent); }
.slide .stat .label { font-size: 24px; color: var(--slide-text-2); margin-top: 14px; line-height: 1.35; max-width: 420px; }

/* Cards / feature row */
/* fill the height by CENTRING the trio (equal-height, content-sized cards) —
   stretching short cards to the full slide height left huge empty cards. */
.slide .three-col { display: grid; grid-template-columns: repeat(3, 1fr); gap: 32px; margin-top: 16px; flex: 1; min-height: 0; align-items: stretch; align-content: center; }
.slide .two-col { display: grid; grid-template-columns: 1fr 1fr; gap: 56px; align-items: start; }
.slide .feature-card { background: var(--slide-surface); border: 1px solid var(--slide-border);
  border-radius: 20px; padding: 44px 40px; display: flex; flex-direction: column; gap: 18px; height: 100%; }
.slide.s-ink .feature-card { background: rgba(255,255,255,0.05); border-color: rgba(255,255,255,0.12); }
.slide .feature-icon { width: 64px; height: 64px; border-radius: 16px; display: grid; place-items: center;
  background: color-mix(in srgb, var(--slide-accent) 12%, transparent); color: var(--slide-accent); margin-bottom: 8px; }
.slide .feature-icon svg { width: 34px; height: 34px; fill: none; stroke: currentColor; stroke-width: 1.7;
  stroke-linecap: round; stroke-linejoin: round; }
/* the renderer emits the heading as a bare data-region div (not <h3>), so size
   it directly — otherwise it inherits the ~16px default and reads smaller than
   the description. */
.slide .feature-card h3, .slide .feature-card [data-region$="-h"] { font-size: var(--type-h3); font-weight: 500; color: var(--slide-text); margin: 0; letter-spacing: -0.012em; }
.slide .feature-card .desc { font-size: 23px; line-height: 1.45; color: var(--slide-text-2); margin: 0; }

/* Quote / testimonial */
.slide .testimonial-quote { font-size: 60px; line-height: 1.12; letter-spacing: -0.03em;
  font-weight: 400; color: var(--slide-text); max-width: 1500px; text-wrap: balance; margin-top: auto; }
.slide .testimonial-quote em { font-style: normal; color: var(--slide-accent); }
.slide .testimonial-by { display: flex; align-items: center; gap: 22px; margin-top: 56px; margin-bottom: auto; }
.slide .testimonial-avatar { width: 80px; height: 80px; border-radius: 50%; display: grid; place-items: center;
  background: var(--slide-ink); color: var(--slide-on-ink); font-size: 28px; font-weight: 600; letter-spacing: 0.03em; }
.slide .by-name { font-size: 28px; color: var(--slide-text); font-weight: 500; }
.slide .by-meta { font-size: 23px; color: var(--slide-text-2); margin-top: 4px; }

/* Bullets */
.slide ul.bullets { list-style: none; margin: 8px 0 0; padding: 0; display: flex; flex-direction: column; gap: 26px; flex: 1; justify-content: center; }
/* Density: tighten the gap as the list grows so long bullet lists still fit. */
.slide ul.bullets:has(li:nth-child(6)) { gap: 18px; }
.slide ul.bullets:has(li:nth-child(8)) { gap: 12px; }
.slide ul.bullets li { font-size: var(--type-body); line-height: 1.4; color: var(--slide-text); padding-left: 44px; position: relative; }
.slide ul.bullets li::before { content: ""; position: absolute; left: 6px; top: 16px; width: 14px; height: 14px;
  border-radius: 50%; background: var(--slide-accent); }

/* CTA — theme-aware by default (reads the slide tokens so it is legible on ANY
   paper, light or dark); the on-ink palette is applied only when the slide is an
   ink band (.s-ink) or carries a background image (.has-bg). */
.slide .cta { display: flex; flex-direction: column; justify-content: center; height: 100%; gap: 40px; }
.slide .cta-headline { font-size: var(--type-display); font-weight: 400; letter-spacing: -0.045em;
  line-height: 0.98; margin: 0; color: var(--slide-text); }
.slide .cta-sub { font-size: 32px; line-height: 1.4; color: var(--slide-text-2); max-width: 1200px; margin: 0; }
.slide .cta-contact { display: grid; grid-template-columns: auto 1fr; gap: 14px 40px; margin-top: 24px; font-size: 26px; }
.slide .cta-contact .key { color: var(--slide-text-3); text-transform: uppercase; letter-spacing: 0.1em; font-size: 22px; }
.slide .cta-contact .val { color: var(--slide-text); }
/* ink-band / image-backed CTA: switch to the on-ink palette */
.slide.s-ink .cta-headline { color: var(--slide-on-ink); }
.slide.s-ink .cta-sub { color: rgba(253,252,250,0.8); }
.slide.s-ink .cta-contact .key { color: rgba(253,252,250,0.55); }
.slide.s-ink .cta-contact .val { color: var(--slide-on-ink); }

/* ── Image art placeholder + Ken Burns ────────────────────────────────── */
/* media placeholder is DARK (a white surface flashed through at the top of
   full-bleed images during load/render — read as a cream band). */
.slide .art { width: 100%; height: 100%; position: relative; overflow: hidden; display: grid; place-items: center; background: var(--slide-ink); }
.slide .art-layer { position: absolute; inset: 0; }
.slide .art-glyph { width: 96px; height: 96px; color: rgba(255,255,255,0.55); position: relative; z-index: 1; }
.slide .art-glyph svg { width: 100%; height: 100%; }

/* ── Video background (YouTube) ───────────────────────────────────────────
   The static markup carries ONLY a poster (the YouTube thumbnail <img>) plus a
   play glyph — never a cross-origin iframe — so it is safe in the sandboxed
   share/live viewer. The player bridge (slide-stage.js) mounts a real YT
   <iframe> over this poster ONLY in non-sandboxed contexts (editor + Present);
   in the hermetic iframe the poster is the final state. .video-frame is the
   bridge-injected player; once it is present the poster/glyph fade out. */
.slide .art.bg-video, .slide .video-layer { background: var(--slide-ink); }
.slide .video-poster { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; z-index: 0; }
.slide .video-play { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); z-index: 2;
  width: 96px; height: 68px; pointer-events: none; transition: opacity 200ms var(--ease, ease); }
.slide .video-play svg { width: 100%; height: 100%; display: block; filter: drop-shadow(0 4px 14px rgba(0,0,0,0.35)); }
.slide .art.bg-video .video-frame { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; z-index: 1; }
/* once a real player is mounted, hide the poster + glyph beneath it */
.slide .art.bg-video.is-playing .video-poster,
.slide .art.bg-video.is-playing .video-play { opacity: 0; }
.slide .art::after { content: ""; position: absolute; inset: 0; z-index: 1; opacity: 0.5; pointer-events: none;
  background-image: radial-gradient(rgba(255,255,255,0.10) 1px, transparent 1px); background-size: 7px 7px; mix-blend-mode: overlay; }
.slide .art-dawn  { background: radial-gradient(120% 120% at 20% 15%, #E8A06A 0%, #C9603C 38%, #7B3320 78%); }
.slide .art-sage  { background: radial-gradient(120% 120% at 75% 20%, #4FB58A 0%, #1E8A5B 42%, #0C4733 82%); }
.slide .art-slate { background: radial-gradient(120% 120% at 25% 20%, #5E7E9C 0%, #2C4A66 44%, #15273A 84%); }
.slide .art-dusk  { background: radial-gradient(120% 120% at 80% 25%, #9B7BC0 0%, #6B4E8E 42%, #38275A 82%); }
.slide .art-mist  { background: radial-gradient(130% 130% at 30% 25%, #AEC2D4 0%, #6E8AA6 45%, #3C5066 85%); }
.slide .art-ember { background: radial-gradient(120% 120% at 70% 20%, #E0B15A 0%, #C77A2E 40%, #6E3B14 82%); }
.slide .art-ocean  { background: linear-gradient(135deg, #2E5E8C 0%, #1B3A5C 55%, #0E2236 100%); }
.slide .art-plum   { background: conic-gradient(from 210deg at 35% 30%, #7B4E9E, #4A2D6E 55%, #2A1842 100%); }
.slide .art-gold   { background: linear-gradient(120deg, #E6B450 0%, #C98A2E 50%, #7A4E14 100%); }
.slide .art-forest { background: radial-gradient(120% 120% at 30% 20%, #4E9E6E 0%, #1E6E45 45%, #0C3322 85%); }
.slide .art.kenburns .art-layer { animation: artdevelop 700ms var(--ease) both, kenburns 18s ease-in-out 0.4s infinite alternate; transform-origin: center; }
@keyframes kenburns { 0% { transform: scale(1.05) translate(0,0); } 100% { transform: scale(1.18) translate(-2.5%, -2%); } }
/* images "develop" in — echoes streaming / image generation */
.slide .art-layer { animation: artdevelop 700ms var(--ease) both; }
@keyframes artdevelop { from { opacity: 0; filter: blur(16px) saturate(0.5); } to { opacity: 1; filter: blur(0) saturate(1); } }
@media (prefers-reduced-motion: reduce) { .slide .art.kenburns .art-layer, .slide .art-layer { animation: none; } }

/* ── Background image on title / close slides ─────────────────────────── */
.slide.has-bg { color: #fff; }
.slide.has-bg .bg-art { position: absolute; inset: 0; z-index: 0; }
.slide.has-bg .bg-scrim { position: absolute; inset: 0; z-index: 1;
  background: linear-gradient(0deg, rgba(8,12,18,0.84) 0%, rgba(8,12,18,0.42) 52%, rgba(8,12,18,0.16) 100%); }
.slide.has-bg.bg-center .bg-scrim { background: radial-gradient(120% 120% at 50% 50%, rgba(8,12,18,0.34), rgba(8,12,18,0.82)); }
.slide.has-bg > .top, .slide.has-bg > .cover-mid, .slide.has-bg > .bottom, .slide.has-bg > .cta { position: relative; z-index: 2; }
.slide.cover.has-bg .mark { color: rgba(255,255,255,0.72); }
.slide.cover.has-bg h1, .slide.cta.has-bg .cta-headline { color: #fff; }
.slide.cover.has-bg .subtitle { color: rgba(255,255,255,0.86); }
.slide.cover.has-bg .byline { color: rgba(255,255,255,0.62); }
.slide.cover.has-bg .eyebrow.accent { color: #fff; }
.slide.cover.has-bg .eyebrow.accent::before { content: ""; display: inline-block; width: 30px; height: 2px; background: var(--slide-accent); vertical-align: middle; margin-right: 16px; }
.slide.cover.has-bg.bg-center { justify-content: center; }
.slide.cover.has-bg.bg-center .cover-mid { align-items: center; text-align: center; }
.slide.cover.has-bg.bg-center .cover-mid .eyebrow.accent::before { display: none; }
.slide.cover.has-bg.bg-center .top { position: absolute; left: var(--pad-x); right: var(--pad-x); top: var(--pad-top); }
.slide.cover.has-bg.bg-center .bottom { position: absolute; left: var(--pad-x); right: var(--pad-x); bottom: 44px; }
.slide.cta.has-bg .cta-sub { color: rgba(255,255,255,0.84); }
.slide.cta.has-bg .cta-contact .key { color: rgba(255,255,255,0.6); }
.slide.cta.has-bg .cta-contact .val { color: #fff; }

/* ── Unified slide background (any kind) ──────────────────────────────────
   The data/card kinds gain a backdrop via .slide-bg + .slide-scrim. A media
   backdrop (gradient/image/motion) carries a tokenised scrim and flips the
   palette vars to read light over it (.bg-media); a colour wash stays light
   and keeps the normal palette (.bg-wash). Scrim/on-media are tokens so the
   five copies stop drifting. */
.slide { --slide-scrim-1: rgba(8,12,18,0.86); --slide-scrim-2: rgba(8,12,18,0.40); --slide-on-media: #FDFCFA; }
.slide .slide-bg { position: absolute; inset: 0; z-index: 0; }
.slide .slide-scrim { position: absolute; inset: 0; z-index: 1;
  background: linear-gradient(0deg, var(--slide-scrim-1) 0%, var(--slide-scrim-2) 55%, transparent 100%); }
/* A colour background is a NEUTRAL surface tone (a subtle elevation from the
   canvas) — never the accent. Accent is reserved for highlights, so a coloured
   slide no longer reads as an accent panel ("window within a window"). */
.slide .slide-bg.bg-wash { background: var(--deck-bg, color-mix(in srgb, var(--slide-text) 6%, var(--slide-canvas))); }
/* hoist real content above the backdrop */
.slide.has-bg.bg-media > *:not(.slide-bg):not(.slide-scrim),
.slide.has-bg.bg-wash > *:not(.slide-bg) { position: relative; z-index: 2; }
/* media backdrop: read light, translucent card surfaces (like an ink band) */
.slide.has-bg.bg-media { color: var(--slide-on-media);
  --slide-text: var(--slide-on-media); --slide-text-2: rgba(253,252,250,0.82); --slide-text-3: rgba(253,252,250,0.62);
  --slide-border: rgba(255,255,255,0.18); --slide-surface: rgba(255,255,255,0.07); }

/* ── Cover style variants ─────────────────────────────────────────────── */
.slide.cover--editorial h1 { font-family: var(--font-serif); font-weight: 400; letter-spacing: -0.025em; }
.slide.cover--statement { justify-content: center; }
.slide.cover--statement .cover-mid h1 { font-size: 180px; line-height: 0.92; letter-spacing: -0.05em; }
.slide.cover--statement .top, .slide.cover--statement .bottom { position: absolute; left: var(--pad-x); right: var(--pad-x); }
.slide.cover--statement .top { top: var(--pad-top); } .slide.cover--statement .bottom { bottom: 44px; }

/* ── Split: image one side, content the other ─────────────────────────── */
.slide.split { flex-direction: row; padding: 0; gap: 0; align-items: stretch; }
.slide.split .split-media { flex: 0 0 48%; position: relative; }
.slide.split .split-body { flex: 1; display: flex; flex-direction: column; justify-content: center;
  padding: var(--pad-top) var(--pad-x) var(--pad-bottom); gap: 28px; }
.slide.split .split-body .eyebrow { margin: 0; }
.slide.split .split-body .title { font-size: 84px; }
.slide.split .split-cap { position: absolute; left: 32px; bottom: 28px; z-index: 2; font-size: 22px; color: #fff;
  background: rgba(15,20,28,0.55); backdrop-filter: blur(6px); padding: 10px 16px; border-radius: 100px; }

/* ── Full-bleed image with overlaid text ──────────────────────────────── */
.slide.full-bleed { padding: 0; background: var(--slide-ink); }
.slide.full-bleed .art-glyph { display: none; }
.slide.full-bleed .fb-media { position: absolute; inset: 0; }
.slide.full-bleed .fb-scrim { position: absolute; inset: 0; z-index: 1;
  background: linear-gradient(0deg, rgba(8,12,18,0.86) 0%, rgba(8,12,18,0.4) 38%, rgba(8,12,18,0.12) 70%); }
.slide.full-bleed .fb-content { position: absolute; left: var(--pad-x); right: var(--pad-x); bottom: var(--pad-bottom); z-index: 2; max-width: 1500px; }
.slide.full-bleed .fb-content .eyebrow { color: rgba(255,255,255,0.82); margin-bottom: 20px; }
.slide.full-bleed .fb-content .display { color: #fff; font-size: 128px; }
.slide.full-bleed .fb-content .lede { color: rgba(255,255,255,0.86); margin-top: 24px; max-width: 1200px; }
.slide.full-bleed.fb-center .fb-scrim { background: radial-gradient(120% 120% at 50% 50%, rgba(8,12,18,0.34), rgba(8,12,18,0.8)); }
.slide.full-bleed.fb-center .fb-content { left: 0; right: 0; bottom: 0; top: 0; display: flex; flex-direction: column; justify-content: center; align-items: center; text-align: center; padding: 0 220px; }
.slide.full-bleed.fb-center .fb-content .eyebrow,
.slide.full-bleed.fb-center .fb-content .display,
.slide.full-bleed.fb-center .fb-content .lede { text-align: center; width: 100%; }
.slide.full-bleed.fb-center .fb-content .display { font-size: 116px; line-height: 1.0; max-width: 1400px; margin-inline: auto; }
.slide.full-bleed.fb-center .fb-content .lede { max-width: 1080px; margin-inline: auto; }

/* ── Gallery grid ─────────────────────────────────────────────────────── */
.slide .gallery-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 28px; margin-top: 20px; flex: 1; min-height: 0; grid-auto-rows: 1fr; }
.slide .gallery-cell { display: flex; flex-direction: column; border-radius: 18px; overflow: hidden; background: var(--slide-surface); border: 1px solid var(--slide-border); min-height: 0; }
.slide .gallery-cell .art { flex: 1; min-height: 0; border-radius: 0; }
.slide .gallery-cap { padding: 24px 26px; }
.slide .gallery-cap-t { font-size: 30px; font-weight: 500; color: var(--slide-text); letter-spacing: -0.01em; }
.slide .gallery-cap-n { font-size: 22px; color: var(--slide-text-2); margin-top: 6px; line-height: 1.4; }

/* ── Trend (area/line) chart ──────────────────────────────────────────── */
.slide .trend { display: flex; flex-direction: column; height: 100%; }
.slide .trend-read { display: flex; align-items: baseline; gap: 22px; margin-bottom: 8px; }
.slide .trend-x { font-size: 26px; color: var(--slide-text-2); text-transform: uppercase; letter-spacing: 0.08em; }
.slide .trend-v { font-size: 64px; font-weight: 400; letter-spacing: -0.03em; color: var(--slide-text); font-variant-numeric: tabular-nums; }
.slide .trend-note { font-size: 24px; }
.slide .trend-svg { width: 100%; flex: 1; min-height: 0; overflow: visible; margin-top: var(--gap-title); }
.slide .trend-line { stroke-dasharray: 1; stroke-dashoffset: 1; }
.slide .trend.is-revealed .trend-line { transition: stroke-dashoffset 1400ms var(--ease); stroke-dashoffset: 0; }
.slide .trend-area { opacity: 0; }
.slide .trend.is-revealed .trend-area { transition: opacity 900ms var(--ease) 500ms; opacity: 1; }
.slide .trend-dot { cursor: pointer; transition: r var(--motion-fast) var(--ease); }
.slide .trend-axis { display: flex; justify-content: space-between; margin-top: 10px; padding: 0 24px; }
.slide .trend-axis span { font-size: 22px; color: var(--slide-text-3); }
.slide .trend-axis span.on { color: var(--slide-accent); font-weight: 500; }
.slide .trend-src { margin-top: 14px; font-size: 20px; }

/* ── KPI dashboard (finance) ─────────────────────────────────────────── */
.slide .kpi-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 28px; margin-top: 12px; flex: 1; min-height: 0; grid-auto-rows: 1fr; }
.slide .kpi-tile { background: var(--slide-surface); border: 1px solid var(--slide-border); border-radius: 20px; padding: 36px 38px; display: flex; flex-direction: column; justify-content: center; gap: 12px; }
.slide.s-ink .kpi-tile { background: rgba(255,255,255,0.05); border-color: rgba(255,255,255,0.12); }
.slide .kpi-label { font-size: 26px; color: var(--slide-text-2); }
.slide .kpi-value { font-size: 88px; font-weight: 400; letter-spacing: -0.04em; line-height: 1; margin: 14px 0 16px; color: var(--slide-text); font-variant-numeric: tabular-nums; }
.slide .kpi-foot { display: flex; align-items: center; justify-content: space-between; gap: 16px; }
.slide .kpi-delta { font-size: 26px; font-weight: 500; }
.slide .kpi-delta.up { color: var(--success-strong); }
.slide .kpi-delta.down { color: var(--error-strong); }
.slide .spark { width: 132px; height: 40px; flex: none; }

/* ── Pricing structure ────────────────────────────────────────────────── */
.slide .price-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 28px; margin-top: 16px; flex: 1; min-height: 0; align-items: stretch; }
.slide .price-card { position: relative; background: var(--slide-surface); border: 1px solid var(--slide-border); border-radius: 22px;
  padding: 44px 40px; display: flex; flex-direction: column; gap: 8px; }
.slide .price-card.featured { border-color: var(--slide-accent); box-shadow: 0 0 0 2px var(--slide-accent); }
.slide.s-ink .price-card { background: rgba(255,255,255,0.05); border-color: rgba(255,255,255,0.12); }
.slide .price-badge { position: absolute; top: -18px; left: 40px; background: var(--slide-accent); color: var(--slide-on-accent); font-size: 20px; font-weight: 600;
  padding: 7px 16px; border-radius: 100px; letter-spacing: 0.02em; }
.slide .price-name { font-size: 30px; font-weight: 500; color: var(--slide-text); }
.slide .price-amount { display: flex; align-items: baseline; gap: 10px; margin: 6px 0 2px; }
.slide .price-num { font-size: 80px; font-weight: 400; letter-spacing: -0.04em; line-height: 1; color: var(--slide-text); }
.slide .price-per { font-size: 26px; color: var(--slide-text-2); }
.slide .price-note { font-size: 23px; color: var(--slide-text-2); margin-bottom: 14px; line-height: 1.4; }
.slide .price-feats { list-style: none; margin: 8px 0 0; padding: 16px 0 0; border-top: 1px solid var(--slide-border); display: flex; flex-direction: column; gap: 16px; }
.slide .price-feats li { display: flex; align-items: flex-start; gap: 14px; font-size: 24px; line-height: 1.35; color: var(--slide-text); }
.slide .price-feats svg { width: 26px; height: 26px; flex: none; color: var(--slide-accent); margin-top: 2px; }

/* ── Timeline / roadmap ───────────────────────────────────────────────── */
/* background/border/padding reset neutralises the legacy deck-system.css
   `.timeline { background: var(--surface); border; border-radius; padding }`
   card that the app shell loads alongside the V2 preview (it painted a stark
   white band over the theme canvas). */
.slide .timeline { position: relative; display: grid; grid-auto-flow: column; grid-auto-columns: 1fr; gap: 24px;
  margin-top: 0; padding: 6px 0 0; background: none; border: 0; border-radius: 0; flex: 1; align-items: center; }
.slide .timeline-line { position: absolute; top: 12px; left: 4%; right: 4%; height: 3px; background: var(--slide-border); }
.slide .tl-node { position: relative; padding-top: 48px; }
.slide .tl-dot { position: absolute; top: 0; left: 0; width: 26px; height: 26px; border-radius: 50%; background: var(--slide-canvas);
  border: 4px solid var(--slide-text-3); }
.slide .tl-node.done .tl-dot { background: var(--slide-accent); border-color: var(--slide-accent); }
.slide .tl-node.active .tl-dot { border-color: var(--slide-accent); box-shadow: 0 0 0 8px color-mix(in srgb, var(--slide-accent) 18%, transparent); }
.slide .tl-date { font-size: 24px; font-weight: 500; color: var(--slide-accent); letter-spacing: 0.04em; }
.slide .tl-title { font-size: 32px; font-weight: 500; color: var(--slide-text); margin-top: 8px; letter-spacing: -0.01em; }
.slide .tl-note { font-size: 23px; color: var(--slide-text-2); margin-top: 8px; line-height: 1.4; }

/* ── Comparison (two-column) ──────────────────────────────────────────── */
.slide .cmp-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 32px; margin-top: 16px; flex: 1; min-height: 0; }
.slide .cmp-col { background: var(--slide-surface); border: 1px solid var(--slide-border); border-radius: 22px; padding: 44px 44px 48px; height: 100%; }
.slide.s-ink .cmp-col { background: rgba(255,255,255,0.05); border-color: rgba(255,255,255,0.12); }
.slide .cmp-col.cmp-hl { border-color: var(--slide-accent); box-shadow: 0 0 0 2px var(--slide-accent); background: color-mix(in srgb, var(--slide-accent) 6%, var(--slide-surface)); }
.slide .cmp-head { margin-bottom: 28px; }
.slide .cmp-tag { display: inline-block; font-size: 20px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.1em;
  color: var(--slide-text-3); margin-bottom: 10px; }
.slide .cmp-hl .cmp-tag { color: var(--slide-accent); }
.slide .cmp-title { font-size: 42px; font-weight: 400; letter-spacing: -0.02em; color: var(--slide-text); }
.slide .cmp-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 22px; }
.slide .cmp-list li { display: flex; align-items: flex-start; gap: 16px; font-size: 27px; line-height: 1.35; color: var(--slide-text); }
.slide .cmp-list svg { width: 28px; height: 28px; flex: none; margin-top: 2px; color: var(--slide-text-3); }
.slide .cmp-hl .cmp-list svg { color: var(--slide-accent); }

/* ── Quote over image ─────────────────────────────────────────────────── */
.slide.quote-image { padding: 0; }
.slide.quote-image .art-glyph { display: none; }
.slide.quote-image .qi-scrim { position: absolute; inset: 0; z-index: 1;
  background: linear-gradient(90deg, rgba(8,12,18,0.9) 0%, rgba(8,12,18,0.66) 45%, rgba(8,12,18,0.32) 100%); }
.slide.quote-image .qi-content { position: absolute; inset: 0; z-index: 2; display: flex; flex-direction: column; justify-content: center;
  padding: 0 160px 0 var(--pad-x); max-width: 1180px; }
.slide.quote-image .qi-mark { font-family: var(--font-serif); font-size: 200px; line-height: 0.6; color: var(--slide-accent); height: 90px; }
.slide.quote-image .qi-quote { font-size: 68px; line-height: 1.12; letter-spacing: -0.025em; font-weight: 400; color: #fff; max-width: 1400px; text-wrap: balance; }
.slide.quote-image .qi-quote em { font-style: normal; color: var(--slide-accent); }
.slide.quote-image .qi-by { display: flex; align-items: center; gap: 20px; margin-top: 48px; }
.slide.quote-image .qi-avatar { width: 76px; height: 76px; border-radius: 50%; display: grid; place-items: center; background: var(--slide-accent); color: var(--slide-on-accent); font-size: 26px; font-weight: 600; }
.slide.quote-image .qi-name { font-size: 28px; font-weight: 500; color: #fff; }
.slide.quote-image .qi-meta { font-size: 23px; color: rgba(255,255,255,0.7); margin-top: 4px; }

/* ── Build/reveal (present mode) ──────────────────────────────────────── */
.slide .bb { opacity: 0; transform: translateY(26px); transition: opacity 480ms var(--ease), transform 480ms var(--ease); will-change: opacity, transform; }
.slide .bb.bb-in { opacity: 1; transform: none; }

/* ── Interactive chart ────────────────────────────────────────────────── */
/* the chart sits in a [data-build] reveal wrapper; make that wrapper + chart
   fill the height so the bars use the whole canvas rather than clustering top. */
.slide.k-chart > [data-build="1"] { flex: 1; min-height: 0; display: flex; flex-direction: column; }
.slide .chart { margin-top: 16px; flex: 1; display: flex; flex-direction: column; }
/* align-items:stretch overrides the legacy deck-system.css `.chart-bars{align-items:flex-end}`
   (it styles a vertical bar chart) that would otherwise right-collapse the V2 rows. */
.slide .chart-bars { display: flex; flex-direction: column; gap: 22px; flex: 1; justify-content: center; align-items: stretch; }
.slide .chart-row { display: grid; grid-template-columns: 280px 1fr 140px; align-items: center; gap: 32px;
  padding: 10px 20px; margin: -10px -20px; border-radius: 16px; cursor: default; color: var(--slide-text);
  transition: background var(--motion) var(--ease), opacity var(--motion) var(--ease); }
.slide .chart-row.is-hover { background: var(--slide-surface); }
.slide .chart-row.is-dim { opacity: 0.45; }
.slide .chart-k { font-size: 30px; font-weight: 500; color: var(--slide-text); letter-spacing: -0.01em; }
.slide .chart-track { height: 44px; border-radius: 10px; background: color-mix(in srgb, var(--slide-text-3) 14%, transparent); overflow: hidden; }
.slide.s-ink .chart-track { background: rgba(255,255,255,0.1); }
.slide .chart-fill { display: block; height: 100%; border-radius: 10px; width: 0;
  background: linear-gradient(90deg, color-mix(in srgb, var(--slide-accent) 75%, transparent), var(--slide-accent));
  transition: width 900ms var(--ease); }
.slide .chart-row.is-hover .chart-fill { background: var(--slide-accent); }
.slide .chart-v { font-size: 38px; font-weight: 400; letter-spacing: -0.03em; color: var(--slide-text); text-align: right; font-variant-numeric: tabular-nums; }
.slide .chart-cap { margin-top: 40px; padding-top: 28px; border-top: 1px solid var(--slide-border); font-size: 28px; color: var(--slide-text-2); min-height: 40px; }
.slide .chart-cap .accent { color: var(--slide-accent); font-weight: 500; }
.slide .chart-cap b { color: var(--slide-text); font-weight: 500; }

/* centred slide body (used by the cta 'center' class) */
.slide.center { justify-content: center; align-items: flex-start; }


/* ════════════════════════════════════════════════════════════════════════
   LAYOUT DELTAS (Phase 4) — per-kind named layouts.
   Each rule is scoped to .slide.k-<kind>.lay-<id> so it ONLY applies when a
   non-default layout is chosen; default (layouts[0]) rendering is untouched.
   Authored from the KIND_META catalogue. Some with-image/table layouts carry
   a graceful CSS fallback and gain real side-media once their markup lands.
   ════════════════════════════════════════════════════════════════════════ */
/* ========== cover ========== */
/* --- cover · centred --- */
/* ── Cover · centred — everything stacked dead-centre (vs classic top-left) ── */
.slide.cover.lay-centred { justify-content: center; align-items: center; text-align: center; }
.slide.cover.lay-centred .cover-mid { align-items: center; text-align: center; max-width: 1500px; }
.slide.cover.lay-centred .cover-mid h1 { text-wrap: balance; }
.slide.cover.lay-centred .subtitle { margin-left: auto; margin-right: auto; max-width: 1200px; }
/* the accent eyebrow grows a centred rule under it instead of the left bar */
.slide.cover.lay-centred .eyebrow.accent { display: inline-flex; flex-direction: column; align-items: center; gap: 22px; }
.slide.cover.lay-centred .eyebrow.accent::before { display: none; }
.slide.cover.lay-centred .eyebrow.accent::after { content: ""; width: 80px; height: 3px; background: var(--slide-accent); }
/* pin the chrome to the corners so the centre block floats free */
.slide.cover.lay-centred .top,
.slide.cover.lay-centred .bottom { position: absolute; left: var(--pad-x); right: var(--pad-x); z-index: 2; }
.slide.cover.lay-centred .top { top: var(--pad-top); }
.slide.cover.lay-centred .bottom { bottom: 44px; justify-content: center; }
/* --- cover · with-image --- */
/* ── Cover · with-image — film-poster: title gravity at the bottom-left ──── */
/* Bottom-anchored so a background image (or the wash) breathes above the words; */
/* reads as an editorial/poster cover whether or not a bg is set. */
.slide.cover.lay-with-image { justify-content: flex-end; }
.slide.cover.lay-with-image .top { position: absolute; left: var(--pad-x); right: var(--pad-x); top: var(--pad-top); }
.slide.cover.lay-with-image .cover-mid { max-width: 1500px; margin-bottom: 12px; }
.slide.cover.lay-with-image .cover-mid h1 { font-size: 138px; line-height: 0.96; }
.slide.cover.lay-with-image .subtitle { margin-top: 28px; max-width: 1150px; }
/* a heavier accent flag above the eyebrow grounds the bottom block */
.slide.cover.lay-with-image .eyebrow.accent { display: flex; flex-direction: column; align-items: flex-start; gap: 24px; }
.slide.cover.lay-with-image .eyebrow.accent::before { content: ""; display: block; width: 120px; height: 5px; margin: 0; background: var(--slide-accent); }
.slide.cover.lay-with-image .bottom { margin-top: 40px; }
/* when no media is set, lay a soft bottom wash so the words still sit on a base */
.slide.cover.lay-with-image:not(.has-bg)::after { content: ""; position: absolute; inset: auto 0 0 0; height: 46%; z-index: 0;
  background: linear-gradient(0deg, color-mix(in srgb, var(--slide-accent) 10%, transparent) 0%, transparent 100%); pointer-events: none; }
/* hoist only the content rows above the no-media wash — never the bg-art/scrim */
.slide.cover.lay-with-image > .top,
.slide.cover.lay-with-image > .cover-mid,
.slide.cover.lay-with-image > .bottom { position: relative; z-index: 1; }
/* --- cover · side-panel [needsMarkup] --- */
/* ── Cover · side-panel — 60/40 split, content left, accent/media band right ─ */
.slide.cover.lay-side-panel { display: grid; grid-template-columns: 60% 40%; grid-template-rows: auto 1fr auto;
  grid-template-areas: "top top" "mid panel" "bottom panel"; column-gap: 72px; align-items: center; }
.slide.cover.lay-side-panel > .top { grid-area: top; align-self: start; position: relative; z-index: 1; }
.slide.cover.lay-side-panel > .cover-mid { grid-area: mid; align-self: center; position: relative; z-index: 1; }
.slide.cover.lay-side-panel .cover-mid h1 { font-size: 116px; line-height: 0.98; }
.slide.cover.lay-side-panel .subtitle { max-width: 920px; }
.slide.cover.lay-side-panel > .bottom { grid-area: bottom; align-self: end; position: relative; z-index: 1; }
/* the right 40% — a decorative accent panel breaking the right + vertical padding
   to the slide edge. If a real media element is added (see markupNote) it lives
   here instead and sits above the ::after fallback. */
.slide.cover.lay-side-panel .cover-panel,
.slide.cover.lay-side-panel::after { grid-area: panel; align-self: stretch;
  margin: calc(-1 * var(--pad-top)) calc(-1 * var(--pad-x)) calc(-1 * var(--pad-bottom)) 0; }
.slide.cover.lay-side-panel::after { content: ""; z-index: 0;
  background: linear-gradient(155deg, color-mix(in srgb, var(--slide-accent) 90%, var(--slide-ink)) 0%, var(--slide-ink) 100%); }
.slide.cover.lay-side-panel .cover-panel { position: relative; z-index: 1; overflow: hidden; }
.slide.cover.lay-side-panel .cover-panel .art { width: 100%; height: 100%; }
/* a real panel image supersedes the ::after accent-gradient fallback */
.slide.cover.lay-side-panel:has(.cover-panel .art-layer[style])::after { display: none; }
/* --- cover · colour-band --- */
/* ── Cover · colour-band — title block sits inside a full-bleed accent stripe ─ */
.slide.cover.lay-colour-band { justify-content: center; }
.slide.cover.lay-colour-band .top { position: absolute; left: var(--pad-x); right: var(--pad-x); top: var(--pad-top); z-index: 2; }
.slide.cover.lay-colour-band .bottom { position: absolute; left: var(--pad-x); right: var(--pad-x); bottom: 44px; z-index: 2; }
/* the band: a full-width accent stripe behind the mid block, breaking the x-padding */
.slide.cover.lay-colour-band .cover-mid { position: relative; z-index: 1;
  padding: 80px var(--pad-x); margin: 0 calc(-1 * var(--pad-x)); }
.slide.cover.lay-colour-band .cover-mid::before { content: ""; position: absolute; inset: 0; z-index: -1;
  background: color-mix(in srgb, var(--slide-accent) 94%, var(--slide-ink)); }
/* text reads light over the accent stripe (the sanctioned on-media token, adapts) */
.slide.cover.lay-colour-band .cover-mid h1,
.slide.cover.lay-colour-band .cover-mid .subtitle { color: var(--slide-on-media); }
.slide.cover.lay-colour-band .cover-mid .eyebrow.accent { color: var(--slide-on-media); opacity: 0.86; }
.slide.cover.lay-colour-band .cover-mid .eyebrow.accent::before { display: none; }
.slide.cover.lay-colour-band .cover-mid h1 { font-size: 132px; line-height: 0.96; max-width: 1480px; }
.slide.cover.lay-colour-band .cover-mid .subtitle { opacity: 0.92; max-width: 1300px; }
/* --- cover · split-portrait --- */
/* ── Cover · split-portrait — title rail left (~55%), a tall full-height media
   plate right (~45%) that breaks to the top/right/bottom slide edges. Distinct
   from side-panel: a WIDER, image-forward plate (no baked accent-band framing),
   the title left-ranged and vertically centred. Shares the .cover-panel markup
   + 'bg' image slot (rendered for both side-panel and split-portrait); the
   ::after fallback only shows when no panel image is set. Tokens only. */
.slide.cover.lay-split-portrait { display: grid; grid-template-columns: 55% 45%; grid-template-rows: auto 1fr auto;
  grid-template-areas: "top top" "mid panel" "bottom panel"; column-gap: 80px; align-items: center; }
.slide.cover.lay-split-portrait > .top { grid-area: top; align-self: start; position: relative; z-index: 1; }
.slide.cover.lay-split-portrait > .cover-mid { grid-area: mid; align-self: center; position: relative; z-index: 1; }
.slide.cover.lay-split-portrait .cover-mid h1 { font-size: 124px; line-height: 0.97; }
.slide.cover.lay-split-portrait .subtitle { max-width: 960px; }
.slide.cover.lay-split-portrait > .bottom { grid-area: bottom; align-self: end; position: relative; z-index: 1; }
/* the right ~45% media plate, flush to the top/right/bottom slide edges */
.slide.cover.lay-split-portrait .cover-panel,
.slide.cover.lay-split-portrait::after { grid-area: panel; align-self: stretch;
  margin: calc(-1 * var(--pad-top)) calc(-1 * var(--pad-x)) calc(-1 * var(--pad-bottom)) 0; }
/* no-media fallback: a soft tonal plate (lighter than side-panel's deep accent
   band) so the plate still reads as an intentional composition without an image */
.slide.cover.lay-split-portrait::after { content: ""; z-index: 0;
  background: linear-gradient(150deg, color-mix(in srgb, var(--slide-accent) 22%, var(--slide-canvas)) 0%, color-mix(in srgb, var(--slide-accent) 6%, var(--slide-canvas)) 100%); }
.slide.cover.lay-split-portrait .cover-panel { position: relative; z-index: 1; overflow: hidden; }
.slide.cover.lay-split-portrait .cover-panel .art { width: 100%; height: 100%; }
/* a real panel image supersedes the ::after fallback */
.slide.cover.lay-split-portrait:has(.cover-panel .art-layer[style])::after { display: none; }
/* the media is CONFINED to the right panel, so the left rail always sits on the
   theme canvas — re-assert the normal ink tokens, overriding the cover .has-bg
   white-text rules (which assume a FULL-BLEED background). Higher specificity
   wins. This keeps the left rail legible on a light theme even when a panel
   gradient/image set .has-bg on the slide. */
.slide.cover.lay-split-portrait.has-bg .cover-mid h1 { color: var(--slide-text); }
.slide.cover.lay-split-portrait.has-bg .subtitle { color: var(--slide-text-2); }
.slide.cover.lay-split-portrait.has-bg .byline { color: var(--slide-text-3); }
.slide.cover.lay-split-portrait.has-bg .mark,
.slide.cover.lay-split-portrait.has-bg .eyebrow.accent { color: var(--slide-text-2); }

/* ========== full-bleed ========== */
/* --- full-bleed · overlay-centre --- */
/* ══ full-bleed · overlay-centre ══════════════════════════════════════════
   A centred poster / title-card composition: the text block sits dead-centre
   of the frame in a contained column, centre-aligned, over a radial vignette
   so the middle of the image is darkened enough to read while the edges of the
   picture stay open. Distinct from the default overlay-bottom (left-aligned
   block pinned to the lower edge under a bottom-up gradient).
   NOTE on colours: full-bleed is a MEDIA kind (see MEDIA_KINDS in
   render-slides.ts) — it is NEVER given .has-bg.bg-media, so its text is always
   light-on-dark over .fb-scrim. The literal rgba(8,12,18,…) scrim values below
   deliberately mirror the kind's existing base .fb-scrim convention
   (slides.css §full-bleed); they are NOT theme-adaptive surfaces and must not
   be swapped for var(--slide-*) text tokens (which would render dark-on-dark).
   The accent rule uses the theme token var(--slide-accent). British spelling. */
.slide.k-full-bleed.lay-overlay-centre .fb-scrim {
  /* swap the bottom-up gradient for a centre vignette that protects the middle */
  background:
    radial-gradient(120% 120% at 50% 50%, rgba(8,12,18,0.30) 0%, rgba(8,12,18,0.78) 70%, rgba(8,12,18,0.88) 100%);
}
.slide.k-full-bleed.lay-overlay-centre .fb-content {
  /* fill the canvas and centre the column on both axes (default pins bottom-left) */
  left: 0; right: 0; top: 0; bottom: 0;
  display: flex; flex-direction: column;
  justify-content: center; align-items: center;
  text-align: center;
  padding: var(--pad-top) 220px var(--pad-bottom);
  max-width: none;
}
.slide.k-full-bleed.lay-overlay-centre .fb-content > * { width: 100%; max-width: 1400px; }
.slide.k-full-bleed.lay-overlay-centre .fb-content .eyebrow {
  /* a centred accent rule beneath the eyebrow anchors the composition */
  display: flex; flex-direction: column; align-items: center; gap: 22px; margin-bottom: 28px;
}
.slide.k-full-bleed.lay-overlay-centre .fb-content .eyebrow::after {
  content: ""; width: 88px; height: 2px; background: var(--slide-accent); display: block;
}
.slide.k-full-bleed.lay-overlay-centre .fb-content .display {
  font-size: 116px; line-height: 1.0; letter-spacing: -0.045em; margin-inline: auto; max-width: 1400px;
}
.slide.k-full-bleed.lay-overlay-centre .fb-content .lede {
  margin-top: 28px; margin-inline: auto; max-width: 1040px;
}
/* --- full-bleed · side-panel --- */
/* ══ full-bleed · side-panel ══════════════════════════════════════════════
   A 42%-wide ink/glass panel down the LEFT edge holds the text, vertically
   centred, while the image bleeds across the right ~58%. This is a structural
   split, not an overlay — fundamentally different geometry from both the
   bottom-overlay default and the centred-poster overlay-centre. The full-width
   bottom gradient is replaced by a left-anchored horizontal scrim that feathers
   the image into the panel edge, so there is no hard seam.
   NOTE on colours: full-bleed is a MEDIA kind — never .has-bg.bg-media — so
   text is always light-on-dark over the panel. The literal rgba(8,12,18,…) /
   rgba(255,255,255,…) values below intentionally match the kind's existing
   base .fb-scrim / .fb-content convention and must not be swapped for
   theme-adaptive var(--slide-*) text tokens (which would go dark-on-dark).
   British spelling throughout. */
.slide.k-full-bleed.lay-side-panel .fb-scrim {
  /* protect the panel zone + feather the join; image stays clear on the right */
  background:
    linear-gradient(90deg, rgba(8,12,18,0.92) 0%, rgba(8,12,18,0.88) 38%, rgba(8,12,18,0.42) 50%, transparent 64%);
}
.slide.k-full-bleed.lay-side-panel .fb-content {
  /* a left column band, content vertically centred (default pins to bottom) */
  left: 0; right: auto; top: 0; bottom: 0;
  width: 42%;
  display: flex; flex-direction: column; justify-content: center;
  align-items: flex-start; text-align: left;
  padding: var(--pad-top) 88px var(--pad-bottom) var(--pad-x);
  max-width: none;
  /* a faint glass wash over the scrim gives the panel a defined inner edge */
  background: linear-gradient(90deg, rgba(8,12,18,0.30) 0%, rgba(8,12,18,0.0) 100%);
  border-right: 1px solid rgba(255,255,255,0.16);
}
.slide.k-full-bleed.lay-side-panel .fb-content .eyebrow { margin-bottom: 22px; }
.slide.k-full-bleed.lay-side-panel .fb-content .display {
  /* the column is narrow, so the headline steps down from the full-bleed 128px */
  font-size: 82px; line-height: 1.02; letter-spacing: -0.04em;
}
.slide.k-full-bleed.lay-side-panel .fb-content .lede {
  margin-top: 24px; max-width: 100%;
}

/* ========== quote-image ========== */
/* --- quote-image · centred-mark --- */
/* ── quote-image · centred-mark — cinematic, symmetric pull-quote ────────
   Distinct from the default portrait-overlay (text flush to the left edge over
   the photo): here the whole composition is centred horizontally AND vertically,
   the scrim becomes a centred radial vignette, and the giant serif mark is
   hoisted ABOVE the quote as a deliberate ornament rather than a left-hanging
   glyph. The by-line stacks as a centred row beneath — a full-bleed title card.
   quote-image is a MEDIA_KIND so it keeps its OWN scrim and the base #fff quote
   colour (it is never given .has-bg.bg-media); the literal scrim colours are the
   sanctioned art-background exception and match the existing .qi-scrim palette. */
.slide.quote-image.lay-centred-mark .qi-scrim {
  background: radial-gradient(125% 125% at 50% 45%, rgba(8,12,18,0.30) 0%, rgba(8,12,18,0.70) 58%, rgba(8,12,18,0.90) 100%);
}
.slide.quote-image.lay-centred-mark .qi-content {
  padding: var(--pad-top) 220px var(--pad-bottom);
  max-width: none;
  align-items: center;
  text-align: center;
}
.slide.quote-image.lay-centred-mark .qi-mark {
  height: auto;
  line-height: 0.5;
  font-size: 220px;
  margin-bottom: 8px;
  opacity: 0.92;
}
.slide.quote-image.lay-centred-mark .qi-quote {
  max-width: 1500px;
  font-size: 80px;
  line-height: 1.1;
  margin-inline: auto;
}
.slide.quote-image.lay-centred-mark .qi-by {
  justify-content: center;
  margin-top: 56px;
}
.slide.quote-image.lay-centred-mark .qi-by > div {
  text-align: center;
}
/* --- quote-image · side-quote --- */
/* ── quote-image · side-quote — image band + solid ink panel ─────────────
   Boldly different geometry: instead of text floating over the whole photo, the
   image is pushed into a right-hand band and the quote lives in a solid ink
   panel on the left, tied to the mark by a left accent keyline. The ink panel
   uses --slide-ink / --slide-on-ink so it reads identically in light AND dark
   themes (the panel is always dark). NOTE: in the base quote-image the .fb-media
   wrapper (.art.fb-media) is a RELATIVE flex child — the .full-bleed absolute
   rule does NOT apply here — so this layout must promote it to position:absolute
   before left/right take effect. The scrim is repurposed as a soft seam feather
   from the ink edge to transparent. CSS-only on the existing markup. */
.slide.quote-image.lay-side-quote .fb-media {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 54%;
  right: 0;
}
.slide.quote-image.lay-side-quote .qi-scrim {
  left: 50%;
  right: 38%;
  background: linear-gradient(90deg, var(--slide-ink) 0%, transparent 100%);
}
.slide.quote-image.lay-side-quote .qi-content {
  right: 46%;
  left: 0;
  padding: var(--pad-top) 112px var(--pad-bottom) var(--pad-x);
  max-width: none;
  background: var(--slide-ink);
  border-left: 8px solid var(--slide-accent);
}
.slide.quote-image.lay-side-quote .qi-mark {
  font-size: 150px;
  height: 70px;
}
.slide.quote-image.lay-side-quote .qi-quote {
  font-size: 56px;
  line-height: 1.16;
  max-width: 100%;
  color: var(--slide-on-ink);
}
.slide.quote-image.lay-side-quote .qi-name {
  color: var(--slide-on-ink);
}
.slide.quote-image.lay-side-quote .qi-meta {
  color: color-mix(in srgb, var(--slide-on-ink) 70%, transparent);
}

/* ========== quote ========== */
/* --- quote · with-portrait --- */
.slide.k-quote.lay-with-portrait {
  /* Two-column editorial split: a tall portrait column on the left (the
     attribution block restyled as a framed portrait + caption), the title and
     quote ranged in a vertically-centred rail on the right. Distinct geometry
     from the centred stack — and authored CSS-only on the existing markup, so
     the default 'centred' layout is untouched. */
  display: grid;
  grid-template-columns: 480px 1fr;
  grid-template-rows: 1fr;
  column-gap: 96px;
  align-items: center;
}
/* Right column holds the heading + the quote, vertically centred. */
.slide.k-quote.lay-with-portrait > .title-block {
  grid-column: 2; grid-row: 1; align-self: end; margin-bottom: 28px;
}
.slide.k-quote.lay-with-portrait > .testimonial-quote {
  grid-column: 2; grid-row: 1; align-self: center;
  font-size: 52px; line-height: 1.16; max-width: none; margin: 0;
}
/* A short brand rule above the quote anchors the right rail. */
.slide.k-quote.lay-with-portrait > .testimonial-quote::before {
  content: ""; display: block; width: 96px; height: 4px; margin-bottom: 36px;
  border-radius: 100px; background: var(--slide-accent);
}
/* The attribution block becomes the LEFT portrait column: avatar enlarged to a
   portrait card, name/meta stacked beneath it as a caption. No markup change —
   we restack the children that .testimonial-by already contains. */
.slide.k-quote.lay-with-portrait > .testimonial-by {
  grid-column: 1; grid-row: 1;
  flex-direction: column; align-items: stretch; justify-content: center;
  gap: 28px; margin: 0; height: 100%;
}
.slide.k-quote.lay-with-portrait > .testimonial-by > .testimonial-avatar {
  width: 100%; height: auto; aspect-ratio: 1 / 1; max-height: 700px;
  border-radius: 28px; flex: none;
  font-size: 120px; font-weight: 600; letter-spacing: 0.02em;
  /* a deliberate accent frame so it reads as a portrait, not a bubble */
  box-shadow: inset 0 0 0 2px color-mix(in srgb, var(--slide-accent) 55%, transparent), var(--shadow-lg);
}
.slide.k-quote.lay-with-portrait > .testimonial-by > div {
  text-align: left;
}
.slide.k-quote.lay-with-portrait .by-name { font-size: 34px; }
.slide.k-quote.lay-with-portrait .by-meta { font-size: 25px; margin-top: 6px; }
/* --- quote · editorial-serif --- */
.slide.k-quote.lay-editorial-serif {
  /* Magazine pull-quote: heading at the top, an oversized serif quote ranged
     left with a hanging quotation mark, and a hairline-ruled byline at the
     foot. A print-feature voice the sans centred default never has. */
  justify-content: flex-end;            /* drop the quote block into the lower band */
  padding-right: max(var(--pad-x), 320px);
}
.slide.k-quote.lay-editorial-serif .title-block { margin-bottom: 20px; }
.slide.k-quote.lay-editorial-serif .eyebrow {
  font-family: var(--font-sans);        /* keep the eyebrow crisp against the serif */
}
.slide.k-quote.lay-editorial-serif .title {
  font-family: var(--font-serif); letter-spacing: -0.025em;
}
/* Oversized serif quote with a hanging opening mark. */
.slide.k-quote.lay-editorial-serif .testimonial-quote {
  position: relative;
  font-family: var(--font-serif); font-weight: 400;
  font-size: 80px; line-height: 1.1; letter-spacing: -0.025em;
  max-width: 1400px; margin: 0;
  padding-top: 84px;                    /* room for the hanging mark */
}
.slide.k-quote.lay-editorial-serif .testimonial-quote::before {
  content: "\201C";                     /* left double quotation mark */
  position: absolute; top: -40px; left: -6px;
  font-family: var(--font-serif); font-weight: 400;
  font-size: 240px; line-height: 1;
  color: var(--slide-accent); opacity: 0.85;
  pointer-events: none;
}
/* serif earns true italics for the emphasised span */
.slide.k-quote.lay-editorial-serif .testimonial-quote em {
  font-style: italic; color: var(--slide-accent);
}
/* Byline on a hairline rule; the avatar shrinks to a small ringed initial. */
.slide.k-quote.lay-editorial-serif .testimonial-by {
  margin-top: 48px; gap: 22px; align-items: center;
  border-top: 1px solid var(--slide-border);
  padding-top: 26px; max-width: 1400px;
}
.slide.k-quote.lay-editorial-serif .testimonial-avatar {
  width: 56px; height: 56px; font-size: 22px; border-radius: 50%;
  background: transparent; color: var(--slide-accent);
  box-shadow: inset 0 0 0 2px var(--slide-accent);
}
.slide.k-quote.lay-editorial-serif .by-name {
  font-family: var(--font-serif); font-size: 32px; font-weight: 500;
}
.slide.k-quote.lay-editorial-serif .by-meta {
  font-family: var(--font-sans); text-transform: uppercase;
  letter-spacing: 0.12em; font-size: 20px;
}
/* --- quote · billboard --- */
/* ── quote · BILLBOARD — one oversized centred pull-quote, the whole slide given
   over to the words. Minimal chrome: the title-block is suppressed, the avatar
   dropped, and the byline reduced to a small understated centred line. The quote
   fills the canvas, centre-aligned on both axes, with a short accent rule above
   as the only ornament. A poster moment — far bolder than the centred default's
   ranged block. CSS-only on the existing markup; tokens only so it reads on
   light/dark and (where allowed) over .bg-media. */
.slide.k-quote.lay-billboard { justify-content: center; align-items: center; text-align: center; }
/* drop the heading entirely — billboard is the quote and nothing else */
.slide.k-quote.lay-billboard > .title-block { display: none; }
/* the quote dominates: oversized, centred, with a short accent rule above */
.slide.k-quote.lay-billboard .testimonial-quote {
  position: relative;
  margin: 0 auto;
  max-width: 1640px;
  font-size: 92px; line-height: 1.08; letter-spacing: -0.035em;
  text-wrap: balance;
  padding-top: 64px;
}
.slide.k-quote.lay-billboard .testimonial-quote::before {
  content: ""; position: absolute; top: 0; left: 50%; transform: translateX(-50%);
  width: 96px; height: 5px; border-radius: 100px; background: var(--slide-accent);
}
/* byline: a single small centred line; the avatar bubble is dropped */
.slide.k-quote.lay-billboard .testimonial-by {
  justify-content: center; gap: 14px; margin-top: 56px; margin-bottom: 0;
}
.slide.k-quote.lay-billboard .testimonial-avatar { display: none; }
.slide.k-quote.lay-billboard .testimonial-by > div { display: flex; align-items: baseline; gap: 12px; }
.slide.k-quote.lay-billboard .by-name { font-size: 26px; }
.slide.k-quote.lay-billboard .by-meta { font-size: 24px; margin-top: 0; color: var(--slide-text-3); }
.slide.k-quote.lay-billboard .by-meta::before { content: "— "; }

/* ========== split ========== */
/* --- split · image-right --- */
/* ── Split · image-right — mirror of the default: copy on the left, media plate
   hugs the right edge. Driven by CSS order so it is independent of DOM order
   (the renderer swaps DOM order only for side=right; the layout axis must look
   the same regardless). Widened to a 56/44 emphasis so it reads as a deliberate
   re-balance, not just a flip, and the caption follows the media to the right. */
.slide.k-split.lay-image-right { flex-direction: row; }
.slide.k-split.lay-image-right .split-media { order: 2; flex: 0 0 44%; }
.slide.k-split.lay-image-right .split-body {
  order: 1; flex: 1 1 56%;
  padding: var(--pad-top) 112px var(--pad-bottom) var(--pad-x);
  align-items: flex-start;
}
.slide.k-split.lay-image-right .split-cap { left: auto; right: 32px; }

/* --- split · wide-media --- */
/* ── Split · wide-media — the image becomes the hero: a dominant 60% media band,
   copy compressed into a narrow right-hand rail, title scaled up and the body
   bottom-anchored against the baseline so it reads as a magazine 'big picture'
   frame rather than a 44% panel. Tokens are inherited so it stays legible in
   light/dark; the rail divider + accent tick use slide tokens, not literals. */
.slide.k-split.lay-wide-media { flex-direction: row; }
.slide.k-split.lay-wide-media .split-media { flex: 0 0 60%; }
.slide.k-split.lay-wide-media .split-body {
  flex: 1 1 40%;
  justify-content: flex-end;          /* copy sinks to a shared baseline */
  padding: var(--pad-top) var(--pad-x) 88px 80px;
  gap: 22px;
  border-left: 1px solid var(--slide-border);
}
.slide.k-split.lay-wide-media .split-body .title { font-size: 92px; line-height: 1.0; }
.slide.k-split.lay-wide-media .split-body .lede { font-size: var(--type-h3); }
/* a thin accent tick above the eyebrow anchors the rail to the brand */
.slide.k-split.lay-wide-media .split-body .eyebrow::before {
  content: ""; display: block; width: 64px; height: 3px;
  background: var(--slide-accent); margin-bottom: 22px;
}

/* --- split · caption-over --- */
/* ── Split · caption-over — abandons the side-by-side split entirely: the media
   goes full-bleed across the whole canvas and the copy floats over it in a
   frosted, accent-edged card anchored bottom-left. A bespoke scrim (built from
   the slide scrim tokens) keeps the text legible over any gradient/photo.
   'split' is a MEDIA_KIND so it never gets the .bg-media palette flip — the card
   therefore reads light by design via --slide-on-media (it always sits on art). */
.slide.k-split.lay-caption-over { position: relative; display: block; }
.slide.k-split.lay-caption-over .split-media {
  position: absolute; inset: 0; flex: none; width: 100%; height: 100%;
}
/* darken the lower band so the floating card always sits on contrast */
.slide.k-split.lay-caption-over .split-media::before {
  content: ""; position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background: linear-gradient(0deg, var(--slide-scrim-1) 0%, var(--slide-scrim-2) 46%, transparent 78%);
}
.slide.k-split.lay-caption-over .split-body {
  position: absolute; z-index: 2; left: var(--pad-x); bottom: 80px;
  width: min(880px, 56%);
  flex: none; justify-content: flex-end; gap: 22px;
  padding: 48px 52px;
  background: color-mix(in srgb, var(--slide-scrim-1) 60%, transparent);
  -webkit-backdrop-filter: blur(14px) saturate(150%); backdrop-filter: blur(14px) saturate(150%);
  border: 1px solid color-mix(in srgb, var(--slide-on-media) 18%, transparent);
  border-left: 4px solid var(--slide-accent);
  border-radius: 20px;
  color: var(--slide-on-media);
}
/* the card always reads light, so pin the text colours to the on-media token */
.slide.k-split.lay-caption-over .split-body .title { color: var(--slide-on-media); font-size: 72px; }
.slide.k-split.lay-caption-over .split-body .lede { color: color-mix(in srgb, var(--slide-on-media) 86%, transparent); }
.slide.k-split.lay-caption-over .split-body .eyebrow.accent { color: var(--slide-on-media); }
.slide.k-split.lay-caption-over .split-body ul.bullets li { color: color-mix(in srgb, var(--slide-on-media) 92%, transparent); }
/* the in-image .split-cap would collide with the card — hide it here */
.slide.k-split.lay-caption-over .split-cap { display: none; }
@media (prefers-reduced-motion: reduce) {
  .slide.k-split.lay-caption-over .split-media .art-layer { animation: none; }
}
/* --- split · image-top --- */
/* ── Split · image-top — abandons the side-by-side column for a HORIZONTAL band
   stack: a wide media plate fills the top ~52% of the canvas, the copy sits in a
   padded block beneath. Media is forced ABOVE the copy via flex order regardless
   of slide.side (the renderer swaps DOM order for side=right; this layout reads
   the same either way). A magazine 'lead image then story' frame, distinct from
   the vertical splits. Tokens only; the caption pins to the media band's foot. */
.slide.k-split.lay-image-top { flex-direction: column; align-items: stretch; }
.slide.k-split.lay-image-top .split-media {
  order: 1; flex: 0 0 52%; width: 100%; position: relative; min-height: 0;
}
.slide.k-split.lay-image-top .split-body {
  order: 2; flex: 1 1 auto;
  justify-content: center;
  padding: 56px var(--pad-x) var(--pad-bottom);
  gap: 22px;
}
.slide.k-split.lay-image-top .split-body .title { font-size: 76px; line-height: 1.02; }
/* keep the in-image caption anchored to the media band's lower-left */
.slide.k-split.lay-image-top .split-cap { left: 32px; right: auto; bottom: 24px; }


/* ========== gallery ========== */
/* --- gallery · grid-2 --- */
/* ── Gallery · grid-2 — two big poster tiles, caption banded onto the art ──
   Half the columns of the default 3-up, so each cell is a large landscape
   tile; the caption is hoisted onto the bottom of the art as a translucent
   on-media band (reads light over any gradient/image) instead of the card
   strip below it. Reads as a pair of posters, not a row of small cards.
   The cell keeps the base flex column, but its children are absolutely
   positioned, so grid-auto-rows:1fr is what sizes each tile. */
.slide.k-gallery.lay-grid-2 .gallery-grid {
  grid-template-columns: repeat(2, 1fr);
  grid-auto-rows: 1fr;
  gap: 36px;
  margin-top: 24px;
}
.slide.k-gallery.lay-grid-2 .gallery-cell {
  position: relative;
  border-radius: 24px;
}
.slide.k-gallery.lay-grid-2 .gallery-cell .art {
  position: absolute; inset: 0;
  min-height: 0;            /* let the 1fr row track size the tile, fill it */
  border-radius: 24px;
}
.slide.k-gallery.lay-grid-2 .gallery-cap {
  position: absolute; left: 0; right: 0; bottom: 0; z-index: 2;
  padding: 96px 40px 30px;
  /* dark-to-clear veil so caption text stays legible over the imagery in any theme */
  background: linear-gradient(0deg, var(--slide-scrim-1) 0%, var(--slide-scrim-2) 60%, transparent 100%);
}
.slide.k-gallery.lay-grid-2 .gallery-cap-t { color: var(--slide-on-media); font-size: 38px; }
.slide.k-gallery.lay-grid-2 .gallery-cap-n {
  color: color-mix(in srgb, var(--slide-on-media) 82%, transparent);
  font-size: 25px; margin-top: 8px;
}
/* --- gallery · mosaic --- */
/* ── Gallery · mosaic — hero tile + dense surrounding tiles ────────────────
   An asymmetric editorial mosaic: a 4-column / 2-row dense grid where the
   FIRST cell spans 2×2 to anchor the composition as a hero, and the rest of
   the items flow densely into the remaining cells (grid-auto-flow:dense fills
   the gaps). Completely different geometry from the uniform 3-up default. */
.slide.k-gallery.lay-mosaic .gallery-grid {
  grid-template-columns: repeat(4, 1fr);
  grid-auto-rows: 1fr;
  grid-auto-flow: dense;
  gap: 20px;
  margin-top: 24px;
}
.slide.k-gallery.lay-mosaic .gallery-cell {
  position: relative;
  border-radius: 18px;
}
.slide.k-gallery.lay-mosaic .gallery-cell .art {
  position: absolute; inset: 0; min-height: 0; border-radius: 18px;
}
/* hero: top-left, two columns wide and the full height of the strip */
.slide.k-gallery.lay-mosaic .gallery-cell:first-child {
  grid-column: span 2;
  grid-row: span 2;
}
/* captions ride onto each tile as an on-media band so the dense grid stays tight */
.slide.k-gallery.lay-mosaic .gallery-cap {
  position: absolute; left: 0; right: 0; bottom: 0; z-index: 2;
  padding: 64px 24px 22px;
  background: linear-gradient(0deg, var(--slide-scrim-1) 0%, var(--slide-scrim-2) 64%, transparent 100%);
}
.slide.k-gallery.lay-mosaic .gallery-cap-t { color: var(--slide-on-media); font-size: 27px; }
.slide.k-gallery.lay-mosaic .gallery-cap-n {
  color: color-mix(in srgb, var(--slide-on-media) 80%, transparent);
  font-size: 21px; margin-top: 5px;
}
/* the hero earns a larger caption to match its scale */
.slide.k-gallery.lay-mosaic .gallery-cell:first-child .gallery-cap-t { font-size: 40px; }
.slide.k-gallery.lay-mosaic .gallery-cell:first-child .gallery-cap-n { font-size: 25px; }
/* --- gallery · filmstrip --- */
/* ── Gallery · filmstrip — one horizontal contact-sheet row ───────────
   Every item lines up side-by-side in a single equal-width row of tall
   portrait frames sharing one baseline, with the caption beneath each frame.
   A pair of rules top and bottom frame the strip like a contact sheet — a
   landscape filmstrip rhythm, distinct from the stacked default and the
   on-media bands of the other layouts. Scales to any item count via
   grid-auto-columns. Captions keep the base palette tokens so they stay
   legible in light/dark and flip light automatically under .has-bg.bg-media. */
.slide.k-gallery.lay-filmstrip .gallery-grid {
  grid-template-columns: none;
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
  gap: 28px;
  margin-top: 40px;
  padding: 28px 0;
  /* sprocket-style framing rules top + bottom */
  border-top: 2px solid var(--slide-border);
  border-bottom: 2px solid var(--slide-border);
  align-items: stretch;
}
.slide.k-gallery.lay-filmstrip .gallery-cell {
  background: transparent;
  border: 0;
  border-radius: 0;
  overflow: visible;
}
.slide.k-gallery.lay-filmstrip .gallery-cell .art {
  flex: 1;
  min-height: 0;            /* portrait frame; the stretched row track gives the height */
  border-radius: 14px;
  border: 1px solid var(--slide-border);
}
.slide.k-gallery.lay-filmstrip .gallery-cap {
  flex: none;
  padding: 18px 4px 0;
}
.slide.k-gallery.lay-filmstrip .gallery-cap-t { font-size: 27px; }
.slide.k-gallery.lay-filmstrip .gallery-cap-n { font-size: 21px; margin-top: 5px; }

/* ========== agenda ========== */
/* --- agenda · two-col --- */
/* ══ agenda · two-col ══ The single running-order column splits into TWO parallel
   columns via CSS multi-column, so the eye reads top-to-bottom down the left
   half then the right — a balanced double-stack, obviously different from the
   default full-width single list. The numbered counter + note are preserved.
   Items are kept whole (no mid-item column breaks) and each gets a tinted
   surface card so the two streams read as distinct lanes. */
.slide.k-agenda.lay-two-col .agenda {
  display: block;            /* leave flex; multi-column owns the flow */
  columns: 2;
  column-gap: 72px;
  margin-top: 16px;
}
.slide.k-agenda.lay-two-col .agenda-item {
  /* break the 110px+1fr grid into a stacked card so it tiles inside a column */
  display: block;
  break-inside: avoid;
  -webkit-column-break-inside: avoid;
  border: 1px solid var(--slide-border);
  border-radius: 18px;
  background: var(--slide-surface);
  padding: 28px 32px 30px;
  margin: 0 0 22px;
}
.slide.k-agenda.lay-two-col .agenda-item::before {
  /* counter moves to a chip above the title rather than a side gutter */
  display: block;
  font-size: 26px;
  margin-bottom: 12px;
  color: var(--slide-accent);
}
.slide.k-agenda.lay-two-col .agenda-item.active {
  /* drop the default left-rail/negative-margin trick; highlight the card edge */
  border-color: var(--slide-accent);
  box-shadow: 0 0 0 2px var(--slide-accent);
  padding-left: 32px;
  margin-left: 0;
}
.slide.k-agenda.lay-two-col .agenda-title { font-size: 34px; }
.slide.k-agenda.lay-two-col .agenda-note { font-size: 23px; margin-top: 6px; }
/* --- agenda · with-image [needsMarkup] --- */
/* ══ agenda · with-image ══ A two-pane split: the running order is squeezed into
   the LEFT 56% as a tight list, and the RIGHT 40% becomes a full-height media
   panel. With no media element in the markup, the panel is painted as a
   brand-wash art surface via a pseudo-element on the slide, so the layout is
   genuinely a side-by-side composition, not just a narrower list. (See
   markupNote for swapping the wash for a real image.) */
.slide.k-agenda.lay-with-image { position: relative; }
.slide.k-agenda.lay-with-image .title-block { max-width: 56%; }
.slide.k-agenda.lay-with-image .agenda {
  width: 56%;
  margin-top: 8px;
}
.slide.k-agenda.lay-with-image .agenda-item { padding: 24px 0; gap: 26px; grid-template-columns: 84px 1fr; }
.slide.k-agenda.lay-with-image .agenda-item::before { font-size: 26px; }
.slide.k-agenda.lay-with-image .agenda-title { font-size: 34px; }
.slide.k-agenda.lay-with-image .agenda-note { font-size: 22px; }
/* the media panel — pinned to the right edge, full bleed top-to-bottom */
.slide.k-agenda.lay-with-image::after {
  content: "";
  position: absolute;
  top: 0; bottom: 0; right: 0;
  width: 40%;
  z-index: 0;
  background:
    linear-gradient(135deg,
      color-mix(in srgb, var(--slide-accent) 26%, var(--slide-canvas)) 0%,
      color-mix(in srgb, var(--slide-accent) 8%, var(--slide-canvas)) 60%,
      var(--slide-surface) 100%);
  border-left: 1px solid var(--slide-border);
}
/* keep real content above the painted panel */
.slide.k-agenda.lay-with-image > .title-block,
.slide.k-agenda.lay-with-image > .agenda,
.slide.k-agenda.lay-with-image > .footer-meta { position: relative; z-index: 1; }
/* on a media background the slide already carries art + scrim — let it show
   through with a token-driven translucent panel instead of a brand wash */
.slide.k-agenda.lay-with-image.has-bg.bg-media::after {
  background: var(--slide-surface);
  border-left-color: var(--slide-border);
}
/* Option B: a real .ag-media rail in the right 40% — pinned full-height to the
   right edge, sitting above the content rail but below the list text. */
.slide.k-agenda.lay-with-image .ag-media {
  position: absolute;
  top: 0; bottom: 0; right: 0;
  width: 40%;
  z-index: 0;
  overflow: hidden;
  border-left: 1px solid var(--slide-border);
}
.slide.k-agenda.lay-with-image .ag-media .art { width: 100%; height: 100%; }
/* a real media rail supersedes the painted ::after wash */
.slide.k-agenda.lay-with-image:has(.ag-media)::after { display: none; }
/* --- agenda · ledger --- */
/* ══ agenda · ledger ══ A ruled ledger / index table. Every row is one full-width
   strong-ruled line: a wide tabular number, the section title pushed left, and
   the note RIGHT-aligned at the far edge — like a contents page or invoice
   register. Heavier rules + tabular figures + a baseline-aligned three-track
   grid make it read as a document table, distinct from both the default list
   and the card/columns layouts. */
.slide.k-agenda.lay-ledger .agenda { margin-top: 20px; border-top: 2px solid var(--slide-text-3); }
.slide.k-agenda.lay-ledger .agenda-item {
  /* number | title | note(right) — the inner <div> spans the title+note tracks
     via display:contents so its children sit on the row grid directly */
  grid-template-columns: 120px 1fr auto;
  gap: 0 48px;
  align-items: baseline;
  padding: 26px 0;
  border-top: 1px solid var(--slide-border);
  border-bottom: none;
}
.slide.k-agenda.lay-ledger .agenda-item:last-child { border-bottom: 2px solid var(--slide-text-3); }
.slide.k-agenda.lay-ledger .agenda-item > div { display: contents; }
.slide.k-agenda.lay-ledger .agenda-item::before {
  font-size: 38px;
  font-weight: 400;
  color: var(--slide-accent);
  letter-spacing: 0.02em;
}
.slide.k-agenda.lay-ledger .agenda-title {
  font-size: 38px;
  align-self: baseline;
}
.slide.k-agenda.lay-ledger .agenda-note {
  /* third track, hard right, like a ledger amount column */
  grid-column: 3;
  margin-top: 0;
  text-align: right;
  font-size: 24px;
  max-width: 560px;
  color: var(--slide-text-3);
  text-wrap: balance;
}
.slide.k-agenda.lay-ledger .agenda-item.active {
  /* swap the default rail trick for a full-row tint band that bleeds into the
     side padding, with an inset accent bar */
  border-left: none;
  background: color-mix(in srgb, var(--slide-accent) 8%, transparent);
  box-shadow: inset 6px 0 0 0 var(--slide-accent);
  padding: 26px 24px;
  margin-left: -24px;
  margin-right: -24px;
}
.slide.k-agenda.lay-ledger .agenda-item.active .agenda-title,
.slide.k-agenda.lay-ledger .agenda-item.active::before { color: var(--slide-accent); }
/* --- agenda · cards --- */
/* ══ agenda · cards ══ The ruled vertical list becomes a 2-column grid of
   self-contained cards: each item a bordered tile with a big index numeral in a
   header row over the title + note. Reads as a modular board rather than a
   contents list — good for an agenda you want to feel like a set of tracks.
   CSS-only on the existing <ol.agenda> / <li.agenda-item> markup (the index is
   the same `agenda` counter, just restyled into a chip). Tokens only, so it
   adapts to light/dark and degrades legibly under .has-bg.bg-media. */
.slide.k-agenda.lay-cards .agenda {
  display: grid;
  grid-template-columns: 1fr 1fr;
  grid-auto-rows: 1fr;
  gap: 28px 32px;
  margin-top: 24px;
  flex: 1;
  align-content: stretch;
}
.slide.k-agenda.lay-cards .agenda-item {
  /* drop the ruled-row grid; each tile lays out as numeral header + body stack */
  display: flex;
  flex-direction: column;
  gap: 18px;
  padding: 40px 44px;
  border: 1px solid var(--slide-border);
  border-top: 1px solid var(--slide-border);   /* override the base top-rule */
  border-radius: 22px;
  background: var(--slide-surface);
  min-height: 0;
}
.slide.s-ink.k-agenda.lay-cards .agenda-item { background: rgba(255,255,255,0.05); border-color: rgba(255,255,255,0.12); }
.slide.k-agenda.lay-cards .agenda-item:last-child { border-bottom: 1px solid var(--slide-border); }
/* the counter becomes a leading chip numeral at the top of each card. The chip
   tint + a hairline accent border keep it reading as a defined element on both
   light and dark themes (where a translucent accent fill alone goes faint). */
.slide.k-agenda.lay-cards .agenda-item::before {
  content: counter(ag, decimal-leading-zero);
  width: 72px; height: 72px;
  border-radius: 18px;
  display: grid; place-items: center;
  background: color-mix(in srgb, var(--slide-accent) 16%, var(--slide-canvas));
  border: 1px solid color-mix(in srgb, var(--slide-accent) 40%, transparent);
  color: var(--slide-accent);
  font-size: 34px; font-weight: 500; letter-spacing: 0.02em;
  font-variant-numeric: tabular-nums;
}
.slide.k-agenda.lay-cards .agenda-title { font-size: 36px; line-height: 1.1; }
.slide.k-agenda.lay-cards .agenda-note { font-size: 23px; margin-top: 0; }
/* active card: a soft accent fill + an inset accent rail (replaces the base's
   negative-margin trick which would break the card border) */
.slide.k-agenda.lay-cards .agenda .agenda-item.active {
  background: color-mix(in srgb, var(--slide-accent) 12%, var(--slide-surface));
  border-color: color-mix(in srgb, var(--slide-accent) 45%, var(--slide-border));
  box-shadow: inset 5px 0 0 var(--slide-accent);
  padding-left: 44px;
}
/* density: tighten when the grid grows to 5–6 items (3 rows) so nothing clips */
.slide.k-agenda.lay-cards .agenda:has(.agenda-item:nth-child(5)) .agenda-item { padding: 30px 36px; gap: 14px; }
.slide.k-agenda.lay-cards .agenda:has(.agenda-item:nth-child(5)) .agenda-item::before { width: 60px; height: 60px; font-size: 30px; }
.slide.k-agenda.lay-cards .agenda:has(.agenda-item:nth-child(5)) .agenda-title { font-size: 32px; }

/* ========== stat-row ========== */
/* --- stat-row · centred --- */
/* ── stat-row · CENTRED — vertically + horizontally centred hero of stats ── */
.slide.k-stat-row.lay-centred { justify-content: center; align-items: center; text-align: center; }
.slide.k-stat-row.lay-centred .title-block { align-items: center; max-width: 1400px; margin-left: auto; margin-right: auto; margin-bottom: 56px; }
.slide.k-stat-row.lay-centred .lede { margin-left: auto; margin-right: auto; }
/* keep the base grid auto-flow (columns) but collapse the gap so the hairline
   dividers carry the separation, and centre each pillar */
.slide.k-stat-row.lay-centred .stat-row { gap: 0; margin-top: 0; width: 100%; justify-items: center; align-items: center; }
.slide.k-stat-row.lay-centred .stat { display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 0 72px; position: relative; }
/* vertical divider between adjacent stats — token-driven, so it flips with theme + under .bg-media */
.slide.k-stat-row.lay-centred .stat:not(:first-child)::before {
  content: ""; position: absolute; left: 0; top: 50%; transform: translateY(-50%);
  width: 1px; height: 132px; background: var(--slide-border); }
.slide.k-stat-row.lay-centred .n { font-size: 132px; }
.slide.k-stat-row.lay-centred .label { max-width: 360px; margin-top: 22px; text-align: center; }
/* --- stat-row · colour-band --- */
/* ── stat-row · COLOUR-BAND — stats banded inside a solid accent block ───── */
/* The title-block stays in normal flow at the top and is authored nothing here, so
   it keeps the theme-aware base palette (dark ink on paper / on-media light under
   .bg-media). Only the stat-row becomes a solid accent banner with on-primary ink. */
.slide.k-stat-row.lay-colour-band .stat-row {
  background: var(--slide-accent); border-radius: 28px; box-shadow: var(--shadow-lg);
  padding: 64px 72px; gap: 0; margin-top: 40px; align-items: center; }
.slide.k-stat-row.lay-colour-band .stat { position: relative; padding: 0 56px; }
/* translucent vertical divider, drawn from the on-primary ink so it reads on the band */
.slide.k-stat-row.lay-colour-band .stat:not(:first-child)::before {
  content: ""; position: absolute; left: 0; top: 50%; transform: translateY(-50%);
  width: 1px; height: 96px; background: color-mix(in srgb, var(--slide-on-accent) 28%, transparent); }
/* The band is an opaque solid fill, so it carries its own legible palette — the
   sanctioned accent / on-primary pairing (mirrors the base .price-badge). This reads
   on any paper, in dark theme, and over .has-bg.bg-media alike, because the fill is
   opaque and independent of the media backdrop. */
.slide.k-stat-row.lay-colour-band .stat .n { color: var(--slide-on-accent); }
/* the in-number accent can't pop via colour inside an accent band — distinguish it
   with a thick on-primary underline instead */
.slide.k-stat-row.lay-colour-band .stat .n .accent {
  color: var(--slide-on-accent); text-decoration: underline; text-decoration-thickness: 5px; text-underline-offset: 8px; }
.slide.k-stat-row.lay-colour-band .stat .label { color: var(--slide-on-accent); opacity: 0.78; max-width: 380px; }
/* --- stat-row · ledger --- */
/* ── stat-row · LEDGER — stats stacked as ruled table rows (number | label) ─ */
/* swap the horizontal grid for a single vertical column of ruled rows */
.slide.k-stat-row.lay-ledger .stat-row { display: flex; flex-direction: column; gap: 0; margin-top: 32px; }
.slide.k-stat-row.lay-ledger .stat {
  display: grid; grid-template-columns: minmax(360px, max-content) 1fr; column-gap: 56px;
  align-items: baseline; padding: 34px 8px; border-top: 1px solid var(--slide-border); }
.slide.k-stat-row.lay-ledger .stat:last-child { border-bottom: 1px solid var(--slide-border); }
/* tabular figures so the left number column lines up cleanly down the rows */
.slide.k-stat-row.lay-ledger .n { font-size: 88px; line-height: 1; font-variant-numeric: tabular-nums; }
/* label moves to the right column, vertically centred against the big number */
.slide.k-stat-row.lay-ledger .label { margin-top: 0; max-width: none; text-align: left; font-size: 28px; align-self: center; }
/* --- stat-row · stacked --- */
/* ── stat-row · STACKED — the FIRST stat becomes a full-width hero (an oversized
   headline figure), the remaining stats drop into a supporting row beneath it,
   divided from the hero by a hairline rule. More dramatic than the equal 'row'
   default: one figure leads, the rest support. CSS-only on the flat sibling
   .stat list — first child takes a full flex row, the rest wrap onto the next
   row and share it equally. Works for 2–4 stats. Tokens only. */
.slide.k-stat-row.lay-stacked .stat-row {
  display: flex;
  flex-wrap: wrap;
  flex-direction: row;
  gap: 0;
  margin-top: 24px;
  align-items: stretch;
  align-content: center;
}
/* the hero — full width, an enormous figure with the label ranged beside it */
.slide.k-stat-row.lay-stacked .stat:first-child {
  flex: 0 0 100%;
  display: flex;
  align-items: baseline;
  gap: 48px;
  padding-bottom: 44px;
  margin-bottom: 8px;
  border-bottom: 1px solid var(--slide-border);
}
.slide.k-stat-row.lay-stacked .stat:first-child .n { font-size: 220px; line-height: 0.86; letter-spacing: -0.05em; }
.slide.k-stat-row.lay-stacked .stat:first-child .label { font-size: 32px; margin-top: 0; max-width: 720px; align-self: center; }
/* the supporting stats share the row beneath, each its own column */
.slide.k-stat-row.lay-stacked .stat:not(:first-child) {
  flex: 1 1 0;
  min-width: 0;
  padding: 44px 56px 0 0;
}
.slide.k-stat-row.lay-stacked .stat:not(:first-child) .n { font-size: 84px; }
.slide.k-stat-row.lay-stacked .stat:not(:first-child) .label { font-size: 23px; }

/* ========== bignum ========== */
/* --- bignum · split-context --- */
/* ── bignum · split-context: the figure stands alone on the right, all the
   words (eyebrow + title + lede) read down the left as a narrow context
   column, divided by a hairline rule. Genuinely different geometry from the
   top-stacked 'centred' default: a two-column, vertically centred split.
   CSS-only on the existing .title-block + .bignum siblings (the only two
   in-flow children of .slide; .footer-meta and any .slide-bg/.slide-scrim
   are position:absolute, so they never take a grid cell). */
.slide.k-bignum.lay-split-context {
  display: grid;
  grid-template-columns: minmax(440px, 0.9fr) 1.1fr;
  column-gap: 0;
  align-items: center;
}
.slide.k-bignum.lay-split-context .title-block {
  margin: 0;
  align-self: center;
  padding-right: 80px;
  border-right: 2px solid var(--slide-border);
  height: 62%;
  justify-content: center;
}
.slide.k-bignum.lay-split-context .bignum {
  margin: 0;
  padding-left: 96px;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-self: center;
}
/* the figure dominates the right field */
.slide.k-bignum.lay-split-context .bignum-value {
  font-size: 320px;
  line-height: 0.84;
}
.slide.k-bignum.lay-split-context .bignum-label {
  font-size: 34px;
  max-width: 760px;
  margin-top: 32px;
}
.slide.k-bignum.lay-split-context .bignum-delta {
  font-size: 30px;
  margin-top: 28px;
}
/* --- bignum · band --- */
/* ── bignum · band: the title-block pins to the top, then the figure drops
   into a tinted horizontal band that runs full-bleed edge-to-edge across the
   canvas, with the giant number on the left and the label/delta riding in a
   column beside it on the right of the band. Bold banding geometry —
   distinct from both the centred stack and the side-by-side split.
   The band tint reads on light + dark; s-ink and bg-media get their own
   token-only surfaces so light text never lands on a light band. */
.slide.k-bignum.lay-band {
  justify-content: space-between;
}
.slide.k-bignum.lay-band .title-block {
  margin: 0;
  padding-top: 8px;
}
/* break the band out of the slide's horizontal padding to span full width;
   the slide's default padding-bottom is kept so the band clears the absolute
   .footer-meta (bignum renders a footer — it is not in NO_FOOT). */
.slide.k-bignum.lay-band .bignum {
  margin: 0 calc(-1 * var(--pad-x)) 0;
  padding: 72px var(--pad-x) 88px;
  background: color-mix(in srgb, var(--slide-accent) 12%, var(--slide-canvas));
  border-top: 3px solid var(--slide-accent);
  display: grid;
  grid-template-columns: auto 1fr;
  align-items: end;
  column-gap: 88px;
  row-gap: 0;
}
/* ink band: a darker accent-over-ink panel keeps the cream s-ink text legible
   (var(--slide-surface) is a LIGHT raised surface under s-ink, so it is wrong
   here). bg-media: reuse the translucent on-media surface token. */
.slide.s-ink.k-bignum.lay-band .bignum {
  background: color-mix(in srgb, var(--slide-accent) 22%, var(--slide-ink));
}
.slide.has-bg.bg-media.k-bignum.lay-band .bignum {
  background: var(--slide-surface);
}
/* the figure anchors the band's left; it spans both rows of the right column.
   :first-child is the value's wrapper div (the actual grid item). */
.slide.k-bignum.lay-band .bignum > :first-child {
  grid-row: 1 / span 2;
  align-self: center;
}
.slide.k-bignum.lay-band .bignum-value {
  font-size: 300px;
  line-height: 0.82;
}
.slide.k-bignum.lay-band .bignum-label {
  margin: 0 0 12px;
  font-size: 36px;
  max-width: 820px;
}
.slide.k-bignum.lay-band .bignum-delta {
  margin: 0;
  align-self: start;
  font-size: 30px;
}

/* ========== kpi ========== */
/* --- kpi · row --- */
/* ── KPI · row ─────────────────────────────────────────────────────────
   One full-bleed horizontal band. The boxed tiles dissolve into equal
   columns divided by hairline rules; each cell stacks label -> big value
   -> foot. Reads like a finance ticker strip, not a card grid. */
.slide.k-kpi.lay-row .kpi-grid {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
  gap: 0;
  margin-top: 40px;
  flex: 1;
  align-items: stretch;
  border-top: 2px solid var(--slide-border);
  border-bottom: 2px solid var(--slide-border);
}
.slide.k-kpi.lay-row .kpi-tile {
  background: none;
  border: 0;
  border-radius: 0;
  padding: 48px 56px;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 4px;
}
/* keep tiles borderless even on ink bands / media backdrops */
.slide.s-ink.k-kpi.lay-row .kpi-tile,
.slide.has-bg.bg-media.k-kpi.lay-row .kpi-tile { background: none; border: 0; }
.slide.k-kpi.lay-row .kpi-tile + .kpi-tile {
  border-left: 1px solid var(--slide-border);
}
.slide.k-kpi.lay-row .kpi-label {
  font-size: 24px;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--slide-text-3);
}
.slide.k-kpi.lay-row .kpi-value {
  font-size: 104px;
  margin: 10px 0 18px;
}
.slide.k-kpi.lay-row .kpi-foot {
  justify-content: flex-start;
  gap: 24px;
}
.slide.k-kpi.lay-row .spark { width: 100px; }
/* --- kpi · spotlight --- */
/* ── KPI · spotlight ───────────────────────────────────────────────────
   The first KPI becomes a hero — a tall left card with an oversized value —
   and the remaining metrics stack as compact supporting rows on the right.
   Asymmetric feature/supporting split, the opposite of the even 2x2 grid. */
.slide.k-kpi.lay-spotlight .kpi-grid {
  display: grid;
  grid-template-columns: 1.35fr 1fr;
  grid-auto-rows: 1fr;
  gap: 28px;
  margin-top: 28px;
  flex: 1;
  min-height: 0;
}
/* hero — spans the full height of the left column */
.slide.k-kpi.lay-spotlight .kpi-tile:first-child {
  grid-column: 1;
  grid-row: 1 / -1;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: 56px 60px;
  border-radius: 26px;
  background: color-mix(in srgb, var(--slide-accent) 8%, var(--slide-surface));
  border-color: color-mix(in srgb, var(--slide-accent) 30%, var(--slide-border));
}
/* media backdrop: drop the accent wash (it fights the flipped translucent
   surface) and keep the standard translucent card */
.slide.has-bg.bg-media.k-kpi.lay-spotlight .kpi-tile:first-child {
  background: var(--slide-surface);
}
.slide.k-kpi.lay-spotlight .kpi-tile:first-child .kpi-label { font-size: 30px; }
.slide.k-kpi.lay-spotlight .kpi-tile:first-child .kpi-value {
  font-size: 168px;
  margin: 20px 0 28px;
}
.slide.k-kpi.lay-spotlight .kpi-tile:first-child .kpi-foot {
  justify-content: flex-start;
  gap: 28px;
}
.slide.k-kpi.lay-spotlight .kpi-tile:first-child .spark {
  width: 220px;
  height: 56px;
}
/* supporting metrics — compact, right column */
.slide.k-kpi.lay-spotlight .kpi-tile:not(:first-child) {
  grid-column: 2;
  display: grid;
  grid-template-columns: 1fr auto;
  grid-template-rows: auto auto;
  align-items: center;
  column-gap: 20px;
  padding: 24px 32px;
}
.slide.k-kpi.lay-spotlight .kpi-tile:not(:first-child) .kpi-label { grid-column: 1; grid-row: 1; font-size: 23px; }
.slide.k-kpi.lay-spotlight .kpi-tile:not(:first-child) .kpi-value { grid-column: 1; grid-row: 2; font-size: 56px; margin: 2px 0 0; }
.slide.k-kpi.lay-spotlight .kpi-tile:not(:first-child) .kpi-foot { grid-column: 2; grid-row: 1 / -1; flex-direction: column; align-items: flex-end; justify-content: center; gap: 10px; }
.slide.k-kpi.lay-spotlight .kpi-tile:not(:first-child) .spark { width: 110px; height: 34px; }
/* --- kpi · with-image [needsMarkup] --- */
/* ── KPI · with-image ──────────────────────────────────────────────────
   Two-zone composition: a tall media panel on the left, the metrics as a
   single stacked column on the right. Pairs numbers with visual evidence —
   needs an added .kpi-split / .kpi-media wrapper (see markupNote). The grid
   rules below also degrade safely if the markup is not yet changed. */
.slide.k-kpi.lay-with-image .kpi-split {
  display: grid;
  grid-template-columns: 0.82fr 1fr;
  gap: 56px;
  margin-top: 28px;
  flex: 1;
  min-height: 0;
  align-items: stretch;
}
.slide.k-kpi.lay-with-image .kpi-media {
  position: relative;
  border-radius: 24px;
  overflow: hidden;
  min-height: 0;
  background: var(--slide-surface);
  border: 1px solid var(--slide-border);
}
.slide.k-kpi.lay-with-image .kpi-media .art { border-radius: inherit; }
.slide.k-kpi.lay-with-image .kpi-body {
  display: flex;
  flex-direction: column;
  justify-content: center;
  min-width: 0;
}
/* metrics collapse to a single stacked column of compact rows */
.slide.k-kpi.lay-with-image .kpi-grid {
  display: flex;
  flex-direction: column;
  gap: 18px;
  margin-top: 0;
}
.slide.k-kpi.lay-with-image .kpi-tile {
  display: grid;
  grid-template-columns: 1fr auto;
  grid-template-rows: auto auto;
  align-items: center;
  column-gap: 24px;
  padding: 26px 34px;
}
.slide.k-kpi.lay-with-image .kpi-label { grid-column: 1; grid-row: 1; font-size: 24px; }
.slide.k-kpi.lay-with-image .kpi-value { grid-column: 1; grid-row: 2; font-size: 64px; margin: 4px 0 0; }
.slide.k-kpi.lay-with-image .kpi-foot { grid-column: 2; grid-row: 1 / -1; flex-direction: column; align-items: flex-end; justify-content: center; gap: 12px; }
.slide.k-kpi.lay-with-image .spark { width: 120px; height: 38px; }

/* ========== trend ========== */
/* --- trend · area-callout --- */
/* ── Trend · area-callout ─────────────────────────────────────────────────
   Distinct from the default 'area' (read-strip on top, full-width chart below).
   Here the .trend becomes a 2-column grid: a left callout rail carries the
   headline reading vertically, the area chart fills the right column. The big
   value reads as a poster figure, the chart as supporting evidence beside it.
   CSS-only — rearranges the existing .trend-read / .trend-svg / .trend-axis. */
.slide.k-trend.lay-area-callout .trend {
  display: grid;
  grid-template-columns: minmax(360px, 0.9fr) 2.1fr;
  grid-template-rows: 1fr;
  column-gap: 64px;
  align-items: stretch;
}
/* Left rail: the reading, stacked + framed, vertically centred */
.slide.k-trend.lay-area-callout .trend-read {
  grid-column: 1; grid-row: 1;
  flex-direction: column;
  align-items: flex-start;
  justify-content: center;
  gap: 18px;
  margin: 0;
  padding: 8px 40px 8px 0;
  border-right: 2px solid var(--slide-border);
}
.slide.k-trend.lay-area-callout .trend-x {
  font-size: 28px; letter-spacing: 0.1em;
  color: var(--slide-text-3);
}
.slide.k-trend.lay-area-callout .trend-v {
  font-size: 132px; line-height: 0.92;
  color: var(--slide-accent);
}
.slide.k-trend.lay-area-callout .trend-note {
  font-size: 28px; line-height: 1.4;
  color: var(--slide-text-2); max-width: 380px;
}
/* Right: the chart fills its column; svg + axis stack inside it */
.slide.k-trend.lay-area-callout .trend-svg {
  grid-column: 2; grid-row: 1;
  align-self: stretch; height: 100%;
}
.slide.k-trend.lay-area-callout .trend-axis {
  grid-column: 2; grid-row: 1;
  align-self: end;
  margin: 0; padding: 0 6px 6px;
  pointer-events: none;
}

/* --- trend · minimal --- */
/* ── Trend · minimal ──────────────────────────────────────────────────────
   Distinct from the default 'area': no fill, no grid, no poster number — a
   restrained line on open canvas. The reading collapses to a single quiet
   inline caption (label + value share a baseline, small), the line goes
   hairline, the area + grid are suppressed, and the chart is given generous
   vertical breathing room. The opposite end of the spectrum to area-callout. */
.slide.k-trend.lay-minimal .trend {
  justify-content: center;
  gap: 40px;
}
/* reading → one calm caption line, not a hero block */
.slide.k-trend.lay-minimal .trend-read {
  align-items: baseline;
  gap: 18px;
  margin: 0 0 4px;
  padding-bottom: 18px;
  border-bottom: 1px solid var(--slide-border);
}
.slide.k-trend.lay-minimal .trend-x {
  font-size: 24px; letter-spacing: 0.12em;
  color: var(--slide-text-3);
}
.slide.k-trend.lay-minimal .trend-v {
  font-size: 44px; font-weight: 500;
  color: var(--slide-text);
}
.slide.k-trend.lay-minimal .trend-note {
  font-size: 22px; color: var(--slide-text-3);
}
/* the chart is the whole point — give it room, drop the chrome */
.slide.k-trend.lay-minimal .trend-svg {
  flex: 1; min-height: 0;
  max-height: 560px; align-self: center;
}
.slide.k-trend.lay-minimal .trend-area { display: none; }
.slide.k-trend.lay-minimal .cl-grid { display: none; }
/* hairline line; dots keep their default radius so the runtime hover-grow works */
.slide.k-trend.lay-minimal .trend-line { stroke-width: 2.5; }
/* axis → faint, only the rhythm of dates */
.slide.k-trend.lay-minimal .trend-axis {
  margin-top: 18px; padding: 0 30px;
  border-top: 1px solid var(--slide-border);
  padding-top: 14px;
}
.slide.k-trend.lay-minimal .trend-axis span {
  font-size: 20px; color: var(--slide-text-3);
}


/* ========== chart ========== */
/* --- chart · bars-source-band --- */
/* ── chart · bars-source-band ─────────────────────────────────────────────
   Bars pull into a tight, ledger-ruled stack; the source/caption becomes a
   bold full-bleed band stretched edge-to-edge across the slide foot — the
   opposite of the default's airy bars + hairline-rule caption. */
.slide.k-chart.lay-bars-source-band { justify-content: flex-start; }

/* The renderer wraps .chart in an inline-styled `[data-build="1"]` block; make
   that wrapper a fill-height flex column so the chart can actually grow and pin
   its band to the bottom edge (the draft's `.chart{flex:1}` was inert without
   this). The inline `margin-top:8px` forces !important to take the column. */
.slide.k-chart.lay-bars-source-band > [data-build="1"] {
  flex: 1; min-height: 0; display: flex; flex-direction: column;
  margin-top: 16px !important;
}
.slide.k-chart.lay-bars-source-band .chart {
  display: flex; flex-direction: column; flex: 1; min-height: 0; margin-top: 0;
}

/* Ledger-ruled bars: tighter rhythm, a baseline rule under each row, taller
   tracks and an enlarged value column so the eye reads top -> band. */
.slide.k-chart.lay-bars-source-band .chart-bars {
  gap: 0; flex: 1; min-height: 0;
  display: flex; flex-direction: column; justify-content: center;
}
.slide.k-chart.lay-bars-source-band .chart-row {
  grid-template-columns: 320px 1fr 180px;
  gap: 40px;
  margin: 0; padding: 18px 20px;
  border-radius: 0;
  border-bottom: 1px solid var(--slide-border);
}
.slide.k-chart.lay-bars-source-band .chart-row:first-child {
  border-top: 1px solid var(--slide-border);
}
.slide.k-chart.lay-bars-source-band .chart-track { height: 52px; border-radius: 8px; }
.slide.k-chart.lay-bars-source-band .chart-fill { border-radius: 8px; }
.slide.k-chart.lay-bars-source-band .chart-v { font-size: 46px; }

/* Source band: break out of the slide padding to span the full canvas, sit on
   a solid (theme-adaptive) surface, and lift the source/caption to a
   headline-sized read. The surface + text-2 tokens stay legible on light, dark
   and bg-media (where they flip to a translucent panel + light text). */
.slide.k-chart.lay-bars-source-band .chart-cap {
  margin: 32px calc(-1 * var(--pad-x)) calc(-1 * var(--pad-bottom));
  padding: 40px var(--pad-x);
  border-top: 3px solid var(--slide-accent);
  background: var(--slide-surface);
  font-size: 34px; color: var(--slide-text-2);
  display: flex; align-items: center; gap: 20px;
  min-height: 0;
}
.slide.k-chart.lay-bars-source-band .chart-cap::before {
  content: ""; flex: none; width: 14px; height: 14px; border-radius: 50%;
  background: var(--slide-accent);
}
/* --- chart · grouped --- */
/* ── chart · grouped ──────────────────────────────────────────────────────
   The single full-width bar stack banks into a 2-column grid of self-contained
   comparison tiles (label + value stacked over a full-width meter), roughly
   doubling density and reading as grouped pairs rather than one long list.
   NOTE: the meter stays a HORIZONTAL track — the runtime sets the fill via
   inline style.width, so a height-encoded vertical column is not possible
   CSS-only on this markup. */
.slide.k-chart.lay-grouped .chart { margin-top: 16px; }
.slide.k-chart.lay-grouped .chart-bars {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 24px 40px;
}

/* Each row becomes a bordered tile: header line (label + value) over a
   full-width meter, reordered via `order` so the value reads under the label
   and the meter sits at the foot. The track stays horizontal so the runtime's
   inline width:X% fill keeps driving it correctly. */
.slide.k-chart.lay-grouped .chart-row {
  display: flex; flex-direction: column; gap: 16px;
  align-items: stretch;
  margin: 0; padding: 26px 28px;
  border-radius: 18px;
  border: 1px solid var(--slide-border);
  background: var(--slide-surface);
}
.slide.k-chart.lay-grouped .chart-row.is-hover {
  background: color-mix(in srgb, var(--slide-accent) 8%, var(--slide-surface));
  border-color: var(--slide-accent);
}
.slide.k-chart.lay-grouped .chart-k {
  order: 1; font-size: 28px; line-height: 1.2;
}
.slide.k-chart.lay-grouped .chart-v {
  order: 2; align-self: flex-start; text-align: left;
  font-size: 56px; line-height: 1; margin-top: -8px;
}
.slide.k-chart.lay-grouped .chart-track {
  order: 3; height: 18px; border-radius: 9px;
}
.slide.k-chart.lay-grouped .chart-fill { border-radius: 9px; }

/* Caption is a sibling of .chart-bars (not a grid item), so it already spans
   the full width below the two columns — just give it breathing room. */
.slide.k-chart.lay-grouped .chart-cap {
  margin-top: 36px; padding-top: 26px;
}

/* ========== pricing ========== */
/* --- pricing · table [needsMarkup] --- */
/* ── Pricing · layout: table (feature ledger) ──────────────────────────────
   Drops the floating-card geometry for a tariff-table read: tiers become
   flush, gap-less columns separated by hairline rules; each tier's name +
   price compress into a shared header band, and the feature list below turns
   into ruled, zebra-striped rows so the eye scans down a column like a
   spreadsheet. The featured tier is flagged by a tinted column wash + accent
   top rule rather than a lifted card. Reads in light, dark and bg-media. */
.slide.k-pricing.lay-table .price-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 0;
  margin-top: 20px;
  align-items: stretch;
  border: 1px solid var(--slide-border);
  border-radius: 20px;
  overflow: hidden;
}
.slide.k-pricing.lay-table .price-card {
  position: relative;
  background: var(--slide-canvas);
  border: 0;
  border-left: 1px solid var(--slide-border);
  border-radius: 0;
  padding: 0;
  gap: 0;
  box-shadow: none;
}
.slide.k-pricing.lay-table .price-card:first-child { border-left: 0; }
/* ink band + media backdrop: let the band/image show through (canvas is opaque) */
.slide.s-ink.k-pricing.lay-table .price-card,
.slide.has-bg.bg-media.k-pricing.lay-table .price-card { background: transparent; }
/* featured tier: tinted column + accent cap, no lift */
.slide.k-pricing.lay-table .price-card.featured {
  background: color-mix(in srgb, var(--slide-accent) 8%, var(--slide-canvas));
  box-shadow: inset 0 4px 0 0 var(--slide-accent);
}
.slide.has-bg.bg-media.k-pricing.lay-table .price-card.featured {
  background: color-mix(in srgb, var(--slide-accent) 22%, transparent);
}
/* header band: name + price stacked, divider under it */
.slide.k-pricing.lay-table .price-name {
  padding: 30px 36px 4px;
  font-size: 32px;
}
.slide.k-pricing.lay-table .price-amount {
  padding: 0 36px;
  margin: 2px 0 0;
  align-items: baseline;
}
.slide.k-pricing.lay-table .price-num { font-size: 60px; }
.slide.k-pricing.lay-table .price-per { font-size: 24px; }
.slide.k-pricing.lay-table .price-note {
  padding: 8px 36px 22px;
  margin: 0;
  border-bottom: 2px solid var(--slide-border);
  min-height: 84px;
}
/* feature list -> ruled, zebra rows that align vertically per column */
.slide.k-pricing.lay-table .price-feats {
  margin: 0;
  padding: 0;
  border-top: 0;
  gap: 0;
}
.slide.k-pricing.lay-table .price-feats li {
  gap: 14px;
  align-items: center;
  padding: 18px 36px;
  font-size: 23px;
  border-bottom: 1px solid var(--slide-border);
}
.slide.k-pricing.lay-table .price-feats li:nth-child(odd) {
  background: color-mix(in srgb, var(--slide-text) 4%, transparent);
}
.slide.k-pricing.lay-table .price-feats li:last-child { border-bottom: 0; }
/* badge sits inline in the header band, not floating off the top edge */
.slide.k-pricing.lay-table .price-badge {
  position: static;
  display: inline-block;
  margin: 28px 0 -6px 36px;
  font-size: 18px;
  padding: 5px 14px;
}
/* ── pricing · table — true feature-comparison matrix (.pmx) ────────────────
   When the slide carries a `matrix`, the renderer emits a real comparison table
   instead of per-tier cards: a header row (feature gutter + one cell per tier
   name/price) then one row per feature, each cell a tick/cross or a value. Rows
   are display:contents so every cell sits on the one outer grid and columns
   align top-to-bottom. The featured tier column is tinted; rows zebra-stripe.
   Distinct from the .price-grid ledger above (which re-skins the cards). */
.slide.k-pricing.lay-table .pmx {
  display: grid;
  grid-template-columns: 1.4fr repeat(var(--pmx-tiers, 3), 1fr);
  margin-top: 20px;
  border: 1px solid var(--slide-border);
  border-radius: 20px;
  overflow: hidden;
}
.slide.k-pricing.lay-table .pmx-row { display: contents; }
.slide.k-pricing.lay-table .pmx-feat,
.slide.k-pricing.lay-table .pmx-tier,
.slide.k-pricing.lay-table .pmx-cell {
  padding: 18px 28px;
  border-top: 1px solid var(--slide-border);
  display: flex;
  align-items: center;
  font-size: 23px;
}
.slide.k-pricing.lay-table .pmx-cell { justify-content: center; }
/* header band: tier name + price, no top rule */
.slide.k-pricing.lay-table .pmx-head .pmx-feat,
.slide.k-pricing.lay-table .pmx-head .pmx-tier { border-top: 0; }
.slide.k-pricing.lay-table .pmx-tier {
  flex-direction: column;
  align-items: flex-start;
  gap: 2px;
  padding-top: 28px;
  padding-bottom: 24px;
  border-bottom: 2px solid var(--slide-border);
}
.slide.k-pricing.lay-table .pmx-tier .price-name { font-size: 30px; }
.slide.k-pricing.lay-table .pmx-tier .price-amount { align-items: baseline; }
.slide.k-pricing.lay-table .pmx-tier .price-num { font-size: 44px; }
.slide.k-pricing.lay-table .pmx-tier .price-per { font-size: 20px; }
/* zebra rows for the feature gutter + value cells */
.slide.k-pricing.lay-table .pmx-row:nth-of-type(even) .pmx-feat,
.slide.k-pricing.lay-table .pmx-row:nth-of-type(even) .pmx-cell {
  background: color-mix(in srgb, var(--slide-text) 4%, transparent);
}
/* featured tier: tinted column wash across header + every cell */
.slide.k-pricing.lay-table .pmx-tier.featured,
.slide.k-pricing.lay-table .pmx-cell.featured {
  background: color-mix(in srgb, var(--slide-accent) 8%, var(--slide-canvas));
}
.slide.has-bg.bg-media.k-pricing.lay-table .pmx-tier.featured,
.slide.has-bg.bg-media.k-pricing.lay-table .pmx-cell.featured {
  background: color-mix(in srgb, var(--slide-accent) 22%, transparent);
}
/* tick / cross / value glyphs */
.slide.k-pricing.lay-table .pmx-yes svg { width: 28px; height: 28px; color: var(--slide-accent); }
.slide.k-pricing.lay-table .pmx-no svg { width: 24px; height: 24px; color: var(--slide-text-3); }
.slide.k-pricing.lay-table .pmx-val { font-size: 22px; color: var(--slide-text); text-align: center; }
/* --- pricing · spotlight --- */
/* ── Pricing · layout: spotlight (hero tier + rail) ────────────────────────
   Inverts the equal-three-up grid: the featured tier is blown up into a large
   hero panel on the left while every other tier compresses into a stacked
   rail on the right. Count-agnostic and CSS-only — the `.featured` card spans
   all rows of column 1 via grid-row 1/99, while the remaining auto-placed
   cards flow down column 2 (works for 2–4 tiers). align-items: stretch makes
   the hero match the rail's stacked height; we deliberately leave grid rows on
   `auto` (NO grid-auto-rows) so the 1/99 span does not mint 98 sized implicit
   tracks. Targets the stable `.featured` class, so it tracks whichever tier is
   highlighted, not a positional middle. Legible in light, dark and bg-media. */
.slide.k-pricing.lay-spotlight .price-grid {
  display: grid;
  grid-template-columns: 1.55fr 1fr;
  gap: 26px;
  margin-top: 18px;
  align-items: stretch;
}
/* hero: the featured tier takes the whole left column */
.slide.k-pricing.lay-spotlight .price-card.featured {
  grid-column: 1;
  grid-row: 1 / 99;
  padding: 52px 56px;
  justify-content: flex-start;
  background: color-mix(in srgb, var(--slide-accent) 10%, var(--slide-surface));
  border-color: var(--slide-accent);
  box-shadow: 0 0 0 2px var(--slide-accent), var(--shadow-lg);
}
.slide.has-bg.bg-media.k-pricing.lay-spotlight .price-card.featured {
  background: color-mix(in srgb, var(--slide-accent) 26%, transparent);
}
.slide.k-pricing.lay-spotlight .price-card.featured .price-name { font-size: 44px; }
.slide.k-pricing.lay-spotlight .price-card.featured .price-num {
  font-size: 132px; letter-spacing: -0.05em;
}
.slide.k-pricing.lay-spotlight .price-card.featured .price-per { font-size: 34px; }
.slide.k-pricing.lay-spotlight .price-card.featured .price-note {
  font-size: 27px; margin-bottom: 22px; max-width: 640px;
}
.slide.k-pricing.lay-spotlight .price-card.featured .price-feats {
  display: grid; grid-template-columns: 1fr 1fr; gap: 18px 40px; padding-top: 22px;
}
.slide.k-pricing.lay-spotlight .price-card.featured .price-feats li { font-size: 25px; }
.slide.k-pricing.lay-spotlight .price-card.featured .price-badge {
  top: -20px; font-size: 22px; padding: 8px 20px;
}
/* rail: every non-featured tier compresses into the right column, stacked */
.slide.k-pricing.lay-spotlight .price-card:not(.featured) {
  grid-column: 2;
  padding: 28px 32px;
  gap: 4px;
}
.slide.k-pricing.lay-spotlight .price-card:not(.featured) .price-name { font-size: 26px; }
.slide.k-pricing.lay-spotlight .price-card:not(.featured) .price-amount { margin: 2px 0; }
.slide.k-pricing.lay-spotlight .price-card:not(.featured) .price-num { font-size: 52px; }
.slide.k-pricing.lay-spotlight .price-card:not(.featured) .price-per { font-size: 22px; }
.slide.k-pricing.lay-spotlight .price-card:not(.featured) .price-note {
  font-size: 20px; margin-bottom: 8px;
}
.slide.k-pricing.lay-spotlight .price-card:not(.featured) .price-feats {
  padding-top: 12px; gap: 9px;
}
.slide.k-pricing.lay-spotlight .price-card:not(.featured) .price-feats li { font-size: 20px; }
.slide.k-pricing.lay-spotlight .price-card:not(.featured) .price-feats svg {
  width: 22px; height: 22px;
}

/* ========== timeline ========== */
/* --- timeline · vertical --- */
/* ── Timeline · VERTICAL ───────────────────────────────────────────────
   A single-column spine: nodes stack as rows, a vertical rail runs down the
   left, each dot sits ON the rail and date/title/note flow to its right.
   Genuinely distinct from the default horizontal rail (column flow + left
   spine + per-row dot, not a top bar with columns beneath). */
.slide.k-timeline.lay-vertical .timeline {
  display: flex; flex-direction: column;
  gap: 0; margin-top: 40px; padding-top: 0; padding-left: 26px;
  flex: 1; justify-content: center;
}
/* re-route the connecting line: top bar -> left spine */
.slide.k-timeline.lay-vertical .timeline-line {
  top: 24px; bottom: 24px; left: 12px; right: auto;
  width: 3px; height: auto;
}
/* each milestone becomes a row hung off the spine */
.slide.k-timeline.lay-vertical .tl-node {
  padding-top: 0; padding-left: 60px;
  padding-bottom: 40px; min-height: 0;
}
.slide.k-timeline.lay-vertical .tl-node:last-child { padding-bottom: 0; }
/* dot centred on the spine at the row's start (26px dot centre ≈ 13px ≈ spine) */
.slide.k-timeline.lay-vertical .tl-dot { top: 2px; left: 0; }
/* tighter row rhythm: date eyebrow -> title -> note */
.slide.k-timeline.lay-vertical .tl-title { margin-top: 4px; }
.slide.k-timeline.lay-vertical .tl-note { max-width: 1280px; }
/* --- timeline · milestones-band --- */
/* ── Timeline · MILESTONES-BAND ────────────────────────────────────────
   A bold horizontal band of discrete milestone CARDS instead of bare points
   on a line. The connecting rail is dropped; each node is a surface-backed
   tile with a large marker dot in its corner and an accent top edge that
   fills in for done/active states. Reads as a row of solid stops, not a
   thin rail. */
.slide.k-timeline.lay-milestones-band .timeline {
  /* keep the columnar grid but make the tracks equal cards with breathing room */
  grid-auto-flow: column; grid-auto-columns: 1fr;
  gap: 28px; margin-top: 48px; padding-top: 0;
  align-items: stretch; flex: 1;
}
/* the thin connecting line is meaningless against solid cards — hide it */
.slide.k-timeline.lay-milestones-band .timeline-line { display: none; }
/* each node -> a filled milestone tile */
.slide.k-timeline.lay-milestones-band .tl-node {
  padding: 40px 36px 36px;
  background: var(--slide-surface);
  border: 1px solid var(--slide-border);
  border-radius: var(--radius-xl);
  border-top: 4px solid var(--slide-border);
  display: flex; flex-direction: column; gap: 6px;
  position: relative;
}
/* ink band: translucent card surface (mirrors price-card/kpi-tile/cmp-col) */
.slide.s-ink.k-timeline.lay-milestones-band .tl-node {
  background: rgba(255,255,255,0.05); border-color: rgba(255,255,255,0.12);
  border-top-color: rgba(255,255,255,0.12);
}
/* accent the top edge for progressed states */
.slide.k-timeline.lay-milestones-band .tl-node.done,
.slide.k-timeline.lay-milestones-band .tl-node.active {
  border-top-color: var(--slide-accent);
}
.slide.k-timeline.lay-milestones-band .tl-node.active {
  box-shadow: 0 0 0 2px var(--slide-accent);
}
/* move the marker dot into the tile's top-right corner, larger */
.slide.k-timeline.lay-milestones-band .tl-dot {
  top: 28px; left: auto; right: 32px;
  width: 22px; height: 22px; border-width: 3px;
}
.slide.k-timeline.lay-milestones-band .tl-date { padding-right: 40px; }
.slide.k-timeline.lay-milestones-band .tl-title { margin-top: 6px; padding-right: 40px; }
.slide.k-timeline.lay-milestones-band .tl-note { margin-top: auto; padding-top: 12px; }

/* ========== compare ========== */
/* --- compare · stacked --- */
.slide.k-compare.lay-stacked .cmp-grid {
  /* one column, two stacked rows — bands instead of side-by-side cards */
  grid-template-columns: 1fr;
  grid-auto-rows: 1fr;
  gap: 28px;
  margin-top: 24px;
  flex: 1;
  min-height: 0;
}
/* the per-side build wrappers must fill their row so the bands are equal height */
.slide.k-compare.lay-stacked .cmp-grid > * { display: flex; }
.slide.k-compare.lay-stacked .cmp-col {
  /* a wide band: label rail | list, not head-over-list */
  display: grid;
  grid-template-columns: 380px 1fr;
  align-items: center;
  gap: 56px;
  width: 100%;
  padding: 40px 56px;
  border-radius: 22px;
}
.slide.k-compare.lay-stacked .cmp-head {
  /* the rail: vertical separator pins the heading to its own zone */
  margin: 0;
  padding-right: 44px;
  border-right: 2px solid var(--slide-border);
}
.slide.k-compare.lay-stacked .cmp-hl .cmp-head { border-right-color: var(--slide-accent); }
.slide.k-compare.lay-stacked .cmp-title { font-size: 48px; line-height: 1.05; }
.slide.k-compare.lay-stacked .cmp-list {
  /* checks flow across two columns to use the wide band */
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 18px 48px;
  align-content: center;
}
.slide.k-compare.lay-stacked .cmp-list li { font-size: 25px; }
/* --- compare · versus --- */
.slide.k-compare.lay-versus .cmp-grid {
  /* pull the two sides together so they collide at a central seam */
  grid-template-columns: 1fr 1fr;
  gap: 0;
  margin-top: 28px;
  position: relative;
  flex: 1;
  min-height: 0;
  align-items: stretch;
}
.slide.k-compare.lay-versus .cmp-grid > * { display: flex; }
.slide.k-compare.lay-versus .cmp-col {
  width: 100%;
  padding: 52px 64px;
}
/* square the meeting edges so the two panels read as one split surface */
.slide.k-compare.lay-versus .cmp-left {
  border-top-right-radius: 0;
  border-bottom-right-radius: 0;
  border-right: none;
}
.slide.k-compare.lay-versus .cmp-right {
  border-top-left-radius: 0;
  border-bottom-left-radius: 0;
}
/* the seam: a hairline divider down the centre */
.slide.k-compare.lay-versus .cmp-grid::after {
  content: "";
  position: absolute;
  top: 8%;
  bottom: 8%;
  left: 50%;
  width: 2px;
  transform: translateX(-1px);
  background: var(--slide-border);
  z-index: 1;
}
/* the VS medallion straddling the seam */
.slide.k-compare.lay-versus .cmp-grid::before {
  content: "VS";
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 2;
  width: 104px;
  height: 104px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: var(--slide-accent);
  color: var(--slide-on-accent);
  font-size: 34px;
  font-weight: 700;
  letter-spacing: 0.04em;
  box-shadow: 0 0 0 12px var(--slide-canvas);
}
/* on a media backdrop there is no solid canvas to ring against — use a dark halo */
.slide.k-compare.lay-versus.has-bg.bg-media .cmp-grid::before {
  box-shadow: 0 0 0 12px var(--slide-scrim-1);
}
/* give each list breathing room either side of the central medallion */
.slide.k-compare.lay-versus .cmp-left .cmp-list { padding-right: 40px; }
.slide.k-compare.lay-versus .cmp-right .cmp-list { padding-left: 40px; }
.slide.k-compare.lay-versus .cmp-title { font-size: 46px; }

/* ========== features ========== */
/* --- features · two-col --- */
/* ── features · two-col ─────────────────────────────────────────────────
   The trio default is three upright cards in a single row. two-col reflows
   the same cards into a 2-up grid of WIDE horizontal rows: the icon becomes a
   fixed left rail and the heading + description stack to its right. The grid
   grows to fill the slide so the cards read as balanced bands. Tokens are
   inherited so it stays legible in light/dark and under .has-bg.bg-media. */
.slide.k-features.lay-two-col .three-col {
  grid-template-columns: 1fr 1fr;
  grid-auto-rows: 1fr;
  gap: 32px 40px;
  flex: 1;                       /* fill the space below the title block */
  margin-top: 28px;
  align-content: stretch;
}
.slide.k-features.lay-two-col .feature-card {
  display: grid;
  grid-template-columns: 96px 1fr;
  align-items: start;
  column-gap: 32px;
  row-gap: 10px;
  padding: 40px 44px;
}
.slide.k-features.lay-two-col .feature-icon {
  grid-row: 1 / span 2;          /* icon spans the full card height as a rail */
  width: 96px;
  height: 96px;
  border-radius: 22px;
  margin-bottom: 0;
  align-self: center;
}
.slide.k-features.lay-two-col .feature-icon svg { width: 46px; height: 46px; }
/* the un-classed heading div + the .desc sit in the right column */
.slide.k-features.lay-two-col .feature-card > [data-region$="-h"] {
  align-self: end;
  font-size: 38px;
  font-weight: 500;
  letter-spacing: -0.018em;
  color: var(--slide-text);
}
.slide.k-features.lay-two-col .feature-card .desc { align-self: start; font-size: 25px; }
/* --- features · with-lede-panel [needsMarkup] --- */
/* ── features · with-lede-panel ─────────────────────────────────────────
   An asymmetric split: a tall LEFT intro panel (eyebrow/title/lede on a soft
   banded surface) and a RIGHT column of feature cards stacked vertically.
   Two paths: the ideal one targets an added `.feat-split` wrapper (see
   markupNote); a fallback re-lays the bare default markup via the slide grid
   so it still looks like a split even before the markup lands, and switches
   itself off via :has() once the wrapper exists. */

/* Ideal: explicit two-pane wrapper */
.slide.k-features.lay-with-lede-panel .feat-split {
  display: grid;
  grid-template-columns: minmax(560px, 38%) 1fr;
  gap: 64px;
  flex: 1;
  align-items: stretch;
  min-height: 0;
}
.slide.k-features.lay-with-lede-panel .feat-split > .title-block {
  margin: 0;
  align-self: stretch;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 24px;
  padding: 56px 52px;
  border-radius: 24px;
  background: color-mix(in srgb, var(--slide-accent) 7%, var(--slide-surface));
  border: 1px solid var(--slide-border);
}
.slide.k-features.lay-with-lede-panel .feat-split > .title-block .title { font-size: 64px; }
.slide.k-features.lay-with-lede-panel .feat-split .lede { margin-top: 4px; font-size: 27px; }
.slide.k-features.lay-with-lede-panel .feat-split .three-col {
  grid-template-columns: 1fr;
  grid-auto-rows: 1fr;
  gap: 22px;
  margin-top: 0;
  min-height: 0;
}
/* right-hand cards read as compact horizontal rows */
.slide.k-features.lay-with-lede-panel .feat-split .feature-card {
  display: grid;
  grid-template-columns: 72px 1fr;
  align-items: center;
  column-gap: 28px;
  row-gap: 6px;
  padding: 28px 36px;
}
.slide.k-features.lay-with-lede-panel .feat-split .feature-icon {
  grid-row: 1 / span 2; width: 72px; height: 72px; margin-bottom: 0; align-self: center;
}
.slide.k-features.lay-with-lede-panel .feat-split .feature-card > [data-region$="-h"] {
  align-self: end; font-size: 30px; font-weight: 500; letter-spacing: -0.014em; color: var(--slide-text);
}
.slide.k-features.lay-with-lede-panel .feat-split .feature-card .desc { align-self: start; font-size: 23px; }

/* Fallback: no wrapper yet — split the slide itself into panel + cards.
   Guarded by :not(:has(.feat-split)) so it disables once the markup lands. */
.slide.k-features.lay-with-lede-panel:not(:has(.feat-split)) {
  display: grid;
  grid-template-columns: minmax(560px, 38%) 1fr;
  column-gap: 64px;
  align-items: stretch;
}
.slide.k-features.lay-with-lede-panel:not(:has(.feat-split)) > .title-block {
  margin: 0; align-self: stretch; display: flex; flex-direction: column; justify-content: center; gap: 24px;
  padding: 56px 52px; border-radius: 24px;
  background: color-mix(in srgb, var(--slide-accent) 7%, var(--slide-surface));
  border: 1px solid var(--slide-border);
}
.slide.k-features.lay-with-lede-panel:not(:has(.feat-split)) > .title-block .title { font-size: 64px; }
.slide.k-features.lay-with-lede-panel:not(:has(.feat-split)) > .three-col {
  grid-template-columns: 1fr; grid-auto-rows: 1fr; gap: 22px; margin-top: 0; align-self: stretch;
}
.slide.k-features.lay-with-lede-panel:not(:has(.feat-split)) > .three-col .feature-card {
  display: grid; grid-template-columns: 72px 1fr; align-items: center; column-gap: 28px; row-gap: 6px; padding: 28px 36px;
}
.slide.k-features.lay-with-lede-panel:not(:has(.feat-split)) > .three-col .feature-icon {
  grid-row: 1 / span 2; width: 72px; height: 72px; margin-bottom: 0; align-self: center;
}
.slide.k-features.lay-with-lede-panel:not(:has(.feat-split)) > .three-col .feature-card > [data-region$="-h"] {
  align-self: end; font-size: 30px; font-weight: 500; letter-spacing: -0.014em; color: var(--slide-text);
}
.slide.k-features.lay-with-lede-panel:not(:has(.feat-split)) > .three-col .feature-card .desc { align-self: start; font-size: 23px; }
/* --- features · numbered --- */
/* ── features · numbered ────────────────────────────────────────────────
   No cards: the same features become a tall single-column list of rows split
   by hairline rules, each led by an OVERSIZED index number set by a CSS
   counter in the left gutter. The icon chip is hidden so the numeral is the
   visual anchor. A two-track grid (number | text) keeps headings + desc tidy.
   Numerals use --slide-text-3 so they read on light, dark, and bg-media. */
.slide.k-features.lay-numbered .three-col {
  display: flex;
  flex-direction: column;
  gap: 0;
  counter-reset: feat;
  flex: 1;
  margin-top: 28px;
}
.slide.k-features.lay-numbered .feature-card {
  counter-increment: feat;
  display: grid;
  grid-template-columns: 150px 1fr;
  align-items: baseline;
  column-gap: 44px;
  row-gap: 8px;
  background: none;
  border: 0;
  border-radius: 0;
  border-top: 1px solid var(--slide-border);
  padding: 34px 8px;
  flex: 1;
}
.slide.k-features.lay-numbered .feature-card:last-child { border-bottom: 1px solid var(--slide-border); }
/* the big gutter numeral */
.slide.k-features.lay-numbered .feature-card::before {
  content: counter(feat, decimal-leading-zero);
  grid-row: 1 / span 2;
  align-self: center;
  font-size: 92px;
  font-weight: 400;
  line-height: 0.9;
  letter-spacing: -0.04em;
  color: var(--slide-text-3);
  font-variant-numeric: tabular-nums;
}
/* hide the icon chip — the numeral replaces it */
.slide.k-features.lay-numbered .feature-icon { display: none; }
.slide.k-features.lay-numbered .feature-card > [data-region$="-h"] {
  align-self: end;
  font-size: 40px;
  font-weight: 500;
  letter-spacing: -0.018em;
  color: var(--slide-text);
}
.slide.k-features.lay-numbered .feature-card .desc { align-self: start; font-size: 26px; max-width: 1180px; }

/* ========== bullets ========== */
/* --- bullets · two-col --- */
/* ── bullets · two-col — masonry of bordered point-tiles via CSS columns ─
   The default 'list' is one tall single-column flex stack. Here the same
   <li>s reflow into two newspaper-style columns; each becomes a bordered
   tile with a left accent rule, so the eye reads a grid of cards rather
   than a vertical list. Tokens only, so it adapts to light/dark/bg-media. */
.slide.k-bullets.lay-two-col ul.bullets {
  display: block;            /* leave flex behind so `columns` can govern */
  columns: 2;
  column-gap: 56px;
  margin-top: 24px;
}
.slide.k-bullets.lay-two-col ul.bullets li {
  break-inside: avoid;
  -webkit-column-break-inside: avoid;
  display: block;
  margin: 0 0 28px 0;
  padding: 30px 34px;
  background: var(--slide-surface);
  border: 1px solid var(--slide-border);
  border-left: 4px solid var(--slide-accent);
  border-radius: 18px;
  font-size: var(--type-small);
  line-height: 1.4;
}
/* retire the round dot — the accent rule on the tile carries the marker now */
.slide.k-bullets.lay-two-col ul.bullets li::before { content: none; }

/* --- bullets · with-image [needsMarkup] --- */
/* ── bullets · with-image — image rail left, bullets right ──────────────
   The default 'list' fills the full content width top-to-bottom. Here the
   slide becomes a horizontal split: a tall media rail (~42%) on the left,
   the title-block + bullets stacked in the remaining column. Needs the
   extra .bul-media / .bul-text wrappers (see markupNote). Falls back to a
   plain centred column if the media element isn't emitted. */
.slide.k-bullets.lay-with-image {
  flex-direction: row;
  align-items: stretch;
  gap: 72px;
  padding-top: var(--pad-top);
  padding-bottom: var(--pad-bottom);
}
.slide.k-bullets.lay-with-image .bul-media {
  flex: 0 0 42%;
  align-self: stretch;
  border-radius: 22px;
  overflow: hidden;
  position: relative;
  min-height: 0;
}
.slide.k-bullets.lay-with-image .bul-media .art {
  width: 100%; height: 100%; border-radius: 22px;
}
.slide.k-bullets.lay-with-image .bul-text {
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  justify-content: center;
  min-width: 0;
}
.slide.k-bullets.lay-with-image .bul-text .title-block { margin-bottom: 36px; }
.slide.k-bullets.lay-with-image ul.bullets { gap: 24px; }

/* --- bullets · numbered --- */
/* ── bullets · numbered — counter-driven index chips, ordered sequence ──
   The default 'list' uses a small round accent dot. Here each <li> is a
   two-column grid: a big numbered chip (CSS counter) + the text, with a
   divider rule between rows — reads as a ranked, ordered walkthrough.
   Counter + ::before only; no markup change needed. */
.slide.k-bullets.lay-numbered ul.bullets {
  counter-reset: bul;
  gap: 0;
  margin-top: 16px;
}
.slide.k-bullets.lay-numbered ul.bullets li {
  counter-increment: bul;
  display: grid;
  grid-template-columns: 104px 1fr;
  align-items: center;
  gap: 36px;
  padding: 26px 0;
  border-top: 1px solid var(--slide-border);
  line-height: 1.35;
}
.slide.k-bullets.lay-numbered ul.bullets li:last-child {
  border-bottom: 1px solid var(--slide-border);
}
/* repurpose the dot ::before into a numbered chip */
.slide.k-bullets.lay-numbered ul.bullets li::before {
  content: counter(bul, decimal-leading-zero);
  position: static;
  width: 84px; height: 84px;
  top: auto; left: auto;
  border-radius: 20px;
  background: color-mix(in srgb, var(--slide-accent) 14%, transparent);
  color: var(--slide-accent);
  display: grid;
  place-items: center;
  font-size: 40px;
  font-weight: 500;
  letter-spacing: 0.02em;
  font-variant-numeric: tabular-nums;
}

/* --- bullets · colour-band --- */
/* ── bullets · colour-band — full-width stacked accent bands ────────────
   The default 'list' is airy dotted lines. Here every <li> becomes a
   full-bleed horizontal band (a numbered marker tab + text), banded with
   alternating accent tints so the slide reads as solid colour stripes
   spanning edge-to-edge. The bands are pulled out to the slide edges with
   negative margins against --pad-x. Tokens only; the tints adapt per
   theme and degrade legibly under .bg-media (translucent surfaces). */
.slide.k-bullets.lay-colour-band ul.bullets {
  counter-reset: band;
  gap: 0;
  margin: 24px calc(-1 * var(--pad-x)) 0;
}
.slide.k-bullets.lay-colour-band ul.bullets li {
  counter-increment: band;
  display: flex;
  align-items: center;
  gap: 32px;
  padding: 30px var(--pad-x);
  font-size: var(--type-body);
  line-height: 1.3;
  background: color-mix(in srgb, var(--slide-accent) 8%, transparent);
}
.slide.k-bullets.lay-colour-band ul.bullets li:nth-child(even) {
  background: color-mix(in srgb, var(--slide-accent) 16%, transparent);
}
/* repurpose the round dot into a leading numbered marker tab */
.slide.k-bullets.lay-colour-band ul.bullets li::before {
  content: counter(band, decimal-leading-zero);
  position: static;
  top: auto; left: auto;
  width: auto; height: auto;
  border-radius: 0;
  background: none;
  color: var(--slide-accent);
  font-size: 32px;
  font-weight: 500;
  letter-spacing: 0.04em;
  font-variant-numeric: tabular-nums;
  flex: none;
  min-width: 64px;
}


/* ========== cta ========== */
/* --- cta · ink-band --- */
/* CTA · ink-band — the whole call-to-action lifts onto one full-bleed rounded
   ink panel: headline pinned top-left, contacts re-flowed as a horizontal foot
   band of stacked KEY-over-value columns. Geometrically opposite the centred
   default's left-aligned vertical stack. */
.slide.k-cta.lay-ink-band { padding: 56px; }
.slide.k-cta.lay-ink-band .cta {
  /* width:100% is REQUIRED — .slide.center sets align-items:flex-start, so a
     .cta with no explicit width would shrink-wrap and the panel would collapse */
  width: 100%;
  height: 100%;
  background: var(--slide-ink);
  color: var(--slide-on-ink);
  border-radius: 28px;
  padding: 104px 112px 88px;
  justify-content: flex-start;
  gap: 34px;
}
.slide.k-cta.lay-ink-band .cta-headline { color: var(--slide-on-ink); max-width: 1400px; }
.slide.k-cta.lay-ink-band .eyebrow.accent { color: var(--slide-accent); }
.slide.k-cta.lay-ink-band .cta-sub {
  color: color-mix(in srgb, var(--slide-on-ink) 82%, transparent);
  max-width: 1200px;
}
/* contacts become a horizontal band pinned to the foot of the panel:
   grid-auto-flow:column with two fixed rows lays each pair as a stacked
   KEY-over-value column flowing left-to-right */
.slide.k-cta.lay-ink-band .cta-contact {
  margin-top: auto;
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: max-content;
  grid-template-rows: auto auto;
  justify-content: start;
  gap: 10px 84px;
  padding-top: 40px;
  border-top: 1px solid color-mix(in srgb, var(--slide-on-ink) 18%, transparent);
}
.slide.k-cta.lay-ink-band .cta-contact .key {
  color: color-mix(in srgb, var(--slide-on-ink) 55%, transparent);
  text-transform: uppercase;
  letter-spacing: 0.12em;
  font-size: 20px;
}
.slide.k-cta.lay-ink-band .cta-contact .val { color: var(--slide-on-ink); font-size: 30px; }
/* --- cta · with-image [needsMarkup] --- */
/* CTA · with-image — text column left, full-height media panel right.
   The slide becomes a 2-col grid (overriding the base flex + .center). */
.slide.k-cta.lay-with-image {
  display: grid;
  grid-template-columns: 1.05fr 0.95fr;
  align-items: stretch;
  padding: 0;
}
.slide.k-cta.lay-with-image .cta {
  height: 100%;
  justify-content: center;
  gap: 30px;
  padding: var(--pad-top) 96px var(--pad-bottom) var(--pad-x);
}
.slide.k-cta.lay-with-image .cta-sub { max-width: 760px; }
/* the added media sibling fills the right column edge-to-edge */
.slide.k-cta.lay-with-image .cta-media {
  position: relative;
  height: 100%;
  overflow: hidden;
  background: var(--slide-surface);
}
.slide.k-cta.lay-with-image .cta-media .art { height: 100%; border-radius: 0; }
/* decorative fallback wash so the panel still reads well with no chosen image */
.slide.k-cta.lay-with-image .cta-media:empty {
  background: linear-gradient(150deg,
    color-mix(in srgb, var(--slide-accent) 28%, var(--slide-surface)),
    var(--slide-ink));
}
/* graceful pre-markup path: if a full-bleed background art layer was emitted,
   clip it to the right column and soften the seam back into the canvas */
.slide.k-cta.lay-with-image .bg-art { left: auto; right: 0; width: 47%; }
.slide.k-cta.lay-with-image .bg-scrim {
  left: 47%;
  background: linear-gradient(90deg, var(--slide-canvas) 0%, transparent 34%);
}
/* --- cta · split-contact --- */
/* CTA · split-contact — wide left column stacks eyebrow/headline/sub while the
   contact rows occupy a full-height bordered block on the right. A side-by-side
   split, distinct from the default's single stacked column. */
.slide.k-cta.lay-split-contact .cta {
  /* width:100% is REQUIRED — .slide.center sets align-items:flex-start, so the
     grid would otherwise shrink-wrap and the fr columns would not span */
  width: 100%;
  height: 100%;
  display: grid;
  grid-template-columns: 1.35fr 0.95fr;
  grid-template-rows: auto auto auto;
  grid-template-areas:
    "eb ct"
    "ha ct"
    "su ct";
  align-content: center;
  column-gap: 88px;
  row-gap: 24px;
}
.slide.k-cta.lay-split-contact .eyebrow.accent { grid-area: eb; }
.slide.k-cta.lay-split-contact .cta-headline { grid-area: ha; }
.slide.k-cta.lay-split-contact .cta-sub { grid-area: su; max-width: 100%; }
/* contacts form a self-contained block on the right, vertically centred,
   divided from the headline by a hairline; tokens adapt to light/dark */
.slide.k-cta.lay-split-contact .cta-contact {
  grid-area: ct;
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-top: 0;
  align-self: center;
  padding: 8px 0 8px 64px;
  border-left: 1px solid var(--slide-border);
}
.slide.k-cta.lay-split-contact .cta-contact .key {
  color: var(--slide-text-3);
  text-transform: uppercase;
  letter-spacing: 0.12em;
  font-size: 21px;
}
.slide.k-cta.lay-split-contact .cta-contact .key:not(:first-child) { margin-top: 28px; }
.slide.k-cta.lay-split-contact .cta-contact .val { color: var(--slide-text); font-size: 34px; line-height: 1.2; }
/* over a background image cta uses the legacy .has-bg path (text vars do NOT
   flip); this layout's higher-specificity rules would otherwise defeat the base
   white overrides and leave keys/border illegible on the scrim — restate them */
.slide.k-cta.lay-split-contact.has-bg .cta-contact { border-left-color: color-mix(in srgb, var(--slide-on-ink) 28%, transparent); }
.slide.k-cta.lay-split-contact.has-bg .cta-contact .key { color: color-mix(in srgb, var(--slide-on-ink) 62%, transparent); }
.slide.k-cta.lay-split-contact.has-bg .cta-contact .val { color: var(--slide-on-ink); }

/* ── Rich-text region styles (regionStyles channel) ───────────────────────
   Heading-size + alignment are NOT stored as tags — the renderer applies them
   as classes on the editable [data-region] element via reg()/er(). They layer
   on top of whatever size the region already has, so they read as a relative
   "make this bigger/smaller" step using the projection type scale, and they
   never affect non-styled regions. */
.slide [data-region].rt-h1 { font-size: var(--type-title); line-height: 1.04; }
.slide [data-region].rt-h2 { font-size: var(--type-cardtitle); line-height: 1.08; }
.slide [data-region].rt-h3 { font-size: var(--type-h3); line-height: 1.2; }
.slide [data-region].rt-align-left { text-align: left; }
.slide [data-region].rt-align-center { text-align: center; }
.slide [data-region].rt-align-right { text-align: right; }

