/* Shared slide-content styling for the course player engine.
   ---------------------------------------------------------------------------
   Added 2026-09-14, replacing an earlier state where this entire vocabulary
   lived only in course-player-presenter.css and was invisible to course mode
   -- found via two real bugs (Slide 3's embedded interaction, and the opening
   slide's full-bleed background) that were both "unstyled outside presenter
   mode," not real responsiveness problems. This file is the fix: content
   AUTHORING classes (what a slide/module's own HTML uses) live here, once,
   loaded by every host page (player.php, the presenter console and the
   Audience View) alongside whichever CHROME file (course-player-
   engine.css or course-player-presenter.css) that page also loads. Chrome --
   sidebar/module list/nav buttons, or notes pane/timer/outline -- stays
   wherever it already was; this file never styles anything view-specific.

   Token system: deliberately uses the platform's own --color-* names
   (--color-primary, --color-text-dark, --color-neutral-mid, --color-border),
   NOT course-player-presenter.css's --sl-* tokens. Two reasons: (1)
   --color-primary is the exact token renderTenantBrandingCss() populates on
   a real tenant page, already run through that engine's WCAG-AA contrast
   loop -- using this name means a tenant's own brand color applies to slide
   content automatically, with zero changes needed here, the moment this is
   served through a real tenant page; (2) course-player-engine.css uses the
   same names. Each host page supplies the values: style.css and the
   tenant's branding CSS in player.php, course-player-presenter.css's own
   :root on the presenter pages.

   In player.php the sl-rich-content rules apply only to a course marked
   presentable (player.php adds the class), so ordinary courses keep their
   own text styling.

   Every rule that uses a token here works in light AND dark, in every host
   page, because --color-border/--color-text-dark/--color-neutral-mid/
   --color-bg-light already carry dark-mode-safe values wherever they're
   defined (style.css for the real app; course-player-presenter.css's own
   :root/dark blocks for presenter/audience -- see the entries added there
   2026-09-14). --color-primary is the one exception: it's deliberately never
   redefined at the token level for dark mode (same reasoning as style.css's
   own comment on this -- it's dual-purpose as a background color too, e.g.
   .sl-open-demo-link below, and lightening it globally regressed that
   contrast when tried in the main app). Anywhere --color-primary is used as
   plain TEXT on a dark surface (.sl-slide-statement, .sl-quote-list strong,
   links) gets a scoped dark-mode override instead, at the bottom of this
   file, reading --color-primary-on-dark: the tenant's brand lightened by
   renderTenantBrandingCss() until it passes 4.5:1 on the dark surfaces.

   No hard-coded colors (CLAUDE.md, 2026-10-02): every rule here reads a
   token, so a tenant's brand and dark mode reach every template. Values
   live only in token definitions -- style.css on a real tenant page,
   course-player-presenter.css's :root for the presenter/audience pages. */

.sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

/* ---- Rich text inside any slide/module body (class "sl-rich-content" on
   the mount point) ---- */

.sl-rich-content h2,
.sl-rich-content h3,
.sl-rich-content h4 {
    margin: 1.5rem 0 0.5rem;
    color: var(--color-text-dark);
}

.sl-rich-content h2:first-child,
.sl-rich-content h3:first-child,
.sl-rich-content h4:first-child {
    margin-top: 0;
}

.sl-rich-content p {
    margin: 0 0 1rem;
}

.sl-rich-content ul,
.sl-rich-content ol {
    margin: 0 0 1rem;
    padding-left: 1.5rem;
}

.sl-rich-content li {
    margin-bottom: 0.5rem;
}

.sl-rich-content img {
    max-width: 100%;
    height: auto;
    margin: 1rem 0;
    border-radius: 0.5rem;
}

.sl-rich-content blockquote {
    margin: 0 0 1rem;
    padding: 0.5rem 0 0.5rem 1rem;
    border-left: 4px solid var(--color-primary);
    color: var(--color-neutral-mid);
    font-style: italic;
}

.sl-rich-content figure {
    margin: 1rem 0;
}

.sl-rich-content figcaption {
    font-size: 0.85rem;
    color: var(--color-neutral-mid);
}

/* "One idea per slide, massive minimal text" -- a bigger, sparer treatment
   for slides whose on-screen text is a single short statement, not a list.
   Fluid via clamp() rather than a fixed size on purpose -- this is the part
   of the design that answers "does a slide have to look like a shrunk
   PowerPoint slide in a narrow course layout": it doesn't, because nothing
   here is pinned to a canvas size to begin with. */
.sl-slide-statement {
    font-size: clamp(2.5rem, 6vw, 5rem);
    line-height: 1.15;
    color: var(--color-primary);
    font-weight: 700;
    text-align: center;
    margin: 0 0 1rem;
}

.sl-slide-subtitle {
    font-size: clamp(1.1rem, 2.5vw, 1.5rem);
    color: var(--color-neutral-mid);
    text-align: center;
    margin: 0;
}

.sl-slide-cta {
    text-align: center;
    margin: 1.5rem 0 0;
}

.sl-slide-kicker {
    font-size: 0.9rem;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--color-neutral-mid);
    margin: 0 0 0.5rem;
}

/* An embedded interaction (e.g. an iframe'd demo). Fills its container at
   whatever width that container happens to be -- course mode's narrower
   content column, or audience mode's wider slide area -- rather than
   assuming either. */
.sl-demo-frame {
    display: block;
    width: 100%;
    height: 60vh;
    max-width: 100%;
    border: 1px solid var(--color-border);
    border-radius: 0.5rem;
    margin: 1rem 0;
}


/* Rendered as a link, not a button, because it navigates -- but sized to the
   same 44px target as every other control across every view mode. White
   text is always safe here regardless of which page/tenant supplies
   --color-primary: renderTenantBrandingCss() (or this file's own light-mode
   defaults) guarantees that color already clears >=4.5:1 against white --
   the same guarantee .module-btn[aria-current="true"] in
   course-player-engine.css already relies on for the identical pairing. */
.sl-open-demo-link {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-height: 44px;
    padding: 0.75rem 1.25rem;
    margin: 0 auto;
    border-radius: 0.375rem;
    background: var(--color-primary);
    color: var(--color-on-primary);
    text-decoration: none;
    font-weight: 600;
}

.sl-open-demo-link:hover {
    opacity: 0.9;
}

/* ---- Slide layout templates (draft) ----
   Ported 2026-10-02 from presenter-prototype/style.css as it stood for the
   ATD Core 4 talk (2026-09-30), then generalized into named templates the
   same day. The catalog -- what each template is for, its slots, word
   limits, and the markup to emit -- is presenter-integration/templates/
   slide-layouts.json; that file is the source of truth for authors and the
   MCP, and templates/README.md is the human guide. Status: draft, pending a
   sighted polish pass before 1.0.

   Every templated slide wraps its content in
       <div class="sl-slide" data-sl-layout="<template id>">
   and starts with exactly one <h1 class="sl-slide-title"> (or a .sr-only
   <h1> for full-bleed-image). The layout lives in the slide's own markup,
   not the manifest, so one fragment lays out the same way in course,
   presenter and audience views.

   Sizes here are the course-mode/default ones (fluid, reflowing). Projector
   sizing -- pt-based type inside a capped 16:9 safe area -- is audience-view
   chrome in course-player-presenter.css, scoped to .sl-audience-shell.

   Illustrations beside a statement are decorative (alt="") -- the statement
   carries the meaning. An image that carries meaning gets real alt text. */

.sl-slide-title {
    color: var(--color-text-dark);
    line-height: 1.15;
    margin: 0 0 0.5em;
}

.sl-slide[data-sl-layout="closing"] {
    text-align: center;
}

.sl-slide[data-sl-layout="closing"] .sl-slide-title {
    text-align: center;
}

/* Statement on one side, decorative illustration on the other. */
.sl-slide-split {
    display: grid;
    grid-template-columns: minmax(0, 1.25fr) minmax(0, 1fr);
    gap: 4vw;
    align-items: center;
}

.sl-slide-split .sl-slide-statement {
    text-align: left;
    margin: 0;
}

/* .sl-rich-content-qualified to beat .sl-rich-content img's margin/radius. */
.sl-rich-content .sl-slide-illustration {
    display: block;
    width: 100%;
    max-height: 50vh;
    margin: 0;
    border-radius: 0;
}

.sl-slide-split--solo {
    grid-template-columns: minmax(0, 1fr);
    justify-items: center;
}

.sl-rich-content .sl-slide-split--solo .sl-slide-illustration {
    width: auto;
    max-width: 100%;
    height: 50vh;
}

/* A statement above an embedded interaction is a short lead-in, not a
   headline: left-aligned, modest size, leaving the room to the embed. */
.sl-rich-content:has(.sl-demo-frame) .sl-slide-statement {
    font-size: clamp(1.25rem, 2.6vw, 1.75rem);
    text-align: left;
    margin: 0 0 0.75rem;
}

/* ---- Quote list: numbered label + quotation, a list style for the
   content and two-column templates (Army of One's stakeholder scripts) ---- */

.sl-rich-content .sl-quote-list {
    line-height: 1.35;
    margin: 0;
    padding-left: 2.25em;
}

.sl-quote-list > li {
    margin-bottom: 1em;
}

.sl-quote-list strong {
    color: var(--color-primary);
}

.sl-rich-content .sl-quote-list blockquote {
    margin: 0.25em 0 0;
    padding: 0.25em 0 0.25em 1em;
    color: var(--color-text-dark);
    font-style: normal;
}

/* ---- Title and content / two columns ---- */

.sl-slide[data-sl-layout="content"] ul,
.sl-slide[data-sl-layout="content"] ol {
    margin: 0;
}

/* Lists on a slide step in from the title (and column headings): the title is
   the furthest-left edge, the bullets sit visibly inside it. With a 1.5rem
   indent the bullet markers lined up with the title's first letter, which
   read as misaligned to sighted reviewers (2026-11-05). In em, so the step
   grows with the 28pt projector text instead of staying 24px. */
.sl-rich-content .sl-slide ul,
.sl-rich-content .sl-slide ol {
    padding-left: 2.25em;
}

.sl-slide-columns {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 4vw;
    align-items: start;
}

.sl-rich-content .sl-slide-column > h2 {
    margin: 0 0 0.5em;
    color: var(--color-text-dark);
}

.sl-slide-column > :last-child {
    margin-bottom: 0;
}

.sl-rich-content .sl-slide-column img {
    display: block;
    max-width: 100%;
    max-height: 40vh;
    margin: 0;
}

/* ---- Image and text (image-text template) ----
   A picture beside a few points at body size, in either order. Order in the
   markup is the order on screen, so reading order and visual order always
   match; the --text-first modifier only moves the wider column to the text.
   Added 2026-11-05 from a sighted review: the most-missed PowerPoint layout. */
.sl-slide-media {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.2fr);
    gap: 4vw;
    align-items: center;
}

.sl-slide-media--text-first {
    grid-template-columns: minmax(0, 1.2fr) minmax(0, 1fr);
}

.sl-rich-content .sl-slide-media-image {
    display: block;
    width: 100%;
    height: auto;
    max-height: 50vh;
    object-fit: contain;
    margin: 0;
    border-radius: 0;
}

.sl-slide-media-text > :last-child {
    margin-bottom: 0;
}

/* ---- Closing slides: Thank You, Let's Connect, Questions ---- */

.sl-thank-you-slide {
    display: flex;
    justify-content: center;
}

.sl-rich-content .sl-thank-you-logo {
    width: auto;
    max-width: 100%;
    height: min(50vh, 80vw);
    margin: 0;
}

.sl-connect-slide {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 2rem;
}

.sl-connect-info {
    text-align: center;
}

.sl-rich-content .sl-contact-line {
    font-size: clamp(1.1rem, 2.5vw, 1.5rem);
    margin: 0 0 0.25em;
    color: var(--color-text-dark);
}

/* The two images form the base of a triangle under the centered text:
   logo lower-left, QR lower-right, tops aligned. */
.sl-connect-images {
    display: flex;
    justify-content: center;
    align-items: flex-start;
    flex-wrap: wrap;
    gap: 2rem 12vw;
}

.sl-rich-content .sl-connect-logo,
.sl-rich-content .sl-contact-qr {
    width: min(220px, 40vw);
    height: auto;
    margin: 0;
    border-radius: 0.375rem;
}

/* ---- Reflow ----
   Side-by-side layouts stack on narrow screens (phones, a narrow course
   column, 400% zoom): statement before its illustration, left column
   before right. */
@media (max-width: 700px) {
    .sl-slide-split,
    .sl-slide-columns,
    .sl-slide-media {
        grid-template-columns: minmax(0, 1fr);
        gap: 1.5rem;
    }

    .sl-rich-content .sl-slide-split--solo .sl-slide-illustration {
        height: auto;
        max-height: 50vh;
    }
}

/* ---- Full-bleed display variant (manifest entry `fullBleed: true`) ---- */
/* Layout-level full-bleed handling (hiding audience chrome, switching
   .sl-slide-main to absolute positioning) stays view-specific in
   course-player-presenter.css -- course mode has no equivalent chrome to
   hide. Only the branding content itself -- what actually goes inside a
   full-bleed slide -- lives here. */

/* .sl-fullbleed is the generic container (full-bleed-image template);
   .sl-fullbleed-branding is the ATD Core 4 variant, whose blue shows only
   while the image loads. Neither is a theme color: a full-bleed image looks
   the same in light and dark. */
/* No background color: the image covers the whole box, and a fixed color
   here (it used to be ATD's blue) would be a hard-coded color showing
   through while the image loads. */
/* Outside the audience view (a course page, the presenter's notes) there is
   no screen-sized box to fill, so a full-bleed slide shows as a 16:9 banner.
   Without this the 100% height resolved to 0 and only an overlaid logo
   showed. The audience view's full-bleed mode switches back to filling the
   window (course-player-presenter.css). */
.sl-fullbleed,
.sl-fullbleed-branding {
    position: relative;
    width: 100%;
    aspect-ratio: 16 / 9;
    overflow: hidden;
}

/* .sl-rich-content-qualified selectors needed here: .sl-rich-content img
   (specificity 0,1,1) would otherwise beat a bare class (0,1,0) and reapply
   its margin/border-radius, which breaks these images' absolute positioning
   (margin still offsets an absolutely positioned box). Qualified by
   .sl-rich-content itself -- the class every mount point (course content
   area, presenter notes, audience slide body) already carries -- rather than
   a view-specific container id/class, so this wins the specificity fight
   the same way in every view mode. */
.sl-rich-content .sl-fullbleed-bg {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    margin: 0;
    border-radius: 0;
}

.sl-rich-content .sl-fullbleed-logo {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    width: 31.83%;
    height: auto;
    margin: 0;
    border-radius: 0;
}


/* ---- Links ---- */
/* Brand-colored, not the browser's default blue, so a tenant's brand reaches
   them too. */
.sl-rich-content a {
    color: var(--color-primary);
}

/* ---- Dark-mode overrides for --color-primary used as plain text ---- */
/* --color-primary itself is never redefined at the token level for dark mode
   (see file header); these selectors read --color-primary-on-dark instead,
   which renderTenantBrandingCss() derives from the tenant's own brand.
   Mirrors both dark-mode entry points (an explicit data-theme toggle, and
   bare OS preference) -- keep the two in sync. */
[data-theme="dark"] .sl-slide-statement,
[data-theme="dark"] .sl-quote-list strong,
[data-theme="dark"] .sl-rich-content a {
    color: var(--color-primary-on-dark);
}

@media (prefers-color-scheme: dark) {
    html:not([data-theme="light"]) .sl-slide-statement,
    html:not([data-theme="light"]) .sl-quote-list strong,
    html:not([data-theme="light"]) .sl-rich-content a {
        color: var(--color-primary-on-dark);
    }
}

/* Talking points below a slide in course view, for a deck that turns them on
   (properties.showTalkingPoints). The engine adds the section; its h2 follows the
   slide's own h1. */
.sl-talking-points {
    margin-top: 2rem;
    padding-top: 1rem;
    border-top: 1px solid var(--color-border);
}
